第四章 步态特征分析:人体行走的加速度特征

好,咱们进入正题。

上一章我们聊了怎么采集加速度数据。但原始数据就像一堆乱码,你得从中找出规律。说白了,步态特征分析就是给行走过程做一次「解剖」。

我个人习惯把这一步叫做「读懂身体的节拍器」。你想想看,人走路时,手臂摆动、脚掌落地、身体起伏,这些动作都会在加速度波形上留下痕迹。我们要做的,就是把这些痕迹翻译成机器能理解的参数。

4.1 人体行走的加速度特征

先看一个最直观的现象:你把手机握在手里走路,屏幕上的加速度波形是什么样的?

我当年第一次做这个实验时,盯着波形图看了半天,发现它其实很有规律——

  • 垂直方向(Z轴):波形呈周期性波动。脚离地时加速度减小,脚落地时加速度增大。峰值通常出现在脚跟着地瞬间。
  • 前进方向(X轴):波形相对平缓,但也能看出周期性。身体前倾时加速,后仰时减速。
  • 侧向方向(Y轴):波形幅度最小,主要反映身体左右摇摆。

嗯,这里要注意:不同佩戴位置,波形特征差异很大。手腕、腰部、口袋,各有各的脾气。

核心结论:垂直方向(Z轴)的加速度信号,是步态分析中最可靠的信息源。它的信噪比最高,周期性最明显。

为什么会这样?因为人体行走时,垂直方向的运动幅度最大,而且受干扰最小。你手臂摆动可能被其他动作干扰,但身体重心的上下起伏,基本只跟走路有关。

4.2 步态周期分析

步态周期,就是一次完整的迈步过程。从左脚跟着地,到左脚跟再次着地,这就是一个周期。

我习惯把周期拆成两个阶段:

  1. 支撑相:脚接触地面的阶段,约占周期的60%
  2. 摆动相:脚离开地面的阶段,约占周期的40%

你可能会问:知道这个有什么用?

用处大了。比如你要判断一个人是不是在正常走路,还是拖着脚走。支撑相过长,说明步态异常。我在做老年人跌倒检测项目时,就靠这个特征区分正常行走和蹒跚步态。

从信号上看,一个步态周期对应加速度波形的一个完整波峰-波谷-波峰序列。具体来说:

步态事件 加速度特征 典型幅值(g)
脚跟着地 Z轴出现尖锐波峰 1.2 - 1.8
支撑中期 Z轴回落至1g附近 0.8 - 1.1
脚趾离地 Z轴出现波谷 0.6 - 0.9
摆动中期 Z轴再次上升 1.0 - 1.3

我曾经踩过一个坑:直接用原始数据找波峰。结果发现,有些人走路轻,有些人走路重,波峰高度差异很大。后来我改用归一化后的数据,再结合时间窗口约束,准确率才上来。

避坑指南:我曾经在步态周期分割上栽过跟头。如果你直接用固定阈值找波峰,遇到慢走或快走时,很容易漏检或误检。建议用自适应阈值,或者结合过零检测法。

4.3 特征参数提取

好了,现在我们知道步态周期长什么样了。接下来要做的,就是从每个周期里提取出有意义的参数。

我个人把特征参数分成三类:

4.3.1 时域特征

这类特征直接从时间轴上提取,计算简单,物理意义明确。

  • 步频:每分钟的步数。正常成年人约100-120步/分钟。
  • 步长:一步的距离。可以通过步频和速度推算。
  • 步态周期时间:一个完整周期的时间,约0.5-0.7秒。
  • 支撑相占比:支撑相时间/周期时间。正常约60%。

代码实现也很直接:

def extract_time_features(acc_z, sample_rate):
    """
    提取时域特征
    acc_z: 垂直方向加速度数据
    sample_rate: 采样率(Hz)
    """
    # 找波峰(步态事件)
    peaks, _ = find_peaks(acc_z, distance=sample_rate*0.3)
    
    # 步频
    step_count = len(peaks)
    duration = len(acc_z) / sample_rate
    cadence = step_count / duration * 60  # 步/分钟
    
    # 步态周期时间
    if len(peaks) > 1:
        cycle_times = np.diff(peaks) / sample_rate
        avg_cycle_time = np.mean(cycle_times)
    else:
        avg_cycle_time = 0
    
    return {
        'cadence': cadence,
        'avg_cycle_time': avg_cycle_time,
        'step_count': step_count
    }

4.3.2 频域特征

时域特征够用了?不一定。有些步态信息藏在频率里。

比如,正常行走的步频集中在1.5-2.5Hz。如果你在频谱上看到3Hz以上的能量很高,那可能不是走路,是跑步或者抖动。

常用的频域特征:

  • 主频:频谱中能量最大的频率,对应步频。
  • 频谱能量:特定频段内的能量总和。
  • 频谱熵:反映步态的规律性。规律行走时熵值低,混乱步态时熵值高。

实战经验:我在做运动模式识别时,发现频域特征对区分走路和跑步特别有效。走路的主频在2Hz左右,跑步能到3Hz以上。这个差异在时域里反而不明显。

4.3.3 统计特征

这类特征不依赖周期分割,直接从一段数据里算统计量。

  • 均值:反映整体趋势。走路时Z轴均值接近1g。
  • 标准差:反映波动幅度。走路时约0.2-0.4g。
  • 偏度:反映波形对称性。正常步态偏度接近0。
  • 峰度:反映波形的尖锐程度。脚跟着地冲击大时,峰度值高。

嗯,这里要注意:统计特征虽然简单,但容易受噪声影响。我建议先做滤波,再算统计量。

def extract_stat_features(acc_z):
    """
    提取统计特征
    """
    features = {
        'mean': np.mean(acc_z),
        'std': np.std(acc_z),
        'skewness': skew(acc_z),
        'kurtosis': kurtosis(acc_z),
        'max': np.max(acc_z),
        'min': np.min(acc_z),
        'range': np.max(acc_z) - np.min(acc_z)
    }
    return features

4.4 特征选择与优化

特征不是越多越好。我见过有人一口气提取了50多个特征,结果模型反而过拟合了。

我的建议是:

  1. 先选时域特征:步频、周期时间、支撑相占比,这三个基本够用。
  2. 再补频域特征:如果时域特征区分度不够,加主频和频谱能量。
  3. 最后考虑统计特征:作为辅助,不要喧宾夺主。

重要提醒:特征提取前,一定要做数据预处理。我曾经因为忘记去除重力分量,导致所有特征都偏大。后来花了整整两天排查问题,才发现是预处理环节漏了。

好了,这一章的内容就到这里。步态特征分析是计步算法的核心环节,你掌握了这些特征,就等于拿到了打开步数计算大门的钥匙。下一章,我们会把这些特征用起来,真正实现步数计数。