解读 15 · kinematics/hand.rs + mjcf.rs:手部与模型对齐
解读对象:
kinematics/src/hand.rs(356 行)+kinematics/src/mjcf.rs(227 行)
需要的前置:解读 13。
两份看似不相干的文件共享一种态度:hand.rs 讲"别对噪声输入过度建模",mjcf.rs 讲"几何只许有一个事实来源"。
hand.rs:一次值得写下来的失败
模块注释(hand.rs:1-35)是一篇事故报告:这个文件曾经很聪明—— theremin 武装时捕获背景,让面对墙壁的鸭子仍能区分手与墙;拟合平面豁免墙壁;加慢速漂移适应房间 rearrange。测试全过,真机全死,原因只有一句:传感器的输入不够稳定,撑不起那么硬的推理。哪些 zone 带可用 status 逐帧变化,半秒采的背景每次都不同,同一手势时灵时不灵。结论写成了格言:噪声之上的聪明会放大噪声,不会过滤它(hand.rs:10-11)。
留下的只有两件关于传感器(而非房间)的稳健性:
- 哪些 status 字节算数。 ST 官方只认 5 和 9,但 15 Hz 下 30 cm 外的手常以 4/13(一致性失败)回来、距离照样够音高用——只收 5/9 正是第一版死在 30 cm 的原因。于是
Config::statuses = vec,逐个注明含义,且做成配置而非常量,因为这是台架调试唯一要动的数。 - 一个 hold。 250 ms 内保留上一只手(hand.rs:74),音符才能骑过丢帧;
track(hand.rs:129)里负距离不算测量,带内距离取低百分位in_band[len/5](hand.rs:163)而非最小值——单区飞点会让音高变成一声啁啾。
status_histogram(hand.rs:188)是"终结争论的诊断":"过 30 cm 就停"和"status 4 占了 31 个 zone"是同一句话,只有后者告诉你该改什么。
mjcf.rs:几何的唯一事实来源
最小 MJCF 读取器:只要 bodies/hinge joints/sites,geoms、inertials、actuators、defaults 全部忽略(mjcf.rs:1-7)。两个关键决定:
- 根锚在 identity(mjcf.rs:94-100)。MJCF 根 body 的
pos是 MuJoCo 把机器人丢进世界的地方,躯干系 FK 不能继承它——世界放置是估计器的事。trunk_pos的 Z 单独保留(mjcf.rs:66-68),作为"来自资产而非常量"的站立高度。 - 坏模型保持可用且可见。关节轴默认 +z(mjcf.rs:125),零轴回退 +z 而非 NaN(mjcf.rs:219-227);解析错误逐一命名(
ParseError,mjcf.rs:14-36)。
上层(lib.rs:54-120)把它"编译"成每 site 一条扁平的根→site 链,查询就是一次 Pose 折叠(site_pose,lib.rs:174-190),无哈希、无分配;角度切片长度不足直接 panic——短切片是调用方 bug,不是机器人状态。
对齐
里程估计弃用手抄的腿段表、改用同一份 MJCF(odometry/lib.rs:13-17);头、ToF、脚、theremin 的手,全部解析同一个 ALPHA_MJCF(lib.rs:36)。正确性两端夹:tests/fk_against_mujoco.rs 64 随机位姿对照 MuJoCo,lib.rs:242-253 断言 alpha 资产带得齐守护进程要的 site 与 joint。
你带走的收获
- 噪声输入上的聪明会放大噪声;先决定"哪些输入算数",再做推理,并把它做成可配置。
- 诊断直方图比现象描述更接近可执行动作。
- 解析器只取所需子集、错误逐项命名,好过通用 XML 绑定。
- 几何参数单源化:手抄的参数表迟早和资产漂移。