项目概述

        ESP32-BMP388 运动追踪器是一款基于气压高度测量的便携式运动监测设备,集成卡尔曼滤波、实时折线图、指针式高度表、WiFi Web 仪表盘与历史数据持久化。设备通过 BMP388 气压传感器实时采集大气压强,利用 ISA(国际标准大气)公式将气压转换为海拔,再经 2D 卡尔曼滤波器平滑后得到稳定的高度与垂直速度读数。用户可通过两个物理按键在彩色 TFT 屏上切换 5 个功能界面,记录运动过程并查看历史数据;同时设备开放 WiFi 热点,手机连接后即可访问 Web 仪表盘进行实时监控、远程控制与历史数据导出。

  • 主控:零知ESP32
  • 传感器:BMP388 数字气压传感器
  • 显示屏:ST7789 TFT,240×240,SPI,6 引脚
  • 交互:2 个轻触按键(GPIO27 / GPIO14,内部上拉)
  • 无线:ESP32 内置 WiFi,开放 AP 热点供手机接入
  • 存储:RTC 内存(深度睡眠保留) + SPIFFS 文件系统双备份

项目亮点

  1. 2D 卡尔曼传感器融合 — 同时估计海拔与垂直速度,参数可调(Q/R),显著抑制 BMP388 单次测量抖动(±0.5m → 平滑 ±0.05m)。
  2. 5 屏彩色 UI — 实时数据屏(带海拔+气压双曲线滚动图)、指针式高度表(半圆仪表盘 + 红色指针)、运动总结屏、设置屏、历史柱状图屏,全部基于 TFT_eSPI Sprite 帧缓冲防闪烁绘制。
  3. 指针式高度表 — 模拟飞机高度表的半圆弧 + 刻度 + 指针可视化,量程动态跟随当前海拔 ±50m。
  4. WiFi Web 仪表盘 — 深色渐变 UI,英雄卡 + 三色小卡布局,支持手机端实时监控、远程开始/停止/重置、一键 CSV 导出,刷新带闪光动画。
  5. 数据持久化 — 运动会话先写入 RTC_DATA_ATTR 深度睡眠保留内存,再镜像到 SPIFFS /sessions.dat,重启不丢,最多保存 20 条,循环淘汰。
  6. 平滑数字滚动动画 — 海拔大字每帧 25% 趋近目标值,避免数字跳变;实时折线图采用 80 点环形缓冲,自动归一化双轴显示。
  7. 状态栏信息集成 — 顶部统一显示模式名 + 温度(青色)+ 电池图标(绿/黄/红三色)+ 录制红点。
  8. 可配置性 — 全部参数(引脚、滤波系数、按键时序、刷新率、WiFi 凭据)集中在 config.h,支持 _IIR_FILTER_OSRCALMAN_Q/R 调参。

项目难点及解决方案

难点 1:BMP388 原始气压抖动严重,海拔数字跳变

问题:单次气压测量噪声导致海拔读数波动 ±0.5m,数字频繁跳动影响可读性。
解决:实现 2D 卡尔曼滤波器,状态向量 [altitude, vertical_speed],通过过程噪声 Q 与测量噪声 R 协调平滑度与响应速度;再对卡尔曼输出的速度做一阶低通(α=0.3)进一步抑制。

难点 2:16 位色 Sprite 占用 115KB,超过 ESP32 可分配堆

问题:TFT_eSPI 16 位色 Sprite 240×240 需 115KB,ESP32 单次分配失败,导致白屏。
解决:改用 8 位色深(setColorDepth(8)),占用降至 ~57KB,分配成功;flush() 检查 _sprite.created() 防御性推送。


目录

一、硬件系统部分

1.1 硬件清单

1.2 接线方案

1.3 硬件连接图

1.4 实物连接图

二、软件架构设计

2.1 系统初始化

2.2 主循环逻辑

三、代码拆分讲解

3.1 系统库 + 配置

3.2 SessionData 数据结构

3.3 KalmanFilter2D

3.4 AltitudeTracker

3.5 BMP388Driver

3.6 DisplayManager

四、操作过程及数据展示

4.1 操作步骤

