学习 / 实践
具身实践(四):消息还在到,观测已经过期了
具身智能 · 工程案例
45回合MuJoCo对照,分离采样间隔、异常计数与观测年龄;理解旧消息为何会掩盖真实松爪,以及时间阈值仍有什么局限。
实验结果
浏览统计暂不可用
本文目录 9
上一轮已经让程序在搬运过程中持续检查接触:连续三次异常,就取消后续搬运。但有两个问题还没解决。
如果把传感器从每 20 ms 更新一次改成每 50 ms 一次,“三次”还是同一个等待时间吗?如果网络一直传来“接触正常”,却反复传的是几十毫秒前的旧消息,程序应该相信它吗?
这次用 45 个真实 MuJoCo 仿真回合做了对照。最直接的观察是:同一个三次阈值,报警等待从 22 ms 变成了 102 ms;重送旧正常包时,只看接触内容的策略一直没有报警。补上捕获时间检查,才识别出证据已经过期。
1. 先看一个实际失效过程
下面摘取 20 ms 采样、重送旧正常包条件中的记录。仿真在 4.800 秒开始强制松爪,但传输层继续递交之前的包。表中秒数由日志 tick × 0.002 换算。
| 接收时刻 | 包序号 | 原始捕获时刻 | 包内双侧接触 | 物理双侧接触 | 观测年龄 |
|---|---|---|---|---|---|
| 4.782 s | 239 | 4.782 s | 有 | 有 | 0 ms |
| 4.802 s | 239 | 4.782 s | 有 | 无 | 20 ms |
| 4.822 s | 239 | 4.782 s | 有 | 无 | 40 ms |
| 4.842 s | 239 | 4.782 s | 有 | 无 | 60 ms |
消息没有中断,内容也没有变成“异常”。发生变化的是:当前时间一直往前走,支持“接触正常”的那份证据却停在 4.782 秒。
三次计数策略没有看到坏接触,40 ms 异常跨度策略也没有看到。带新鲜度检查的策略在最后一行报警,原因是 stale,而不是 contact。
这一点很重要:年龄报警只证明“没有足够新的观测支持继续执行”,并不单独证明方块已经掉了。这次方块确实掉落,是另一条物理记录给出的证据。
2. 把采样、接收与控制拆开
复用原有零偏移场景、夹爪、动作时序和搬运前确认。所有回合都通过了前置检查,没有改变拾取难度。
| 时间尺度 | 本轮设定 | 负责什么 |
|---|---|---|
| 物理与控制步长 | 2 ms | 推进物理、读取输入、检查年龄、输出下一步目标 |
| 观测包周期 | 10 / 20 / 50 ms | 产生新的接触观测 |
| 常规轨迹记录 | 20 ms | 保存位置、速度与控制状态 |
| 搬运决策记录 | 2 ms | 记录每次收包、年龄、计数和报警 |
新鲜度检查必须在没有消息到达时也能运行。如果只把它写在接收回调里,完全静默时就没有机会检查超时。本例在每个控制步执行检查;这不是对真实操作系统实时调度能力的保证。
本轮使用同一个仿真时钟,时间以整数 tick 比较。MuJoCo 的时间、物理状态与控制输入本来就是不同字段,因此可以分别记录,而不是把“发送指令”当成“动作已经完成”。MuJoCo 3.3.7 状态与控制说明
3. 三种策略,分别补一条约束
| 策略 | 判定规则 | 新增约束 |
|---|---|---|
| count3 | 连续 3 个唯一坏观测 | 基线 |
| elapsed40 | 连续坏观测的首尾捕获时间相差至少 40 ms | 把样本数量改成时间跨度 |
| fresh60 | elapsed40,再加最近观测年龄达到 60 ms 就报警 | 不把旧正常消息当当前证据 |
三者都只接受序号和捕获时刻同时前进的包。重复包不会增加异常计数,也不会重置“最近捕获时刻”。正常的新观测会清空异常累计,报警后保持锁存。
elapsed40 并不是“坏了一次就启动一个 40 ms 闹钟”。它需要新的坏观测来确认跨度:如果后面完全没消息,只靠等待不能补出“持续异常”的证据。fresh60 另设年龄检查来处理这种未知状态。
实现中的关键关系是:
# now 和 capture_tick 使用同一仿真时钟;一个 tick = 2 ms
age_ticks = now - latest["capture_tick"]
# 收到新包后先更新 latest,再检查年龄
if policy == "fresh60" and age_ticks >= 30:
alarm = True
cause = "stale"源码还处理了无初始观测、重复包、乱序包、未来时间戳和阶段切换。这里摘录的是年龄分支,不是可直接替换整个监测器的完整程序。60 ms 是实验参数,没有经过硬件安全评估。
4. 45 回合到底改变了什么?
矩阵是 3 个观测周期 × 5 个输入条件 × 3 种策略。每格一次确定性运行,没有随机化、置信区间或概率成功率。
| 条件 | 物理输入 | 传给监测器的消息 |
|---|---|---|
| clean | 无扰动 | 正常更新 |
| empty-40ms | 无扰动 | [4.8, 4.84) 秒内应发出的包报告空接触 |
| forced-open | [4.8, 5.04) 秒强制松爪 | 正常报告实际模拟接触 |
| replay-good | 同样强制松爪 | 4.8 秒起重复最后一个旧正常包 |
| silence | 同样强制松爪 | 4.8 秒起不再送达消息 |
empty-40ms 是内容错误,silence 是没有消息;replay-good 则是消息在到、信息没更新。三种情况不能用“没检测到接触”一句话混在一起。
故障发生在 pre-step 时间,首个对应的 post-step 记录是 4.802 秒。传输故障持续到回合结束,强制松爪只持续 0.24 秒。控制器不修改方块坐标,掉落来自模拟动力学。
报警后的处理沿用上一轮:保持最后命令目标 0.6 秒,再结束回合。不冻结物理状态,不重新抓取,也不宣称硬件急停。
5. 三次异常,不是固定等待时间
受控松爪且消息正常更新时,得到以下结果。时间从 4.800 秒的故障起点计算:
| 观测周期 | count3 | elapsed40 | fresh60 |
|---|---|---|---|
| 10 ms | 22 ms | 42 ms | 42 ms |
| 20 ms | 42 ms | 42 ms | 42 ms |
| 50 ms | 102 ms | 52 ms | 52 ms |

