一、前言
在手机 IMU 的 PDR 算法中,步伐检测并不是直接拿原始加速度数据来判断的。
手机加速度计采集到的数据通常非常"脏",里面既有真实走路带来的周期性波动,也有手部抖动、传感器噪声、手机姿态变化、瞬时冲击等干扰。
如果直接用原始加速度去做波峰、波谷检测,很容易出现误计步。
所以,在当前 PDR 算法中,会先对加速度信号做一层预处理,其中非常关键的一步就是:
四阶巴特沃斯低通滤波
它的作用不是直接算步长,也不是直接算位置,而是把原始加速度曲线变得更加平滑,让后面的峰谷检测更稳定。
本文就围绕这个问题展开:
当前 PDR 算法里的四阶巴特沃斯低通滤波到底是怎么做的?
它为什么要放在峰谷检测之前?
它在整个 PDR 流程中到底起什么作用?
二、 PDR 算法中滤波的位置
在PDR 算法中,加速度处理的大致流程是:
手机 IMU 原始数据
ax, ay, az
↓
计算三轴加速度模长
sqrt(ax² + ay² + az²)
↓
减去重力 g
acc_norm = sqrt(ax² + ay² + az²) - g
↓
四阶巴特沃斯低通滤波
fil_Acc
↓
峰谷检测
peak / valley
↓
步伐质量门控
StepEvent
↓
步长估计
step.length
↓
结合航向 yaw_nav
↓
输出 PDR 位移增量 dx / dy
可以看到,四阶巴特沃斯低通滤波处在:
加速度模长减重力之后
峰谷检测之前
也就是说,它处理的不是三轴原始加速度,而是已经变成一维的加速度波动信号:

滤波之后得到:

后续算法真正用来找波峰、波谷的,就是这个滤波后的:

三、为什么不能直接用原始加速度做步伐检测?
手机加速度计输出的是三个方向的数据:

但是用户拿手机的姿态不固定。
比如:
- 手机可能竖着拿
- 可能横着拿
- 可能斜着拿
- 也可能边走边转手机
如果直接看某一个轴,比如只看 aza_zaz,那么手机姿态一变,数据就会明显变化。
所以当前算法先计算三轴模长:

这样不管手机横着、竖着、斜着,三轴合成后的总强度相对更稳定。
但是手机静止时,加速度模长也不是 0,而是接近重力加速度:

所以还要减去重力:

这样静止时信号会接近 0,走路时信号会围绕 0 上下波动。
四、减去重力后,为什么还要滤波?
减去重力之后,得到的是一条加速度波动曲线。
理想情况下,用户走路时,这条曲线应该是比较规律的波浪形:
上升 → 到达波峰 → 下降 → 到达波谷 → 再次上升
但是实际手机采集的数据不会这么干净。
它可能包含:
| 干扰来源 | 表现 |
|---|---|
| 传感器噪声 | 小幅随机抖动 |
| 手部轻微晃动 | 额外的小波峰、小波谷 |
| 手机瞬间甩动 | 突然出现尖峰 |
| 姿态变化 | 整体曲线发生偏移 |
| 采样误差 | 相邻点之间不够平滑 |
如果不滤波,曲线可能会变成这样:
上升、下降、上升、下降、上升、下降......
算法会误以为出现了很多波峰和波谷。
而 PDR 的步伐检测正是依赖波峰波谷的。
所以必须先滤波,让曲线更像真实走路波形,而不是一堆杂乱的小抖动。
五、什么是低通滤波?
低通滤波可以简单理解为:
低频信号通过,高频信号被削弱。
在人走路这个场景里,正常步态变化通常是比较慢的、有节奏的。
比如正常人走路,大概每秒 1 到 2 步左右,对应的信号频率通常集中在较低频率范围内。
而传感器噪声、手抖、小毛刺,往往变化很快,属于高频成分。
所以低通滤波的目的就是:
保留:正常走路带来的低频周期波动
削弱:高频噪声、手部小抖动、瞬时尖峰
可以把低通滤波理解成一个筛子:
| 信号类型 | 是否保留 |
|---|---|
| 正常走路的周期性波动 | 尽量保留 |
| 高频小抖动 | 削弱 |
| 瞬时尖峰 | 削弱 |
| 传感器毛刺 | 削弱 |
六、为什么截止频率设置为 2 Hz?
当前算法中,采样频率大约是:

也就是说,手机每秒采集大约 100 个 IMU 数据点。
低通滤波的截止频率大约是:

这个参数的意思是:
2 Hz 以下的信号更可能是正常走路节奏,需要保留;
2 Hz 以上的快速变化,更可能是噪声或者抖动,需要压制。
人正常走路不会像机器震动一样一秒高频抖几十次。
所以对于步伐检测来说,2 Hz 左右的截止频率是一个偏稳的设置。
数字滤波器中还会用到归一化截止频率:

其中 s/2 是奈奎斯特频率。
代入当前参数:

