일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- TensorFlow Lite
- tinyml
- 삼성역량테스트
- dfs
- 포스코 AI교육
- 삼성코테
- 포스코 ai 교육
- MCU 딥러닝
- 코테 문제
- 딥러닝
- 포스코 교육
- 삼성역테
- tflite
- 알고리즘
- sort
- BFS
- 코테
- 그리디
- bfs문제
- DP문제
- 자료구조
- dfs문제
- 컴퓨팅사고
- DP
- 삼성코딩테스트
- 다이나믹프로그래밍
- 코딩테스트
- 임베디드 딥러닝
- 초소형머신러닝
- 영상처리
- Today
- Total
코딩뚠뚠
[머신러닝 공부] Tiny ML -19 / 제스처인식 어플리케이션 -3 본문
Chapter19. 제스처인식 어플리케이션
"
자이로센서를 이용해 간단한 제스처를 인식하는 어플리케이션을 만들어보자
"
목차 :
- 개요 -> 이전장
- 만들고자 하는 시스템 -> 이전장
- 기본흐름 코드 -> 이전장
- 제스처 감지 -> 이전장
- MCU에 배포
- 마치며
이전장링크
참고 repo
MCU에 배포
MCU는 sparkfun edge를 사용한다.
배터리슬롯에 배터리를 넣으면 무선으로 작동가능하기 때문에 이번 프로젝트에 적절한 장치이다.
▶ sparkfun_edge/accelerometer_handler.cc
▽ SetupAccelerometer()
▽ initAccelerometer()
TfLiteStatus SetupAccelerometer(tflite::ErrorReporter* error_reporter) {
// 클록 설정
am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0);
// 캐시 구성 설정
am_hal_cachectrl_config(&am_hal_cachectrl_defaults);
am_hal_cachectrl_enable();
// 저전력 작동 모드로 설정
am_bsp_low_power_init();
// 25Hz로 데이터를 수집
int accInitRes = initAccelerometer();
▽ 가속도계 자체에 위치한 특수 메모리 버퍼인 FIFO 버퍼를 활성화 한다.
// LIS2DH12에 FIFO 버퍼가 있으며 CPU 사용중 데이터를 축적한다.
// 모델의 추론주기가 1/25Hz = 1.28s 보다 빠르게 하여 데이터 덮어쓰기 방지
if (lis2dh12_fifo_set(&dev_ctx, 1)) {
error_reporter->Report("Failed to enable FIFO buffer.");
}
if (lis2dh12_fifo_mode_set(&dev_ctx, LIS2DH12_BYPASS_MODE)) {
error_reporter->Report("Failed to clear FIFO buffer.");
return 0;
}
if (lis2dh12_fifo_mode_set(&dev_ctx, LIS2DH12_DYNAMIC_STREAM_MODE)) {
error_reporter->Report("Failed to set streaming mode.");
return 0;
}
error_reporter->Report("Magic starts!");
return kTfLiteOk;
}
▽ init 끝나면 ReadAccelerometer() 호출하여 데이터 수집
bool ReadAccelerometer(tflite::ErrorReporter* error_reporter, float* input,
int length, bool reset_buffer) {
// 예측 성공 후 필요한 경우 버퍼를 비운다.
if (reset_buffer) {
memset(save_data, 0, 600 * sizeof(float));
begin_index = 0;
pending_initial_data = true;
// 정지후 10ms 대기 (대기시간이 없으면 데이터 읽을 시 코드 정지 가능성 있음)
am_util_delay_ms(10);
}
▽ 새 데이터가 있는지 확인하고, 없다면 함수를 반환한다.
lis2dh12_fifo_src_reg_t status;
if (lis2dh12_fifo_status_get(&dev_ctx, &status)) {
error_reporter->Report("Failed to get FIFO status.");
return false;
}
int samples = status.fss;
if (status.ovrn_fifo) {
samples++;
}
// Skip this round if data is not ready yet
if (samples == 0) {
return false;
}
▽ 데이터를 더 큰 버퍼에 저장한후 함수를 호출하여 다음 센서값을 구조체에 채운다.
axis3bit16_t data_raw_acceleration;
for (int i = 0; i < samples; i++) {
// Zero out the struct that holds raw accelerometer data
memset(data_raw_acceleration.u8bit, 0x00, 3 * sizeof(int16_t));
// If the return value is non-zero, sensor data was successfully read
if (lis2dh12_acceleration_raw_get(&dev_ctx, data_raw_acceleration.u8bit)) {
error_reporter->Report("Failed to get raw data.");
▽ 성공적으로 측정 완료시 부동소수점 값으로 변환하고 save_data[]버퍼에 연속적으로 기록한다.
▽ save_data[]는 3축*200 = 600 count가 되면 begin_index를 다시 0으로 둔다.
} else {
// 16비트값을 milli-G단위의 부동소수점으로 변환 및 버퍼의 현재위치에 저장
save_data[begin_index++] =
lis2dh12_from_fs2_hr_to_mg(data_raw_acceleration.i16bit[0]);
save_data[begin_index++] =
lis2dh12_from_fs2_hr_to_mg(data_raw_acceleration.i16bit[1]);
save_data[begin_index++] =
lis2dh12_from_fs2_hr_to_mg(data_raw_acceleration.i16bit[2]);
// 순환 배열처럼 처음부터 시작
if (begin_index >= 600) begin_index = 0;
}
}
▽ 추론전 초기 데이터가 충분한지 확인한다.
// Check if we are ready for prediction or still pending more initial data
if (pending_initial_data && begin_index >= 200) {
pending_initial_data = false;
}
// Return if we don't have enough data
if (pending_initial_data) {
return false;
}
▽ 데이터가 버퍼에 충분하다면, 버퍼로부터 데이터를 텐서에 대한 포인터로 복사한다.
for (int i = 0; i < length; ++i) {
int ring_array_index = begin_index + i - length;
if (ring_array_index < 0) {
ring_array_index += 600;
}
input[i] = save_data[ring_array_index];
}
return true;
}
▶ sparkfun_edge/output_handler.cc > 출력핸들러
▽ LED 출력모드로 설정
void HandleOutput(tflite::ErrorReporter* error_reporter, int kind) {
static bool is_initialized = false;
if (!is_initialized) {
am_hal_gpio_pinconfig(AM_BSP_GPIO_LED_RED, g_AM_HAL_GPIO_OUTPUT_12);
am_hal_gpio_pinconfig(AM_BSP_GPIO_LED_BLUE, g_AM_HAL_GPIO_OUTPUT_12);
am_hal_gpio_pinconfig(AM_BSP_GPIO_LED_GREEN, g_AM_HAL_GPIO_OUTPUT_12);
am_hal_gpio_pinconfig(AM_BSP_GPIO_LED_YELLOW, g_AM_HAL_GPIO_OUTPUT_12);
is_initialized = true;
}
// 추론 수행될때마다 노란 LED를 토글
static int count = 0;
++count;
if (count & 1) {
am_hal_gpio_output_set(AM_BSP_GPIO_LED_YELLOW);
} else {
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_YELLOW);
}
▽ 감지된 제스처를 시리얼로 확인하는 코드 (노가다로 별표 찍는 코드)
if (kind == 0) {
error_reporter->Report(
"WING:\n\r* * *\n\r * * * "
"*\n\r * * * *\n\r * * * *\n\r * * "
"* *\n\r * *\n\r");
am_hal_gpio_output_set(AM_BSP_GPIO_LED_RED);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_BLUE);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_GREEN);
} else if (kind == 1) {
error_reporter->Report(
"RING:\n\r *\n\r * *\n\r * *\n\r "
" * *\n\r * *\n\r * *\n\r "
" *\n\r");
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_RED);
am_hal_gpio_output_set(AM_BSP_GPIO_LED_BLUE);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_GREEN);
} else if (kind == 2) {
error_reporter->Report(
"SLOPE:\n\r *\n\r *\n\r *\n\r *\n\r "
"*\n\r *\n\r *\n\r * * * * * * * *\n\r");
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_RED);
am_hal_gpio_output_clear(AM_BSP_GPIO_LED_BLUE);
am_hal_gpio_output_set(AM_BSP_GPIO_LED_GREEN);
}
}
▶ 예제 실행하기
▽ 저장소 복제 (구버전이기 때문에 clone 보다는 위 참고 repo에서 zip을 다운받아 풀어 사용하기)
git clone https://github.com/tensorflow/tensorflow.git
cd tensorflow
▽ 바이너리 빌드
make -f tensorflow/lite/micro/tools/make/Makefile TARGET=sparkfun_edge magic_wand_bin
tensorflow/lite/micro/tools/make/gen/sparkfun_edge_cortex-m4/bin/magic_wand.bin
▽ 바이너리 서명
cp tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/tools/ \
apollo3_scripts/keys_info0.py tensorflow/lite/micro/tools/make/downloads/ \
AmbiqSuite-Rel2.0.0/tools/apollo3_scripts/keys_info.py
python3 tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/tools/ \
apollo3_scripts/create_cust_image_blob.py --bin tensorflow/lite/micro/tools/ \
make/gen/sparkfun_edge_cortex-m4/bin/magic_wand.bin --load-address 0xC000 \
--magic-num 0xCB -o main_nonsecure_ota --version 0x0
▽ 플래시 할 파일의 최종 버전을 생성
python3 tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/ \
tools/apollo3_scripts/create_cust_wireupdate_blob.py --load-address 0x20000 \
--bin main_nonsecure_ota.bin -i 6 -o main_nonsecure_wire --options 0x1
▽ 바이너리파일 플래시
ls /dev/tty*
export DEVICENAME=/dev/ttyUSB0
export BAUD_RATE=921600
sudo chmod 666 /dev/ttyUSB0
python3 tensorflow/lite/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/ \
tools/apollo3_scripts/uart_wired_update.py -b ${BAUD_RATE} ${DEVICENAME} \
-r 1 -f main_nonsecure_wire.bin -i 6
▽ 결과 확인
- 대기상태
- 제스처를 화면에 띄우기
screen ${DEVICENAME} 115200
- 움직임 (반 삼각형)
- 제스처 출력
반 삼각형 모양의 슬로프가 출력되었다.
여기까지 제스처 인식 모델을 MCU에서 실행시켜봤다.
다음장에서는 지금까지 사용했던 TF Lite에 대해서 조금 더 자세히 알아보도록 하자.
끝
'공부 > ML&DL' 카테고리의 다른 글
[머신러닝 공부] Tiny ML -21 / 최적화 문제 (0) | 2022.05.07 |
---|---|
[머신러닝 공부] Tiny ML -20 / TF Lite (0) | 2022.04.28 |
[머신러닝 공부] Tiny ML -18 / 제스처인식 어플리케이션 -2 (0) | 2022.04.16 |
[머신러닝 공부] Tiny ML -17 / 제스처인식 어플리케이션 -1 (0) | 2022.04.02 |
[머신러닝 공부] Tiny ML -16 / 인체감지 모델 훈련하기 (0) | 2022.03.28 |