feat(exercise): 优化死虫式训练姿态检测算法
- 调整视频处理频率从每帧处理改为每2帧处理 - 添加膝角趋势平滑算法减少单帧抖动误判 - 改进对角伸展检测逻辑支持准备位手臂上举 - 优化状态机确保严格回到准备姿态才计数 - 添加姿态丢失时的候选帧清理机制 - 更新音频文件生成路径至resources目录 - 改进macOS音频生成使用AIFF格式提高质量 - 添加详细的帧处理日志输出间隔配置
This commit is contained in:
@@ -49,6 +49,28 @@ class TestDeadBugRules:
|
||||
)
|
||||
assert detect_diagonal_extension(metrics) == "left_arm_right_leg"
|
||||
|
||||
def test_detect_diagonal_extension_allows_ready_arm_overlap(self):
|
||||
"""测试:准备位双臂上举时,单腿对侧伸展仍应识别"""
|
||||
metrics = DeadBugMetrics(
|
||||
left_arm_extended=True, right_arm_extended=True,
|
||||
left_leg_extended=False, right_leg_extended=True,
|
||||
left_elbow_angle=160, right_elbow_angle=160,
|
||||
left_knee_angle=90, right_knee_angle=160,
|
||||
feedback=[],
|
||||
)
|
||||
assert detect_diagonal_extension(metrics) == "left_arm_right_leg"
|
||||
|
||||
def test_detect_diagonal_extension_rejects_both_legs(self):
|
||||
"""测试:双腿同时伸展不应识别为可计数对角伸展"""
|
||||
metrics = DeadBugMetrics(
|
||||
left_arm_extended=True, right_arm_extended=True,
|
||||
left_leg_extended=True, right_leg_extended=True,
|
||||
left_elbow_angle=160, right_elbow_angle=160,
|
||||
left_knee_angle=160, right_knee_angle=160,
|
||||
feedback=[],
|
||||
)
|
||||
assert detect_diagonal_extension(metrics) is None
|
||||
|
||||
def test_is_ready_position(self):
|
||||
"""测试:膝盖弯曲且四肢收缩应识别为准备姿态"""
|
||||
metrics = DeadBugMetrics(
|
||||
@@ -60,6 +82,17 @@ class TestDeadBugRules:
|
||||
)
|
||||
assert is_ready_position(metrics)
|
||||
|
||||
def test_is_ready_allows_arms_extended(self):
|
||||
"""测试:dead bug 准备位允许双臂上举"""
|
||||
metrics = DeadBugMetrics(
|
||||
left_arm_extended=True, right_arm_extended=True,
|
||||
left_leg_extended=False, right_leg_extended=False,
|
||||
left_elbow_angle=160, right_elbow_angle=160,
|
||||
left_knee_angle=100, right_knee_angle=100,
|
||||
feedback=[],
|
||||
)
|
||||
assert is_ready_position(metrics)
|
||||
|
||||
def test_is_not_ready_legs_extended(self):
|
||||
"""测试:腿部伸展时不识别为准备姿态"""
|
||||
metrics = DeadBugMetrics(
|
||||
|
||||
@@ -26,6 +26,36 @@ class TestDeadBugStateMachine:
|
||||
feedback=[],
|
||||
)
|
||||
|
||||
def _both_legs_extended(self) -> DeadBugMetrics:
|
||||
"""构建双腿同时伸展的非标准姿态"""
|
||||
return DeadBugMetrics(
|
||||
left_arm_extended=True, right_arm_extended=True,
|
||||
left_leg_extended=True, right_leg_extended=True,
|
||||
left_elbow_angle=160, right_elbow_angle=160,
|
||||
left_knee_angle=160, right_knee_angle=160,
|
||||
feedback=[],
|
||||
)
|
||||
|
||||
def _arms_extended_ready_legs(self) -> DeadBugMetrics:
|
||||
"""构建腿已收回但手臂未收回的姿态"""
|
||||
return DeadBugMetrics(
|
||||
left_arm_extended=True, right_arm_extended=True,
|
||||
left_leg_extended=False, right_leg_extended=False,
|
||||
left_elbow_angle=160, right_elbow_angle=160,
|
||||
left_knee_angle=100, right_knee_angle=100,
|
||||
feedback=[],
|
||||
)
|
||||
|
||||
def _right_knee_angle(self, angle: float) -> DeadBugMetrics:
|
||||
"""构建右膝角连续变化但伸展布尔值尚未稳定的姿态"""
|
||||
return DeadBugMetrics(
|
||||
left_arm_extended=True, right_arm_extended=True,
|
||||
left_leg_extended=False, right_leg_extended=False,
|
||||
left_elbow_angle=160, right_elbow_angle=160,
|
||||
left_knee_angle=90, right_knee_angle=angle,
|
||||
feedback=[],
|
||||
)
|
||||
|
||||
def test_initial_state(self):
|
||||
"""测试:状态机初始化后应为READY且计数为0"""
|
||||
sm = DeadBugStateMachine()
|
||||
@@ -46,3 +76,58 @@ class TestDeadBugStateMachine:
|
||||
assert sm.phase == DeadBugPhase.READY
|
||||
sm.update(self._extended_left())
|
||||
assert sm.phase == DeadBugPhase.EXTENDING
|
||||
|
||||
def test_confirm_extension_from_knee_angle_trend(self):
|
||||
"""测试:膝角连续上升时,不依赖单帧伸展布尔值也能确认伸展"""
|
||||
sm = DeadBugStateMachine(extension_confirm_frames=2, reset_confirm_frames=2)
|
||||
|
||||
sm.update(self._right_knee_angle(100))
|
||||
sm.update(self._right_knee_angle(130))
|
||||
assert sm.phase == DeadBugPhase.READY
|
||||
sm.update(self._right_knee_angle(145))
|
||||
sm.update(self._right_knee_angle(150))
|
||||
|
||||
assert sm.phase == DeadBugPhase.EXTENDING
|
||||
|
||||
def test_full_rep_counts_once_after_strict_reset(self):
|
||||
"""测试:确认伸展后,只有严格回到准备姿态才计一次"""
|
||||
sm = DeadBugStateMachine(extension_confirm_frames=2, reset_confirm_frames=2)
|
||||
|
||||
sm.update(self._extended_left())
|
||||
sm.update(self._extended_left())
|
||||
assert sm.phase == DeadBugPhase.EXTENDING
|
||||
|
||||
sm.update(self._arms_extended_ready_legs())
|
||||
assert sm.rep_count == 0
|
||||
assert sm.phase == DeadBugPhase.NEED_RESET
|
||||
|
||||
sm.update(self._ready_metrics())
|
||||
result = sm.update(self._ready_metrics())
|
||||
assert result.rep_count == 1
|
||||
assert sm.phase == DeadBugPhase.READY
|
||||
|
||||
result = sm.update(self._ready_metrics())
|
||||
assert result.rep_count == 1
|
||||
|
||||
def test_both_legs_do_not_start_rep(self):
|
||||
"""测试:双腿同时伸展不进入计数流程"""
|
||||
sm = DeadBugStateMachine(extension_confirm_frames=2, reset_confirm_frames=2)
|
||||
sm.update(self._both_legs_extended())
|
||||
result = sm.update(self._both_legs_extended())
|
||||
|
||||
assert result.rep_count == 0
|
||||
assert sm.phase == DeadBugPhase.READY
|
||||
|
||||
def test_no_pose_preserves_confirmed_rep_until_reset(self):
|
||||
"""测试:已确认伸展后短暂丢姿态,回到准备位仍能完成计数"""
|
||||
sm = DeadBugStateMachine(extension_confirm_frames=2, reset_confirm_frames=2)
|
||||
sm.update(self._extended_left())
|
||||
sm.update(self._extended_left())
|
||||
assert sm.phase == DeadBugPhase.EXTENDING
|
||||
|
||||
sm.mark_no_pose()
|
||||
sm.update(self._ready_metrics())
|
||||
result = sm.update(self._ready_metrics())
|
||||
|
||||
assert result.rep_count == 1
|
||||
assert sm.phase == DeadBugPhase.READY
|
||||
|
||||
Reference in New Issue
Block a user