也就是说,在数字滤波器设计中,当前低通截止点大约是:
0.04
这个数值比较低,说明算法主要想保留慢速步态变化,把快速抖动压掉。
七、什么是巴特沃斯滤波器?
巴特沃斯滤波器是一种经典滤波器。
它最重要的特点是:
通带内非常平滑。
所谓通带,就是允许通过的频率范围。
对于当前 PDR 来说,低频步态信号就是希望保留下来的部分。
巴特沃斯滤波器不会在通带内产生明显波纹,也就是说,它不会一会儿把某些低频放大,一会儿又把另一些低频压低。
它的风格是:
平滑、稳定、不过分改变低频信号形状
这对于步伐检测很重要。
因为我们希望滤波之后,走路波形仍然保持比较自然的峰谷结构,而不是被滤波器扭曲得太厉害。
八、为什么不用简单平均滤波?
有人可能会问:
直接用滑动平均不行吗?为什么要用巴特沃斯?
滑动平均当然也能让曲线变平滑,但它比较粗糙。
比如滑动平均可能带来:
- 峰值被明显压低
- 波峰位置发生延迟
- 对不同频率的抑制不够精细
- 窗口大小不好选
而巴特沃斯低通滤波器是从频率角度设计的。
它可以更明确地表达:
哪些频率要保留,哪些频率要削弱。
所以在 PDR 步态检测中,巴特沃斯低通比简单平均更适合做稳定的信号预处理。
九、四阶是什么意思?
"四阶"可以理解为滤波器的强度等级。
阶数越高,对高频信号的压制越强。
简单理解:
| 阶数 | 滤波特点 |
|---|---|
| 一阶 | 比较温和 |
| 二阶 | 滤波效果明显 |
| 四阶 | 对高频噪声压制更强 |
| 八阶 | 更强,但可能带来更大延迟和稳定性问题 |
当前算法使用的是四阶巴特沃斯低通滤波,说明它不是轻轻平滑一下,而是比较认真地压制高频噪声。
对于 PDR 来说,这是合理的。
因为步伐检测对波峰波谷比较敏感,如果高频抖动太多,很容易误判。
十、四阶滤波器通常怎么实现?
工程上,四阶巴特沃斯低通滤波器通常有两种实现方式。
一种是直接使用四阶差分公式。
另一种更常见、更稳定的方式是:
两个二阶滤波器串联。
也可以理解成:
acc_norm
↓
二阶巴特沃斯滤波器 1
↓
中间输出 stage1
↓
二阶巴特沃斯滤波器 2
↓
最终输出 fil_Acc
写成公式就是:

两个二阶滤波器串起来,就形成了四阶滤波效果。
也就是说:
二阶滤一遍,曲线已经变平滑;
再经过第二个二阶滤波器,噪声进一步被压制;
最终得到四阶低通滤波后的步态信号。
十一、二阶滤波器内部到底怎么算?
一个典型的二阶 IIR 滤波器公式是:

这个公式看起来复杂,但它的意思很简单:
当前输出不只由当前输入决定,还要参考之前的输入和之前的输出。
其中:
| 符号 | 含义 |
|---|---|
| xn | 当前输入 |
| xn−1 | 上一次输入 |
| xn−2 | 上上次输入 |
| yn | 当前输出 |
| yn−1 | 上一次滤波输出 |
| yn−2 | 上上次滤波输出 |
| a1,a2,b0,b1,b2a_1,a_2,b_0,b_1,b_2a1,a2,b0,b1,b2 | 滤波器系数 |
这说明它是一个带记忆的滤波器。
它不是只看当前这一个采样点,而是结合历史数据一起计算。
这也是它能把曲线变平滑的原因。
十二、滤波器为什么需要历史状态?
假设当前输入突然出现一个尖峰。
如果只看当前点,算法可能会认为这就是一个真实波峰。
但是滤波器会结合前面的输入和输出:

如果前面一直很平稳,当前突然跳很高,滤波器不会完全相信这个尖峰。
它会把这个尖峰压低一些,让输出变化更平滑。
所以滤波器本质上是在做一件事:
不要被某一个瞬间采样点带偏,要结合历史趋势判断当前输出。
这对于手机 IMU 特别重要,因为手机数据经常会突然抖一下。
十三、用伪代码理解当前滤波流程
当前算法的整体处理流程可以写成伪代码:
# 1. 输入手机 IMU 三轴加速度
ax = imu.ax
ay = imu.ay
az = imu.az
# 2. 计算三轴加速度模长
acc_mag = sqrt(ax * ax + ay * ay + az * az)
# 3. 减去重力,得到加速度波动信号
acc_norm = acc_mag - gravity
# 4. 输入四阶巴特沃斯低通滤波器
fil_Acc = butterworth_filter.update(acc_norm)
# 5. 使用滤波后的加速度进行峰谷检测
detect_peak_valley(fil_Acc)
如果四阶滤波器用两个二阶滤波器串联,可以写成:
# 计算加速度模长并减重力
acc_norm = sqrt(ax * ax + ay * ay + az * az) - gravity
# 第一级二阶滤波
stage1 = biquad1.update(acc_norm)
# 第二级二阶滤波
fil_Acc = biquad2.update(stage1)
每个二阶滤波器内部大概是:
y = b0 * x + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2
# 更新历史输入
x2 = x1
x1 = x
# 更新历史输出
y2 = y1
y1 = y
return y
其中:
x :当前输入
x1 :上一次输入
x2 :上上次输入
y :当前输出
y1 :上一次输出
y2 :上上次输出
这就是所谓的实时滤波。
每来一个新的 IMU 采样点,滤波器就更新一次。
十四、滤波前后数据有什么区别?
假设减去重力后的加速度数据是:
0.02, 0.15, 0.70, 0.30, 1.80, -0.20, -0.65, -0.10, 0.35
其中 1.80 可能是手机突然晃动产生的尖峰。
如果直接拿这条曲线做峰谷检测,算法很可能把 1.80 当成一个强波峰。
经过低通滤波后,曲线可能变成类似:
0.01, 0.08, 0.25, 0.36, 0.42, 0.18, -0.10, -0.28, -0.12
可以看到,尖峰没有完全消失,但被明显压平了。
这样后面的峰谷检测就不会被瞬时毛刺轻易欺骗。
十五、滤波对峰谷检测有什么帮助?
当前 PDR 的步伐检测主要依赖波峰和波谷。
大致判断逻辑是:
曲线从上升变下降 → 可能是波峰
曲线从下降变上升 → 可能是波谷
波峰超过正阈值 → 记录波峰
波谷低于负阈值 → 记录波谷
波峰波谷配对 → 形成候选步
候选步通过质量门控 → 变成有效步
如果没有滤波,曲线可能因为高频抖动出现大量局部峰谷。
这会导致:
- 误检测波峰
- 误检测波谷
- 候选步过多
- 静止时误计步
- 轻微晃手机时轨迹漂移
滤波之后,曲线变得更加接近真实步态波形:
缓慢上升 → 明显波峰 → 缓慢下降 → 明显波谷 → 再次上升
这样峰谷检测才更稳定。
十六、滤波不是最终判断
需要注意的是,滤波只是信号预处理,不是最终的步伐判断。
也就是说:
经过滤波之后,不代表每个波峰波谷都一定是真实一步。
滤波之后还要经过:
- 峰值阈值判断
- 谷值阈值判断
- 峰谷时间间隔检查
- 峰谷幅值差检查
- 相邻步最小间隔检查
- 连续步步长突变检查
所以当前算法不是简单地:
有波峰 = 有一步
而是:
滤波后的曲线出现明显峰谷
↓
峰谷满足阈值要求
↓
峰谷时间合理
↓
峰谷幅值差合理
↓
相邻步间隔合理
↓
步长没有异常突变
↓
最终确认有效步
四阶巴特沃斯低通滤波只是把第一步做好,让后面的判断更可靠。
十七、为什么滤波不能太强?
滤波不是越强越好。
如果滤波太弱,高频噪声压不住,容易误计步。
如果滤波太强,真实步态也会被压掉,容易漏计步。
滤波太强可能带来几个问题:
| 问题 | 说明 |
|---|---|
| 峰值变低 | 原本明显的步态波峰被压小 |
| 波形延迟 | 滤波器输出比真实动作慢一点 |
| 快速步态被削弱 | 用户走得快时,部分步态信息可能被压掉 |
| 起步响应慢 | 刚开始走时,滤波器状态需要时间跟上 |
所以当前算法使用四阶巴特沃斯低通,是一种折中。
它的目标是:
尽量压制高频噪声
但不要破坏正常走路的主要波形
十八、为什么四阶滤波会带来延迟?
因为滤波器使用了历史输入和历史输出。
当前输出: yn 不仅和当前输入: xn有关,还和之前的输入输出有关:
xn−1,xn−2,yn−1,yn−2
这会让滤波结果更加平滑,但也会让信号变化稍微滞后。
在 PDR 中,这种延迟一般是可以接受的。
因为步伐检测本身不要求毫秒级瞬时反应,更重要的是不要误计步。
十九、四阶巴特沃斯滤波在当前算法中的核心价值
在当前 PDR 中,四阶巴特沃斯低通滤波的价值可以总结为四点。
第一,减少传感器噪声。
手机 IMU 数据天然存在小幅随机波动,滤波可以让曲线更稳定。
第二,压制手部小抖动。
用户拿手机时,手部轻微晃动会产生很多小波峰、小波谷,滤波可以削弱它们。
第三,降低瞬时冲击影响。
手机突然甩一下、碰一下,可能产生尖峰。低通滤波可以把这种尖峰压平。
第四,提高峰谷检测稳定性。
滤波后的曲线更接近真实走路波形,后面的峰谷检测、步伐门控和步长估计都会更可靠。
二十、它和步长估计有什么关系?
当前算法的步长估计依赖峰谷差。
也就是:

再使用 Weinberg 类模型估计步长:

如果滤波前曲线噪声很大,峰值和谷值可能不稳定。
这样会导致:
- 峰谷差偏大
- 步长估计偏大
- 轨迹出现跳点
或者:
- 峰谷差被噪声扰乱
- 步长忽大忽小
- 轨迹不平滑
所以滤波不仅影响"有没有检测到一步",还会间接影响"这一步估计多长"。