4.2 演示视频

五、技术原理

5.1 工作原理

5.2 工作模式配置

六、常见问题指引

Q1:串口输出 [BMP388] 传感器未找到!

Q2:WiFi 热点连不上或打不开192.168.4.1

Q3:重启后历史数据丢失

Q4:海拔数字明显偏高/偏低


一、硬件系统部分

1.1 硬件清单

名称 型号 数量 用途
主控板 零知ESP32开发板 1 MCU + WiFi
扩展板 零知ESP32扩展板 1 方便接线
气压传感器 BMP388 模块 1 气压/温度采集
彩色显示屏 ST7789 TFT 240×240 1 UI 显示
杜邦线 端子对杜邦线母头 若干 连接
USB Type-C 线 Type-C 1 烧录/供电

可选:锂电池 + 充电模块(用于电量显示演示),否则电池图标静态。

1.2 接线方案

BMP388 → ESP32 (I²C)

BMP388 ESP32 GPIO 说明
VCC 3V3 3.3V 供电
GND GND 共地
SDA 21 I²C 数据
SCL 22 I²C 时钟
SDO GND 设定 I²C 地址 0x76(接 VCC 则为 0x77)

1.3 硬件连接图

1.4 实物连接图


二、软件架构设计

2.1 系统初始化

setup() 执行顺序(位于 MotionTracker.ino 末尾):

void setup() {
    disableBrownout();       // 1. 禁用欠压复位(避免 WiFi 启动时重启)
    Serial.begin(115200);    // 2. 串口初始化, 波特率 115200

    display.begin();         // 3. ST7789 + Sprite 帧缓冲初始化
    display.showSplash();    //    显示启动动画 (进度条)

    bmp388.begin();          // 4. BMP388 I²C + 过采样 + IIR 配置
    altTracker.begin();      // 5. 卡尔曼滤波器复位
    ui.begin();              // 6. 两按键 GPIO 初始化 + 消抖
    logger.begin();          // 7. RTC 内存恢复 + SPIFFS 加载历史
    wifi.begin();            // 8. 开放 AP 热点 "MotionTracker" + 启动 WebServer

    // 9. 预热 20 次采样建立基准海拔
    for(int i=0;i<20;i++){ bmp388.readData(p,t); altTracker.update(p,t,0.1f); delay(50); }
}

关键初始化参数:

  • BMP388:温度/压力过采样 8X、IIR 滤波系数 3、ODR 50Hz;
  • Sprite:8 位色深 240×240 ≈ 57KB;
  • WiFi AP:SSID=MotionTracker 密码=12345678,IP 默认 192.168.4.1

2.2 主循环逻辑

loop() 采用 10Hz 主节拍 + 5Hz 显示节拍 + 每帧 WiFi 处理:

loop() 每帧 (~5ms 间隔):
  ├─ 1. 按键处理
  │    ├─ button1: 检测短/长/超长按 → 调用 handleEvent(1)
  │    ├─ button2: 检测短/长按 → 调用 handleEvent(2) 切屏
  │    └─ WiFi 开关: 检测设置项变化 → wifi.setEnabled()
  │
  ├─ 2. 传感器更新 (10Hz, 每 100ms):
  │    ├─ bmp388.readData(&p, &t)
  │    ├─ altTracker.update(p, t, dt)        # 卡尔曼滤波
  │    ├─ display.addChartSample(altitude)    # 折线图采样
  │    └─ display.addPressureSample(p/100)    # 气压曲线采样
  │
  ├─ 3. 海拔平滑动画 (每帧):
  │    └─ display.tickAnimation(targetAlt)     # 25%/帧 趋近
  │
  ├─ 4. 显示刷新 (5Hz, 每 200ms):
  │    └─ updateDisplay()                       # 根据当前屏绘制
  │
  ├─ 5. WiFi (每帧, ~100Hz):
  │    ├─ wifi.updateData(...)                  # 推送最新数据
  │    ├─ wifi.notify()                         # 处理 HTTP 客户端
  │    └─ if(wifi.hasCommand()) handleWiFiCommand(...)
  │
  └─ delay(5)