三次连续样本的首尾跨度是两个采样间隔,即 (3 - 1) × 周期。再加本次从故障到首个样本的 2 ms,分别得到 22、42、102 ms。
对于 elapsed40,10 和 20 ms 周期都能在首个坏样本后的 40 ms 得到新证据;50 ms 周期必须等下一条,所以实际等了 50 ms,再加前面的 2 ms。
用时间表达阈值,减少了它与采样频率的耦合,但不会消除采样量化。 上表也不能视为任意故障相位的最坏延迟;本实验的故障起点固定,没有扫描相位、调度抖动或通信延迟。
6. 更快的采样也可能改变误停结果
在持续 40 ms 的空接触报告条件中,只有 10 ms 周期的 count3 误停,其余八格完成了放置。
原因可以直接从样本时刻读出:
10 ms 周期:4.802、4.812、4.822、4.832 为坏观测
20 ms 周期:4.802、4.822 为坏观测
50 ms 周期:4.802 为坏观测10 ms 情况在第三个坏观测就满足了 count3;同样时长的报告错误,在另外两个周期下没有凑够三个。elapsed40 看到的坏观测首尾跨度最多为 30 ms,下一条已经正常,因此没有报警。
这不是“采样越慢越安全”。降低频率同时可能推迟真正故障的发现。它说明:如果允许传感器频率变化,计数阈值也隐含改变了系统对短暂异常的容忍时长。
7. 为什么慢采样反而更早触发年龄报警?
重送旧正常包和完全静默两种条件中,count3、elapsed40 均未报警。fresh60 的记录如下,两种传输故障结果相同:
| 观测周期 | 最后一次有效捕获 | 达到 60 ms 年龄时报警 | 从故障到报警 |
|---|---|---|---|
| 10 ms | 4.792 s | 4.852 s | 52 ms |
| 20 ms | 4.782 s | 4.842 s | 42 ms |
| 50 ms | 4.752 s | 4.812 s | 12 ms |
50 ms 周期看起来“更快”,并不是它更擅长检测掉落,而是故障开始时,它手里的观测已经旧了 48 ms,距离 60 ms 门槛只差 12 ms。
因此必须同时报告故障时刻、最后捕获时刻和报警时刻。只比较最后一列,会误把采样相位差异当成策略能力。

