Skip to content

Phase 0: 证明核心价值

状态: ✅ 已完成 时间: 2025-12-26 目标: 用跳跃数据证明 EMG 能回答"为什么"

为什么从这里开始?

在投入大量时间写完整系统之前,我们要先回答最关键的问题:

💡 EMG 真的能帮我们找到动作不好的根本原因吗?

如果答案是"不能",那整个项目就没意义了!

所以 Phase 0 的目标很简单:

  1. 找一个"坏"的跳跃案例
  2. 用 EMG 分析为什么它坏
  3. 对比竞品只能说"WHAT",我们能说"WHY"

第一步: 找最差的跳跃

怎么找?

数据集有 15 个 .mat 文件,包含各种动作(跳跃、行走、跑步、弓步、深蹲等)。

我们写了一个脚本 find_worst_jump.py 来评估每个动作的"糟糕程度":

评分标准:

python
badness_score = (1.0 / jump_height) + (100 if 运动链错误 else 0)

换句话说:

  • 跳得越低 → 分数越高
  • 运动链顺序错误 → 直接加 100 分

结果

🏆 冠军(最差案例): Subj04_lunge.mat

📊 基本数据:
- 动作类型: 弓步 (lunge)
- 垂直位移: 0.019 米 (非常低!)
- Badness Score: 152.20

💪 EMG 肌肉激活时序:
- 腿部: 0.879 秒
- 核心: 0.188 秒  ⚠️ 太早了!
- 手臂: 1.438 秒

❌ 问题: 核心肌肉比腿部提前 692 毫秒激活

完美!这是一个典型的倒序运动链案例。

第二步: 分析对比

我们创建了 p0_causality_demo.py 来展示:

左侧: 竞品能看到什么

竞品只有 Vision + IMU:

📹 Vision 说:
"你的垂直位移只有 0.107 米"

📱 IMU 说:
"你的峰值角速度是 200 deg/s"

💬 竞品反馈:
"你的动作幅度太小。尝试更有爆发力地移动。"

右侧: Movement Chain AI 能看到什么

我们有 Vision + IMU + EMG:

📹 Vision 说: (同样的)
"你的垂直位移只有 0.107 米"

📱 IMU 说: (同样的)
"你的峰值角速度是 200 deg/s"

💪 EMG 说: (独有!)
"核心肌肉在 0.188s 激活
 腿部肌肉在 0.879s 激活
 → 核心比腿部早 692ms!"

🔍 Movement Chain AI 反馈:
"你的动作幅度太小,是因为运动链顺序错了!

正确顺序应该是: 腿 → 核心 → 手臂
你的顺序是: 核心 → 腿 → 手臂

这导致:
1. 力量传递效率降低
2. 增加受伤风险
3. 限制最大表现

🎯 可操作建议:
专注于从腿部发起动作,然后让核心稳定,
最后用手臂。慢慢练习这个顺序,建立正确
的神经模式。"

可视化结果

P0 Causality Demo

这张图清楚地展示了:

左侧 (竞品):

  • 📊 能看到结果(位移、速度)
  • ❌ 不知道原因

右侧 (Movement Chain AI):

  • 📊 能看到结果(同样的位移、速度)
  • 能看到原因(肌肉激活时序错误)
  • 🎯 能给出可操作的建议

关键代码

肌肉激活检测

python
def detect_muscle_onset(emg_signal, emg_rate, threshold=0.1):
    """检测肌肉激活开始时刻"""
    # 1. 整流 (取绝对值)
    emg_rect = np.abs(emg_signal)

    # 2. 计算 RMS 包络 (50ms 滑动窗口)
    window_size = int(emg_rate * 0.05)
    rms = np.sqrt(np.convolve(emg_rect**2,
                               np.ones(window_size)/window_size,
                               mode='same'))

    # 3. 归一化到 0-1
    rms_norm = rms / np.max(rms)

    # 4. 找到第一次超过阈值的点
    crossings = np.where(rms_norm > threshold)[0]
    onset_frame = crossings[0] if len(crossings) > 0 else None

    # 5. 转换为时间
    onset_time = onset_frame / emg_rate if onset_frame else None

    return onset_time

运动链分析

python
# 腿部肌肉 (通道 0-2)
leg_onset = np.mean([
    detect_muscle_onset(emg[:, 0]),
    detect_muscle_onset(emg[:, 1]),
    detect_muscle_onset(emg[:, 2])
])

# 核心肌肉 (通道 3-5)
core_onset = np.mean([
    detect_muscle_onset(emg[:, 3]),
    detect_muscle_onset(emg[:, 4]),
    detect_muscle_onset(emg[:, 5])
])

# 手臂肌肉 (通道 6-8)
arm_onset = np.mean([
    detect_muscle_onset(emg[:, 6]),
    detect_muscle_onset(emg[:, 7]),
    detect_muscle_onset(emg[:, 8])
])

# 检查顺序
if core_onset < leg_onset:
    print("❌ 倒序运动链!")
    print(f"核心比腿部早 {(leg_onset - core_onset)*1000:.0f}ms")

技术细节

为什么用 RMS 包络?

原始 EMG 信号非常嘈杂:

原始信号: ∿∿∿∿∿∿∿∿∿∿∿∿∿∿  (高频噪声)

RMS 包络平滑信号:

RMS 包络: ___/‾‾‾‾‾\___  (清晰的激活曲线)

为什么阈值是 0.1?

0.1 表示"当信号达到最大值的 10% 时认为肌肉开始激活"。

这是运动科学中的标准做法:

  • 太低(如 0.05): 会把噪声当作激活
  • 太高(如 0.3): 会错过真正的激活开始

为什么要归一化?

每个人的 EMG 信号幅度不同:

  • 肌肉量大的人: 信号强
  • 肌肉量小的人: 信号弱

归一化让我们能比较不同人的激活时序。

结论

Phase 0 成功!

我们证明了:

  1. EMG 能检测肌肉激活时序
  2. 能识别倒序运动链问题
  3. 能解释"为什么"动作不好
  4. 能给出可操作的改进建议

这验证了 Movement Chain AI 的核心价值主张!

下一步

Phase 0 证明了"EMG 有用",现在我们可以放心投入时间构建完整系统:


代码文件

所有代码在:

  • scripts/find_worst_jump.py
  • scripts/p0_causality_demo.py
  • results/p0_causality_demo.png