4. 数据采集方法
TinyML手势识别项目的成败,首先取决于数据质量。模型再精巧、训练再充分,如果采集到的IMU数据本身存在混叠、漂移或信息缺失,最终识别准确率也会大打折扣。本章围绕“如何用Arduino Nano 33 BLE Sense开发板正确采集加速度计与陀螺仪数据”展开,重点解决三个核心问题:采样频率如何设定、采样时长如何估算、以及如何在资源受限的MCU上稳定地完成数据记录。我们将从IMU传感器的工作原理出发,逐步深入到寄存器配置、代码实现与数据校验,最终形成一个可复用的采集流程。
图4-1展示了从开发板硬件到数据输出的完整链路。Arduino Nano 33 BLE Sense板载的LSM9DS1传感器通过I2C总线与主控nRF52840通信,主控读取原始寄存器数据后,经过时间戳标记和格式化,最终通过USB串口发送到PC端保存。整个链路中,采样频率的设定决定了数据的时间分辨率,而采样时长则决定了数据集的覆盖范围。下面我们逐一深入。
4.1 核心原理:IMU采样频率与手势频带的关系
手势识别本质上是对人体运动信号进行分类。人体自然手势的频带范围通常在0.5Hz到10Hz之间,例如挥手约1-2Hz,快速敲击约5-8Hz,而一些突发性动作(如甩腕)可能包含高达15Hz的谐波成分。根据奈奎斯特采样定理,采样频率必须至少为信号最高频率的两倍,才能无失真地重建信号。因此,理论上30Hz的采样率即可覆盖大多数手势。但在实际工程中,我们还需要考虑以下因素:
- 抗混叠滤波:LSM9DS1内部没有硬件抗混叠滤波器,如果采样率过低,高频噪声会折叠到低频段,污染手势特征。建议采样率至少为手势最高频率的5-10倍。
- 特征提取需求:TinyML模型通常使用滑动窗口提取统计特征(均值、方差、峰值等),较高的采样率能提供更平滑的波形,减少特征估计的偏差。
- MCU处理能力:nRF52840主频64MHz,I2C读取一次6轴数据约需1-2ms。若采样率超过500Hz,主控将忙于读取数据而无法执行其他任务。
综合以上因素,对于手势识别项目,推荐采样频率为50Hz-100Hz。50Hz足以捕捉10Hz以内的手势主频,100Hz则能提供更丰富的细节,同时留给MCU充足的处理余量。若手势包含快速冲击成分(如拍手、弹指),可考虑200Hz,但需注意数据量成倍增长。
| 手势类型 | 典型频带 | 推荐采样率 | 单次采样时长 |
|---|---|---|---|
| 慢速挥手(打招呼) | 0.5-2Hz | 20-50Hz | 2-3秒 |
| 常规手势(滑动、旋转) | 1-5Hz | 50-100Hz | 1-2秒 |
| 快速手势(敲击、甩动) | 3-10Hz | 100-200Hz | 0.5-1秒 |
| 复合手势(多段动作) | 0.5-8Hz | 100Hz | 2-4秒 |
采样时长的设定则取决于手势的持续时间和数据增强需求。每个手势样本应包含完整的动作周期,并预留前后各0.2-0.3秒的静止段,以便模型学习“静止→运动→静止”的过渡模式。例如,一个持续1秒的滑动手势,建议采集1.5秒的数据窗口。
4.2 操作流程:从寄存器配置到串口输出
在Arduino环境中,我们使用Arduino_LSM9DS1库来简化IMU的配置。该库封装了底层寄存器操作,但理解其背后的配置逻辑仍然重要。以下是完整的采集流程:
- 初始化I2C与传感器:调用
IMU.begin(),该函数会检测传感器并设置默认量程(加速度±4g,陀螺仪±245dps)。 - 配置量程与ODR:通过
IMU.setAccelRange()和IMU.setGyroRange()调整量程。对于手势识别,加速度建议设为±4g或±8g(防止剧烈动作削波),陀螺仪设为±500dps或±2000dps。ODR则通过IMU.setAccelODR()和IMU.setGyroODR()设置,可选值包括10Hz、50Hz、100Hz、200Hz等。 - 校准传感器:在采集前,让开发板静止水平放置2秒,计算加速度计和陀螺仪的零偏值,并在后续读数中减去该偏移。
- 循环读取数据:在
loop()中,使用IMU.readAcceleration()和IMU.readGyroscope()读取6轴数据,同时用micros()记录时间戳。 - 格式化输出:将数据拼接为CSV格式字符串,通过
Serial.println()发送。波特率设为115200,确保传输速率高于数据产生速率。
#include <Arduino_LSM9DS1.h>
const float accelBias[3] = {0.02, -0.01, 0.03}; // 示例校准值
const float gyroBias[3] = {0.5, -0.3, 0.2}; // 示例校准值
void setup() {
Serial.begin(115200);
while (!Serial);
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");
while (1);
}
// 配置量程与ODR
IMU.setAccelRange(IMU_ACCEL_RANGE_4G);
IMU.setGyroRange(IMU_GYRO_RANGE_500DPS);
IMU.setAccelODR(100); // 100Hz
IMU.setGyroODR(100); // 100Hz
Serial.println("t,ax,ay,az,gx,gy,gz");
}
void loop() {
float ax, ay, az, gx, gy, gz;
if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable()) {
unsigned long t = micros();
IMU.readAcceleration(ax, ay, az);
IMU.readGyroscope(gx, gy, gz);
// 减去零偏
ax -= accelBias[0]; ay -= accelBias[1]; az -= accelBias[2];
gx -= gyroBias[0]; gy -= gyroBias[1]; gz -= gyroBias[2];
// 输出CSV行
Serial.print(t);
Serial.print(",");
Serial.print(ax, 4); Serial.print(",");
Serial.print(ay, 4); Serial.print(",");
Serial.print(az, 4); Serial.print(",");
Serial.print(gx, 4); Serial.print(",");
Serial.print(gy, 4); Serial.print(",");
Serial.println(gz, 4);
}
}
上述代码中,accelerationAvailable()和gyroscopeAvailable()用于检查FIFO中是否有新数据。当ODR设置为100Hz时,每10ms会有一个新样本,MCU有充足时间完成读取和输出。注意,micros()返回的是微秒级时间戳,在100Hz采样下,相邻时间戳间隔约为10000微秒,可用于后续数据同步和插值。
4.3 案例拆解:采集“挥拳”手势的完整过程
假设我们需要为“挥拳”手势(快速向前出拳并收回)采集训练数据。该手势持续约0.8秒,包含明显的加速度冲击(峰值可达±6g)和角速度旋转(峰值约300dps)。我们按照以下步骤操作:
场景设定:采集环境为室内办公桌旁,开发板通过USB线连接笔记本电脑,使用Arduino IDE的串口监视器或Python脚本保存数据。目标采集50个正样本和50个负样本(其他随机动作)。
第一步:参数选择。根据手势特征,加速度量程设为±8g(避免削波),陀螺仪量程设为±500dps(足够覆盖旋转速度)。采样频率设为100Hz,采样时长设为1.5秒(包含0.3秒前静止、0.8秒动作、0.4秒后静止)。
第二步:编写采集脚本。在PC端使用Python的pyserial库读取串口数据,当检测到用户按下键盘空格键时开始记录,持续1.5秒后自动停止并保存为CSV文件。脚本同时显示实时波形,便于观察数据质量。
import serial
import time
import csv
ser = serial.Serial('COM3', 115200, timeout=1)
input("Press Enter to start...")
start_time = time.time()
duration = 1.5 # 秒
samples = []
while time.time() - start_time < duration:
line = ser.readline().decode().strip()
if line:
samples.append(line.split(','))
with open('punch_001.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['t', 'ax', 'ay', 'az', 'gx', 'gy', 'gz'])
writer.writerows(samples)
print(f"Saved {len(samples)} samples")
第三步:执行采集与质量检查。采集过程中,观察Python打印的波形图,确认:① 静止段加速度模值接近1g(重力加速度);② 动作段加速度峰值未触及量程上限;③ 时间戳间隔均匀(约10ms±2ms)。若发现数据异常,立即停止并调整。
第四步:数据标注与存储。每个样本文件以“手势类别_编号”命名,如punch_001.csv、random_001.csv。同时记录采集时的环境信息(如佩戴位置、动作方向)到元数据文件,便于后续分析。
结果与复盘:共采集50个正样本和50个负样本,每个样本约150行数据。检查发现,有3个正样本的峰值加速度超过±8g导致削波,重新采集后解决。另有2个负样本包含明显的抖动噪声,分析原因是采集时手部未完全放松,后续在采集指导中增加了“保持自然放松”的提示。
4.4 常见误区与总结复盘
在数据采集环节,初学者常犯以下错误,需要特别注意:
误区一:采样率越高越好。过高的采样率(如500Hz)会导致数据量爆炸,且MCU可能因I2C读取耗时过长而丢失样本。实际上,100Hz已能满足绝大多数手势识别需求。若确需高频,应使用DMA或FIFO中断方式。
误区二:忽略传感器校准。未校准的加速度计可能有±0.1g的零偏,陀螺仪零偏可达±5dps。这些偏差在静止段会表现为非零值,导致模型学习到错误的“静止”模式。务必在每次采集前执行2秒静止校准。
误区三:采样时长不足。手势动作前后的过渡段(静止→运动→静止)对模型识别至关重要。若只采集动作核心段,模型将无法区分“开始动作”和“动作中”的状态。
经验建议:① 在采集脚本中加入实时数据质量监控,包括时间戳间隔、量程溢出检测、静止段方差等指标;② 每个手势类别至少采集30个样本,且在不同时间段(上午/下午)和不同佩戴位置(手腕/手背)采集,增加数据多样性;③ 保存原始数据的同时,记录采集参数(采样率、量程、校准值)到JSON配置文件,确保数据可复现。
总结本章,数据采集是TinyML手势识别的基础工程。通过合理设置采样频率(50-100Hz)和采样时长(1-2秒),配合正确的量程选择和校准流程,我们能够获得高质量的训练数据。下一阶段,我们将基于这些数据构建特征工程和模型训练流程。