设计要点:

  • 传感器 10Hz、显示 5Hz:传感器更新更快以保证滤波器收敛,显示降速避免画面过快刷新;
  • WiFi 每帧 notify:HTTP 请求近乎即时响应,不受显示或传感器节拍影响;
  • 每帧 tickAnimation:让海拔数字平滑变化,与 5Hz 显示刷新独立,动画更连续。

三、代码拆分讲解

MotionTracker.ino 单文件结构(从上到下):

3.1 系统库 + 配置

#include <Adafruit_BMP3XX.h>
#define GFXFF 1
#include <TFT_eSPI.h>          // FreeFont 支持

#include <WiFi.h>
#include <FS.h>
using namespace fs;             // 解决 Lingzhi core 命名空间冲突
#include <WebServer.h>

#include "config.h"             // 用户可编辑参数

3.2 SessionData 数据结构

struct SessionData {
    float totalAscent, totalDescent, maxAltitude, minAltitude;
    float avgSpeed, startAltitude;
    uint32_t duration, floorCount, sampleCount, timestamp;
    bool valid;
};

贯穿日志记录、历史柱状图、CSV 导出。

3.3 KalmanFilter2D

2 状态卡尔曼滤波器,状态向量 [海拔, 垂直速度]

class KalmanFilter2D {
    void update(float z_alt, float dt);   // 预测 + 修正
    float getAltitude() const;
    float getVelocity() const;
    void reset(float alt=0);
private:
    float _x[2], _covar[2][2], _Q[2], _R;
};

update() 先用速度做时间预测(x0 += dt·v),再读气压做修正:

_x[0] = x0 + K0 * y;   // 海拔更新
_x[1] = x1 + K1 * y;   // 速度更新

Q/R 越大平滑越强但响应越慢,默认 Q=[0.1, 0.5],R=0.25。

3.4 AltitudeTracker

封装滤波后的高度追踪,含会话管理:

  • 气压→海拔:ISA 公式 44330·(1−(P/P0)^0.190295)
  • 会话生命周期startSession() 记录起始海拔与时间;update() 累计爬升/下降/楼层;stopSession() 计算总时长
  • 楼层计数:累计正位移达 _floorCount × FLOOR_HEIGHT 即 +1
  • 平均速度 = (爬升 + 下降) / 时长

3.5 BMP388Driver

封装 Adafruit_BMP3XX:

bool begin();              // I²C 0x76 + 过采样 8X + IIR 3 + ODR 50Hz
bool readData(float &p, float &t);   // 一次气压+温度读取

读取失败自动打印警告但不阻塞。

3.6 DisplayManager

所有 TFT 绘制,使用 TFT_eSPI + TFT_eSprite 帧缓冲:

class DisplayManager {
    TFT_eSPI _tft;
    TFT_eSprite _sprite;    // 帧缓冲
    float _chartBuf[80];    // 海拔环形缓冲
    float _presBuf[80];     // 气压环形缓冲
public:
    void drawRealtimeScreen(...);     // 实时屏 - 含双曲线
    void drawGaugeScreen(...);       // 指针式高度表屏
    void drawSummaryScreen(...);     // 运动总结屏
    void drawSettingsScreen(...);    // 设置屏 - Clear History 两行
    void drawHistoryScreen(...);     // 历史屏 - 柱状图
    void addChartSample(float);      // 采样
    void addPressureSample(float);
    void tickAnimation(float);       // 25%/帧平滑
};

关键辅助:_drawStatusBar() 状态栏(温度/电池/录制点)、_drawGauge() 半圆弧+指针绘制、_drawLineChart() 双曲线自动缩放。


四、操作过程及数据展示

4.1 操作步骤

步骤 1:烧录固件

选择开发板 “零知ESP32”,上传 MotionTracker.ino

步骤 2:首次开机

  • 屏幕显示 进度条启动动画 约 2 秒;
  • 串口(115200)输出 [BMP388] OK[Display] Sprite OK[WiFi] IP: 192.168.4.1
  • 自动预热采样 20 次,串口输出 基准海拔: xxx.xx m

步骤 3:屏幕操作(两按键)

