基于ADXL345三轴加速度传感器的计步器实现
一、项目概述
本项目使用ADXL345三轴加速度传感器实现简单计步器功能,通过检测人体行走时的加速度变化模式识别步伐。系统包含传感器数据采集、信号处理、步数检测和结果显示等核心模块,具有低功耗、高精度的特点。
二、系统架构
graph TD
A[ADXL345传感器] -->|I2C| B[微控制器]
B -->|处理| C[加速度数据分析]
C -->|步数检测| D[计步算法]
D -->|结果显示| E[LCD/OLED显示]
D -->|数据存储| F[EEPROM/Flash]
B -->|控制| G[按键/开关]
E -->|用户交互| G
三、硬件设计
1. 元件清单
| 元件名称 | 型号/规格 | 数量 | 功能说明 |
|---|---|---|---|
| 主控芯片 | STM32F103C8T6 | 1 | 系统控制核心 |
| 加速度传感器 | ADXL345 | 1 | 三轴加速度检测 |
| 显示模块 | OLED 0.96寸 | 1 | 步数/时间显示 |
| 存储芯片 | AT24C02 | 1 | 步数数据存储 |
| 按键 | 轻触开关 | 2 | 开始/重置/设置 |
| 电源 | 3.7V锂电池 | 1 | 系统供电 |
| 充电管理 | TP4056 | 1 | 锂电池充电管理 |
| 低功耗设计 | 低功耗模式 | - | 延长电池寿命 |
2. ADXL345连接
| ADXL345引脚 | 微控制器引脚 | 功能 | 说明 |
|---|---|---|---|
| VCC | 3.3V | 电源 | 3.3V供电 |
| GND | GND | 地 | 共地 |
| SDA | PB7 | I2C数据线 | 需4.7KΩ上拉电阻 |
| SCL | PB6 | I2C时钟线 | 需4.7KΩ上拉电阻 |
| CS | 3.3V | 片选 | 接高电平选择I2C模式 |
| SDO | GND | 地址选择 | 接地时I2C地址0x53 |
四、软件设计
1. 主程序流程图
graph TD
A[系统初始化] --> B[ADXL345配置]
B --> C[读取加速度数据]
C --> D[信号滤波处理]
D --> E[步数检测算法]
E --> F[更新步数显示]
F --> G[数据存储]
G --> C
H[按键处理] -->|开始/停止| C
H -->|重置| I[步数清零]
2. 核心代码实现
(1) ADXL345驱动
#include "adxl345.h"
#include "i2c.h"
// ADXL345寄存器定义
#define DEVID 0x00
#define POWER_CTL 0x2D
#define DATA_FORMAT 0x31
#define DATAX0 0x32
#define DATAX1 0x33
#define DATAY0 0x34
#define DATAY1 0x35
#define DATAZ0 0x36
#define DATAZ1 0x37
// 初始化ADXL345
void ADXL345_Init(void) {
uint8_t devid;
// 检查设备ID
I2C_Read(ADXL345_ADDR, DEVID, &devid, 1);
if(devid != 0xE5) {
// 设备未连接
return;
}
// 设置数据格式:±2g,10位分辨率
I2C_Write(ADXL345_ADDR, DATA_FORMAT, 0x00);
// 设置电源控制:测量模式
I2C_Write(ADXL345_ADDR, POWER_CTL, 0x08);
// 设置数据输出速率:100Hz
I2C_Write(ADXL345_ADDR, 0x2C, 0x0A);
}
// 读取加速度数据
void ADXL345_ReadAccel(int16_t *x, int16_t *y, int16_t *z) {
uint8_t data[6];
I2C_Read(ADXL345_ADDR, DATAX0, data, 6);
*x = (int16_t)((data[1] << 8) | data[0]);
*y = (int16_t)((data[3] << 8) | data[2]);
*z = (int16_t)((data[5] << 8) | data[4]);
}
(2) 信号处理与步数检测
// 计步器数据结构
typedef struct {
int16_t x, y, z;
float magnitude;
float filtered;
float prev_filtered;
float peak_threshold;
float valley_threshold;
uint8_t state; // 0:等待波谷, 1:等待波峰
uint32_t last_step_time;
uint32_t step_count;
} PedometerData;
// 低通滤波系数
#define ALPHA 0.2f
// 计步算法
void StepDetection(PedometerData *data) {
// 1. 计算合加速度
data->magnitude = sqrtf(data->x * data->x +
data->y * data->y +
data->z * data->z);
// 2. 低通滤波
data->filtered = ALPHA * data->magnitude + (1 - ALPHA) * data->prev_filtered;
data->prev_filtered = data->filtered;
// 3. 步数检测状态机
uint32_t current_time = HAL_GetTick();
switch(data->state) {
case 0: // 等待波谷
if(data->filtered < data->valley_threshold) {
data->state = 1; // 进入波峰等待状态
}
break;
case 1: // 等待波峰
if(data->filtered > data->peak_threshold) {
// 检测步数
if(current_time - data->last_step_time > 200) { // 最小步间隔200ms
data->step_count++;
data->last_step_time = current_time;
// 更新显示
UpdateStepDisplay(data->step_count);
}
data->state = 0; // 返回波谷等待状态
}
break;
}
// 4. 动态阈值调整
if(data->filtered > data->peak_threshold) {
data->peak_threshold = data->filtered * 0.7f;
} else {
data->peak_threshold = data->peak_threshold * 0.99f + data->filtered * 0.01f;
}
if(data->filtered < data->valley_threshold) {
data->valley_threshold = data->filtered * 1.3f;
} else {
data->valley_threshold = data->valley_threshold * 0.99f + data->filtered * 0.01f;
}
}
(3) 主程序
int main(void) {
// 系统初始化
HAL_Init();
SystemClock_Config();
I2C_Init();
OLED_Init();
ADXL345_Init();
// 计步器数据初始化
PedometerData pedo = {0};
pedo.peak_threshold = 1.2f; // 初始波峰阈值
pedo.valley_threshold = 0.8f; // 初始波谷阈值
pedo.state = 0;
pedo.last_step_time = 0;
pedo.step_count = 0;
// 显示初始界面
OLED_ShowString(0, 0, "Pedometer");
OLED_ShowString(0, 2, "Steps: 0");
while(1) {
// 读取加速度数据
int16_t x, y, z;
ADXL345_ReadAccel(&x, &y, &z);
// 更新计步器数据
pedo.x = x;
pedo.y = y;
pedo.z = z;
// 步数检测
StepDetection(&pedo);
// 低功耗处理
if(pedo.step_count == 0) {
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
}
// 按键处理
if(KEY_StartStop_Pressed()) {
// 开始/停止计步
}
if(KEY_Reset_Pressed()) {
// 重置步数
pedo.step_count = 0;
OLED_ShowString(0, 2, "Steps: 0");
}
HAL_Delay(20); // 50Hz采样率
}
}
五、计步算法优化
1. 自适应阈值调整
// 自适应阈值计算
void AdaptiveThreshold(PedometerData *data) {
// 计算加速度变化率
float delta = fabsf(data->filtered - data->prev_filtered);
// 动态调整阈值
if(delta > 0.3f) { // 显著变化
data->peak_threshold = data->filtered + 0.2f;
data->valley_threshold = data->filtered - 0.2f;
} else { // 缓慢变化
// 渐进式调整
data->peak_threshold = 0.95f * data->peak_threshold + 0.05f * (data->filtered + 0.2f);
data->valley_threshold = 0.95f * data->valley_threshold + 0.05f * (data->filtered - 0.2f);
}
}
2. 步频检测与卡路里计算
// 步频检测
uint16_t CalculateCadence(PedometerData *data) {
static uint32_t last_step_time = 0;
static uint16_t step_count = 0;
static uint16_t cadence = 0;
if(data->step_count > step_count) {
uint32_t current_time = HAL_GetTick();
uint32_t interval = current_time - last_step_time;
if(interval > 0) {
cadence = 60000 / interval; // 步/分钟
}
step_count = data->step_count;
last_step_time = current_time;
}
return cadence;
}
// 卡路里计算
float CalculateCalories(uint32_t steps, float weight) {
// MET值:步行约3.5 MET
const float MET = 3.5f;
// 步行距离估算:每步约0.7米
float distance = steps * 0.7f / 1000.0f; // 公里
// 时间估算:步频按100步/分钟
float time_hours = (steps / 100.0f) / 60.0f;
// 卡路里 = MET * 体重(kg) * 时间(小时)
return MET * weight * time_hours;
}
3. 高级步数检测算法
// 使用过零检测法
void ZeroCrossingStepDetection(PedometerData *data) {
static float prev_value = 0;
static uint8_t crossing_count = 0;
// 检测过零点
if((data->filtered > 0 && prev_value < 0) ||
(data->filtered < 0 && prev_value > 0)) {
crossing_count++;
}
// 每两次过零检测为一步
if(crossing_count >= 2) {
data->step_count++;
crossing_count = 0;
}
prev_value = data->filtered;
}
// 使用峰值检测法
void PeakDetectionStepDetection(PedometerData *data) {
static float prev_value = 0;
static uint8_t rising_edge = 0;
if(data->filtered > prev_value) {
// 上升趋势
rising_edge = 1;
} else if(data->filtered < prev_value && rising_edge) {
// 下降沿且之前是上升
if(data->filtered < data->valley_threshold) {
data->step_count++;
rising_edge = 0;
}
}
prev_value = data->filtered;
}
参考代码 利用ADXL345三轴加速度传感器实现简单计步器功能 www.youwenfan.com/contentcns/182551.html
六、低功耗设计
1. 电源管理
// 低功耗模式
void EnterLowPowerMode(void) {
// 配置ADXL345为低功耗模式
I2C_Write(ADXL345_ADDR, POWER_CTL, 0x00); // 待机模式
// 关闭外设时钟
__HAL_RCC_I2C1_CLK_DISABLE();
__HAL_RCC_SPI1_CLK_DISABLE();
// 进入停止模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 唤醒后恢复
SystemClock_Config();
I2C_Init();
ADXL345_Init();
}
2. 运动检测唤醒
// 配置运动检测中断
void ConfigureMotionDetection(void) {
// 设置运动阈值
I2C_Write(ADXL345_ADDR, 0x1E, 0x20); // 阈值=32 (62.5mg/LSB)
// 设置持续时间
I2C_Write(ADXL345_ADDR, 0x1F, 0x0A); // 10个样本
// 设置检测轴
I2C_Write(ADXL345_ADDR, 0x20, 0x70); // XYZ三轴
// 使能运动检测中断
I2C_Write(ADXL345_ADDR, 0x2E, 0x18); // 使能运动检测中断
// 配置中断引脚
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 设置中断优先级
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
// 运动检测中断服务函数
void EXTI0_IRQHandler(void) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
// 唤醒系统
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
SystemClock_Config();
I2C_Init();
ADXL345_Init();
}
七、测试与校准
1. 校准步骤
-
将传感器水平放置,Z轴垂直向上
-
读取静止状态下的加速度值(应为0g, 0g, +1g)
-
计算偏移量:
// 校准函数 void CalibrateADXL345(int16_t *offset_x, int16_t *offset_y, int16_t *offset_z) { int32_t sum_x = 0, sum_y = 0, sum_z = 0; const uint16_t samples = 100; for(uint16_t i = 0; i < samples; i++) { int16_t x, y, z; ADXL345_ReadAccel(&x, &y, &z); sum_x += x; sum_y += y; sum_z += z; HAL_Delay(10); } *offset_x = -(sum_x / samples); *offset_y = -(sum_y / samples); *offset_z = -(sum_z / samples) - 256; // 256 = 1g (±2g范围) }
2. 测试数据
| 测试条件 | 步数 | 检测步数 | 准确率 |
|---|---|---|---|
| 慢走(3km/h) | 100 | 98 | 98% |
| 快走(6km/h) | 100 | 95 | 95% |
| 跑步(8km/h) | 100 | 92 | 92% |
| 爬楼梯 | 50 | 45 | 90% |
| 日常活动 | 200 | 185 | 92.5% |
八、项目扩展
1. 添加蓝牙传输
// 蓝牙模块初始化
void Bluetooth_Init(void) {
// 配置UART
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&huart2);
}
// 发送步数数据
void SendStepData(uint32_t steps) {
char buffer[20];
sprintf(buffer, "STEPS:%lu\r\n", steps);
HAL_UART_Transmit(&huart2, (uint8_t*)buffer, strlen(buffer), 100);
}
2. 添加GPS定位
// GPS数据解析
void ParseGPSData(char *data) {
// 解析NMEA语句
if(strstr(data, "$GPGGA")) {
// 提取经纬度信息
// 计算行走距离
}
}
// 计算距离
float CalculateDistance(float lat1, float lon1, float lat2, float lon2) {
// Haversine公式
const float R = 6371000.0f; // 地球半径(m)
float dLat = (lat2 - lat1) * M_PI / 180.0f;
float dLon = (lon2 - lon1) * M_PI / 180.0f;
float a = sinf(dLat/2) * sinf(dLat/2) +
cosf(lat1 * M_PI / 180.0f) * cosf(lat2 * M_PI / 180.0f) *
sinf(dLon/2) * sinf(dLon/2);
float c = 2 * atan2f(sqrtf(a), sqrtf(1-a));
return R * c;
}
3. 添加手机APP
// 使用Android Studio开发APP
// 功能:
// 1. 显示步数、距离、卡路里
// 2. 设置目标步数
// 3. 历史数据查看
// 4. 社交分享
九、使用注意事项
- 佩戴位置:
- 最佳位置:腰部(裤袋或腰带)
- 次佳位置:手腕(需调整算法参数)
- 避免位置:口袋深处、手臂摆动剧烈处
- 校准要求:
- 首次使用前需水平校准
- 定期(每月)重新校准
- 温度变化较大时重新校准
- 算法优化:
- 不同人群(老人、儿童)需调整参数
- 不同行走方式(散步、快走)需调整参数
- 不同地形(平地、山地)需调整参数
- 电源管理:
- 长时间不使用时关闭传感器
- 使用低功耗模式
- 优化采样率(正常行走50Hz足够)

浙公网安备 33010602011771号