개요
본 기술 아티클에서는 ADXL345 3축 가속도 센서와 STM32 마이크로컨트롤러를 결합하여 실시간으로 걸음 수를 측정하는 보행 수 측정기(Pedometer)를 설계하고 구현하는 방법을 설명한다. 보행 중 발생하는 가속도의 변화 패턴을 분석하고, 동적 임계값(Dynamic Threshold)과 상태 머신(State Machine) 알고리즘을 적용하여 측정 정확도를 높이는 것이 핵심이다.
하드웨어 구성 및 연결
시스템은 제어부인 STM32F103C8T6, 가속도 측정을 위한 ADXL345, 정보를 출력하는 OLED 디스플레이, 데이터를 저장하는 EEPROM으로 구성된다.
핵심 부품 리스트
| 구분 |
모델명 |
역할 |
| MCU |
STM32F103C8T6 |
데이터 처리 및 알고리즘 연산 |
| Sensor |
ADXL345 |
X, Y, Z축 가속도 데이터 수집 |
| Display |
OLED (I2C) |
보행 수 및 칼로리 정보 표시 |
| Storage |
AT24C02 |
전원 차단 시 누적 데이터 보존 |
ADXL345 핀 맵 (I2C 모드)
- VCC/GND: 3.3V 전원 및 접지
- SDA/SCL: I2C 데이터 및 클럭 라인 (4.7KΩ 풀업 저항 권장)
- CS: High (3.3V) 설정으로 I2C 모드 활성화
- SDO: GND 연결 시 장치 주소 0x53 사용
ADXL345 드라이버 및 제어 로직
센서의 가속도 데이터를 읽어오기 위해서는 레지스터 설정과 초기화가 필요하다.
#include "adxl345_driver.h"
// ADXL345 주요 레지스터 주소
#define REG_DEVID 0x00
#define REG_BW_RATE 0x2C
#define REG_POWER_CTL 0x2D
#define REG_DATA_FORMAT 0x31
#define ADXL_XYZ_START 0x32
// 센서 초기화 함수
bool Sensor_Config(void) {
uint8_t sensor_id;
// ID 확인 (기본값: 0xE5)
I2C_ReadReg(ADXL345_ADDR, REG_DEVID, &sensor_id, 1);
if(sensor_id != 0xE5) return false;
// ±2g 범위 설정, 10비트 해상도
I2C_WriteReg(ADXL345_ADDR, REG_DATA_FORMAT, 0x00);
// 출력 데이터 속도(ODR) 100Hz 설정
I2C_WriteReg(ADXL345_ADDR, REG_BW_RATE, 0x0A);
// 측정 모드 활성화
I2C_WriteReg(ADXL345_ADDR, REG_POWER_CTL, 0x08);
return true;
}
// 3축 원시 데이터 읽기
void Read_Acceleration(int16_t *acc_x, int16_t *acc_y, int16_t *acc_z) {
uint8_t raw_buffer[6];
I2C_ReadReg(ADXL345_ADDR, ADXL_XYZ_START, raw_buffer, 6);
*acc_x = (int16_t)((raw_buffer[1] << 8) | raw_buffer[0]);
*acc_y = (int16_t)((raw_buffer[3] << 8) | raw_buffer[2]);
*acc_z = (int16_t)((raw_buffer[5] << 8) | raw_buffer[4]);
}
걸음 수 감지 알고리즘
보행 측정의 핵심은 3축 가속도의 합성 벡터(Magnitude)를 구하고, 이를 필터링하여 불필요한 노이즈를 제거한 뒤 정점(Peak)을 찾는 것이다.
typedef struct {
float filtered_val;
float last_val;
float peak_limit;
float valley_limit;
uint32_t total_steps;
uint32_t last_step_tick;
uint8_t detect_state; // 0: 하강 대기, 1: 상승 대기
} WalkContext;
#define FILTER_COEFF 0.15f
#define MIN_STEP_INTERVAL 250 // ms
void Process_Step_Detection(WalkContext *ctx, int16_t rawX, int16_t rawY, int16_t rawZ) {
// 1. 합성 가속도 크기 계산
float vector_sum = sqrtf((float)rawX * rawX + (float)rawY * rawY + (float)rawZ * rawZ);
// 2. 저역 통과 필터(LPF) 적용
ctx->filtered_val = (FILTER_COEFF * vector_sum) + ((1.0f - FILTER_COEFF) * ctx->last_val);
ctx->last_val = ctx->filtered_val;
uint32_t now = HAL_GetTick();
// 3. 피크 감지 상태 머신
switch(ctx->detect_state) {
case 0: // 골(Valley) 지점 통과 확인
if(ctx->filtered_val < ctx->valley_limit) {
ctx->detect_state = 1;
}
break;
case 1: // 산(Peak) 지점 통과 확인
if(ctx->filtered_val > ctx->peak_limit) {
// 오동작 방지를 위한 시간 간격 체크 (최소 250ms)
if(now - ctx->last_step_tick > MIN_STEP_INTERVAL) {
ctx->total_steps++;
ctx->last_step_tick = now;
Update_Display(ctx->total_steps);
}
ctx->detect_state = 0;
}
break;
}
// 4. 동적 임계값 업데이트 (신호 강도에 따라 적응)
if(ctx->filtered_val > ctx->peak_limit) {
ctx->peak_limit = ctx->filtered_val * 0.75f;
} else {
ctx->peak_limit = ctx->peak_limit * 0.995f + ctx->filtered_val * 0.005f;
}
}
소모 칼로리 및 이동 거리 계산
단순한 걸음 수 측정을 넘어 사용자의 신체 정보를 기반으로 부가 정보를 산출할 수 있다.
// 칼로리 소모량 계산 (MET 계수 활용)
float Get_Calories(uint32_t steps, float user_weight) {
// 평균적인 보폭 0.7m, 걷기 MET 값 3.5 기준
float distance_km = (steps * 0.7f) / 1000.0f;
float time_h = (steps / 100.0f) / 60.0f; // 분당 100보 가정
return 3.5f * user_weight * time_h;
}
// 이동 거리 계산
float Get_Distance(uint32_t steps) {
return (steps * 0.75f); // 단위: 미터
}
저전력 설계 및 인터럽트 활용
배터리 수명을 극대화하기 위해 보행이 감지되지 않을 때 장치를 저전력 모드로 전환하는 기능이 필수적이다. ADXL345의
Activity/Inactivity 인터럽트를 활용하여 시스템을 깨울 수 있다.
void Power_Save_Config(void) {
// 활동 감지 임계값 설정 (62.5mg/LSB)
I2C_WriteReg(ADXL345_ADDR, 0x24, 0x10);
// 감지 축 설정 (X, Y, Z 모두 활성화)
I2C_WriteReg(ADXL345_ADDR, 0x27, 0x70);
// 인터럽트 매핑: Activity 인터럽트를 INT1 핀으로 출력
I2C_WriteReg(ADXL345_ADDR, 0x2E, 0x10);
// STM32 STOP 모드 진입 전 센서 대기 모드 설정 가능
}
정확도 개선 방안
- 중력 가속도 보정: 사용자가 기기를 착용하는 방향에 따라 X, Y, Z축의 정적 가속도가 달라지므로 초기 부팅 시 오프셋(Offset) 제거 과정을 거쳐야 한다.
- 가짜 움직임 필터링: 팔을 휘두르거나 가벼운 진동이 발생하는 경우 보행으로 인식하지 않도록 연속적으로 최소 5~10보 이상 감지될 때만 카운트를 시작하는 로직을 추가한다.
- 동적 샘플링: 보행 속도에 따라 가속도 데이터의 샘플링 주기를 가변적으로 조절하면 전력 효율과 정확도를 동시에 확보할 수 있다.