按键 短按 (<800ms) 长按 (800~3000ms) 超长按 (≥3000ms)
KEY1 (GPIO27) 实时屏:开始/停止记录
设置屏:调整当前项
重置会话(实时屏) 同长按(重置)
KEY2 (GPIO14) 切到下一屏 切到上一屏

按键 2 短按循环切换 5 个屏幕:

REALTIME → GAUGE → SUMMARY → SETTINGS → HISTORY → REALTIME

各屏内容

  • REALTIME 实时屏:大字海拔 + 方向箭头(↑红/↓蓝/→灰) + 速度/爬升/下降数据行 + 海拔(深蓝)+气压(橙)双曲线滚动图 + 底部提示
  • GAUGE 高度表屏:半圆弧 + 5 刻度(动态 ±50m 量程) + 红指针 + 数字读数 + 底部气压/速度
  • SUMMARY 总结屏:本次会话的爬升/下降/最高/平均速度/楼层/时长
  • SETTINGS 设置屏:4 项可调:海平面气压(±0.5 hPa) / 楼层高度(±0.1m) / WiFi 开关 / 清除历史(“Clear History” 标签 + “[Execute]” 分两行显示)
  • HISTORY 历史屏:最近 5 次会话柱状图,按最大爬升归一缩放,柱顶标注数值,下方显示 #编号

步骤 4:开始/停止记录

  1. 在 REALTIME 屏 短按 KEY1 → 状态栏右侧出现红色脉冲圆点,记录开始;
  2. 屏幕实时滚动双曲线,爬升/下降数字逐步累加;
  3. 再次短按 KEY1 → 停止记录,会话自动保存到 RTC+SPIFFS。

步骤 5:查看总结

切到 SUMMARY 屏即可查看本次会话的总爬升、总下降、最高海拔、平均速度、楼层数、时长。

步骤 6:查看历史

切到 HISTORY 屏,柱状图对比最近 5 次会话的总爬升量,柱顶显示精确数值。

步骤 7:手机 WiFi 仪表盘

  1. 手机搜索并连接 WiFi:
    • SSID: MotionTracker
    • 密码: 12345678
  2. 浏览器打开 http://192.168.4.1
  3. 页面显示:
    • 英雄卡:当前海拔(cm 单位,×100 换算)
    • 3 个彩色顶条小卡:速度(m/s) / 爬升(cm) / 下降(cm)
    • 3 按钮:开始(绿)/ 停止(红)/ 重置(灰)
  4. 页面 1 秒自动刷新,数值变化时触发闪光动画
  5. 点击 “导出历史数据 (CSV)” → 下载 motiontracker.csv,含所有会话的 10 列数据。

4.2 演示视频

零知派ESP32-基于BMP388制作运动追踪器


五、技术原理

5.1 工作原理

气压 → 海拔(ISA 国际标准大气)

BMP388 测得大气压 P 后,代入 ISA 公式:

altitude = 44330 × (1 − (P / P0)^0.190295)

其中 P0 = 海平面气压(默认 1013.25 hPa,可在设置屏调整 ±0.5 hPa/步)。原理是大气压随高度按指数衰减(等温层模型),系数 0.190295 = (R·L)/(g·M) 推导自气体状态方程。

2D 卡尔曼滤波

状态向量 x = [altitude, velocity],每步:

  1. 预测altitude_predicted = altitude + dt·velocity(匀速模型)
  2. 更新(读气压 z 后):
    • 残差 y = z − altitude_predicted
    • 卡尔曼增益 K = P / (P + R)(P=估计协方差, R=测量噪声)
    • altitude += K·yvelocity += K·velocity·y
  3. 协方差更新P = (1−K)·P + Q(Q=过程噪声,注入不确定性)

效果:单次测量噪声 ±0.5m → 平滑后约 ±0.05m。速度由海拔变化反推,无需加速度计。

楼层计数

累计垂直正位移(在 smoothSpeed > MIN_SPEED_THRESHOLD 阈值时才计数,过滤噪声):

_floorAccumulator += (新海拔 − 旧海拔)
floorCount = floor(_floorAccumulator / FLOOR_HEIGHT)

默认 FLOOR_HEIGHT=3.0m,即每爬升 3m 一个楼层。

