69 lines
2.0 KiB
Python
69 lines
2.0 KiB
Python
"""rate_limiter_test.py —— Clamp / SlewRateLimiter 单元测试。
|
||
|
||
移植自 reference-c++/visual_track_control/tests/rate_limiter_test.cpp。
|
||
覆盖(物理追踪思路 §8、状态机设计 S6):静态限幅、变化率限幅步进、
|
||
首条指令从零渐变、异常节拍保护、Reset 归零。
|
||
"""
|
||
|
||
import pytest
|
||
|
||
from visual_track_control.rate_limiter import SlewRateLimiter, clamp
|
||
|
||
|
||
def test_clamp_clamps_value():
|
||
assert clamp(5.0, -2.0, 2.0) == 2.0
|
||
assert clamp(-5.0, -2.0, 2.0) == -2.0
|
||
assert clamp(1.5, -2.0, 2.0) == 1.5
|
||
# 边界值:落在区间内
|
||
assert clamp(2.0, -2.0, 2.0) == 2.0
|
||
assert clamp(-2.0, -2.0, 2.0) == -2.0
|
||
|
||
|
||
def test_first_command_ramps_from_zero():
|
||
# S6:初始输出为 0,首条大指令只能按变化率渐变
|
||
lim = SlewRateLimiter(2.0)
|
||
assert lim.limit(10.0, 0.5) == 1.0 # 0 + 2×0.5
|
||
assert lim.limit(10.0, 0.5) == 2.0 # 继续渐变
|
||
assert lim.limit(10.0, 10.0) == 10.0 # 步长足够 → 到达目标
|
||
|
||
|
||
def test_tracks_target_within_step():
|
||
lim = SlewRateLimiter(10.0)
|
||
# 步长 10×1=10,目标 3 在步长内 → 直接到达
|
||
assert lim.limit(3.0, 1.0) == 3.0
|
||
assert lim.limit(3.0, 1.0) == 3.0 # 已在目标:保持
|
||
|
||
|
||
def test_negative_direction():
|
||
lim = SlewRateLimiter(2.0)
|
||
assert lim.limit(-10.0, 0.5) == -1.0
|
||
assert lim.limit(-10.0, 0.5) == -2.0
|
||
|
||
|
||
def test_zero_or_negative_dt_holds():
|
||
lim = SlewRateLimiter(2.0)
|
||
lim.limit(10.0, 1.0) # 输出 2.0
|
||
assert lim.limit(10.0, 0.0) == 2.0
|
||
assert lim.limit(10.0, -1.0) == 2.0
|
||
|
||
|
||
def test_zero_rate_limit_freezes_output():
|
||
lim = SlewRateLimiter(0.0)
|
||
assert lim.limit(5.0, 1.0) == 0.0
|
||
|
||
|
||
def test_reset_to_value():
|
||
lim = SlewRateLimiter(2.0)
|
||
lim.limit(10.0, 1.0)
|
||
lim.reset()
|
||
assert lim.last_output == 0.0
|
||
lim.reset(3.5)
|
||
assert lim.last_output == 3.5
|
||
# 复位后从新值继续渐变
|
||
assert lim.limit(10.0, 1.0) == 5.5 # 3.5 + 2×1
|
||
|
||
|
||
def test_custom_initial_value():
|
||
lim = SlewRateLimiter(1.0, initial=5.0)
|
||
assert lim.limit(10.0, 1.0) == 6.0
|