图的接触和年龄曲线来自未加年龄检查的 elapsed40 回合;竖线标出配对 fresh60 回合的实际报警时刻。两者在决策分歧前的物理状态一致。阴影表示强制松爪区间,不表示传输故障在 5.04 秒结束。
年龄门槛也不能脱离正常周期选择。本例最大的正常更新间隔是 50 ms,60 ms 门槛只多留了 10 ms;真实系统若有传输和调度抖动,这个余量可能不够,本轮没有验证。
8. 如何确认不是程序自己给自己打分?
独立审计器不导入运行器或监测器。它从日志重建消息的来源、序号、捕获时刻、是否被采用、连续异常跨度和年龄,再计算报警与任务验收。
本轮实际通过:
- 45 回合独立核验,没有 MuJoCo warning。
- 45 对策略在首次决策分歧前,保存的物理状态前缀一致。
- 9 条无扰动完整轨迹与上一轮成功基线完全一致。
- 17 项新边界测试与原有 24 项测试通过。
- 180 份已提交原始文件的字节哈希与审计记录一致。
所有无扰动回合完成放置;所有真实松爪条件都未完成放置。新鲜度检查改变的是“证据过期后还要不要继续”,没有把任务重新做成功。
这些是实现和记录的一致性检查,不等于真实机器人安全认证。
9. 自己复现,再问下一个问题
代码使用原工程的 Python 3.12、MuJoCo 3.3.7 和锁定依赖。协议先于运行提交,来源版本和环境写在 manifest.json,没有新增模型调用。
.venv/Scripts/python.exe -m unittest discover -s experiments/vl01_observation_freshness -p test_monitor.py -v
.venv/Scripts/python.exe experiments/vl01_observation_freshness/run.py --out outputs/my-freshness
.venv/Scripts/python.exe experiments/vl01_observation_freshness/audit.py outputs/my-freshness
.venv/Scripts/python.exe experiments/vl01_observation_freshness/render.py outputs/my-freshness输出目录必须不存在。查看 代码与复现说明 和 逐回合审计。control.csv 保存每个控制步的判断,trajectory.csv 与 states.jsonl 保存 50 Hz 物理快照;后者不是完整的 500 Hz 状态序列。图表只读取实际记录,不重新执行物理,也不冒充场景录像。
本轮仍使用仿真真值和同一时钟。没有跨机器时钟同步、序号重启、伪造新时间戳、真实网络、视觉、自然摩擦滑移或恢复策略。监测仍只覆盖搬运阶段。
下一步应先区分“延迟但仍可用”和“必须重新观测”,再定义恢复条件。尤其不能在新消息刚恢复时,就自动接着执行旧动作。这个问题已写回原有 VL01 待办,尚未作为实验结论发布。
写给未来的自己合上文章前,留下一点自己的理解。
你的私人学习便签,只存在当前浏览器,不会上传或公开。清理浏览器数据会丢失,请导出留存。三种语言共用这篇文章的便签。
继续探索
继续探索
接下来,走哪条路?
- 具身实践(二):没抓住,就别继续搬运 →
在 MuJoCo 中加入抬升与双侧接触确认,用18回合对照检验停止空手搬运,并公开轨迹、视频、代码与局限。
- 具身实践(三):抓住之后,还要一直确认吗? →
27回合MuJoCo对照:用单次观测缺失与受控松爪,检验持续监测、误停和40毫秒确认延迟,并公开代码、轨迹与重放。
依据文章关联与同主题已发布内容整理,不使用随机推荐。