数据持久化

  • RTC 内存 (RTC_DATA_ATTR):ESP32 深度睡眠保留区域,断电不丢但复位会清;
  • SPIFFS:Flash 文件系统 /sessions.dat,永久保存,重启自动加载;
  • 双备份策略:运行时优先写 RTC(速度快),空闲时镜像到 SPIFFS。

5.2 工作模式配置

传感器参数(config.h

默认值 作用
BMP_ODR 50 输出数据率 Hz
BMP_OSR 8 过采样倍数
BMP_IIR_FILTER 4 IIR 滤波系数
BMP_I2C_ADDR 0x76 I²C 地址

滤波器参数

默认值 作用
KALMAN_Q_ALT 0.1 海拔过程噪声(越大跟随越快,越抖)
KALMAN_Q_VEL 0.5 速度过程噪声
KALMAN_R_ALT 0.25 测量噪声(越大越平滑,越滞后)

运动检测

默认值 作用
MIN_SPEED_THRESHOLD 0.1 最小有效速度 m/s(过滤静止时的轻微波动)
FLOOR_HEIGHT 3.0 楼层高度(用于楼层计数)
SEA_LEVEL_PRESSURE 1013.25 海平面基准气压 hPa

系统节拍

默认值 作用
UPDATE_RATE_HZ 10 传感器采样率
DISPLAY_REFRESH_MS 200 显示刷新间隔
BUTTON_DEBOUNCE_MS 50 按键消抖
LONG_PRESS_MS 800 长按判定
EXTRA_LONG_PRESS_MS 3000 超长按判定

WiFi

默认值 作用
ENABLE_WIFI 1 是否启用 WiFi(0=禁用)
WIFI_AP_SSID “MotionTracker” AP 名称
WIFI_AP_PASSWORD “12345678” AP 密码(最少 8 位)

调参建议

  • 海拔读数抖动大:增大 KALMAN_R_ALT(0.25→0.5)或减小 KALMAN_Q_ALT
  • 响应滞后明显:减小 KALMAN_R_ALT 或增大 KALMAN_Q_ALT
  • 实际海拔偏差:在设置屏短按调整海平面气压到当地实际值(气象网站查询);
  • 楼层计数不准:调整 FLOOR_HEIGHT 匹配实际楼层高度。

六、常见问题指引

Q1:串口输出 [BMP388] 传感器未找到!

排查

  1. 检查 SDA=21、SCL=22 接线;
  2. 用 I²C 扫描程序确认地址(默认 0x76,SDO 拉高时 0x77);
  3. 在 config.h 修改 BMP_I2C_ADDR 为实际地址;
  4. 确认 3V3 供电正常,I²C 上拉电阻存在(主控板通常已集成)。

Q2:WiFi 热点连不上或打不开192.168.4.1

排查

  1. 串口查看 [WiFi] 热点: MotionTracker 是否输出;
  2. 密码必须 ≥ 8 位(WPA2 要求),当前 12345678 满足;
  3. 确认 config.h 中 ENABLE_WIFI=1
  4. 2.4G WiFi 频段,5G 手机需切换到兼容模式。
  5. 确认手机已获取 IP(应为 192.168.4.x);
  6. 部分手机会检测到无网络后切回移动数据,需关闭移动数据再访问;
  7. 浏览器试试 http://192.168.4.1(必须带 http:// 前缀)。

Q3:重启后历史数据丢失

原因

  • 冷启动(断电)会清空 RTC 内存;
  • SPIFFS 应保留。
    排查
  1. 串口查看 [Logger] SPIFFS加载 N 条
  2. 若显示 0 条:检查 SPIFFS.begin(true) 是否成功(可能需先格式化);
  3. 可在设置屏短按 “Clear History” → “[Execute]” 主动清除后再测试。

Q4:海拔数字明显偏高/偏低

原因:未校准海平面气压。
解决

  1. 查询当地实时海平面气压(气象网站/天气 App);
  2. 进入 设置屏 → 短按切换到 Sea Level 项 → 短按调整 ±0.5 hPa;
  3. 长度按确认后实时屏读数会立即更新。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