试验结果一直只有人工打的成功/失败三态标。这一页说明怎么在不加任何传感器、不改模型、不重训的前提下,让每次试验多带一个客观测量值:合爪时夹爪舵机的电流。也说明为什么现在刻意不下判定。
Present_Current(地址 69)。夹爪合在杯子上走不到目标位、持续加力,电流上去;合在空气里一路走到底,电流很低。这就是可用的触觉参照 —— 物理上是堵转电流,不是压力传感。
夹爪用的是 Feetech STS3215 串行总线舵机。它是个位置伺服:给一个目标角,它会一直加力直到走到。合爪被杯子挡住时走不到目标位,于是持续加力 —— 电流就是「有没有东西顶着」的直接证据。
这四个只读寄存器一直都在(lerobot/motors/feetech/tables.py 的 STS_SMS_SERIES_CONTROL_TABLE,sts3215 用的就是这张):
| 寄存器 | 地址 | 说明 |
|---|---|---|
Present_Position | 56 | 位置 —— 目前唯一在用的 |
Present_Load | 60 | 负载,符号-幅值编码(第 10 位是符号) |
Present_Voltage | 62 | 电压 |
Present_Temperature | 63 | 温度 |
Present_Current | 69 | 电流 —— 不在编码表里,读出来是原始无符号值 |
项目此前一个都没读过:17 维的 observation.state 全是位置和速度。
控制回路 30 Hz,send_action 每拍已经有三路 sync_read。这是一条 17 颗舵机的菊花链,每拍再加一次读等于给总线加约三分之一流量。这条链有前科 —— 2026-08-23 一次 configure 里的丢包就让整场试验作废。
所以只在两个时刻各读一次:
每次试验最多 3 次额外读(接触时读电流+负载,稳定时读电流),对总线是可以忽略的负担。
observation.state 意味着改状态维度 —— 现有 checkpoint 全部作废,必须重训。所以这一版是纯运行时校验:策略照常跑,只是结果里多一个数,和所有现有模型兼容。
要让它参与决策有两条路,代价差很多:
| 路线 | 代价 | 前置条件 |
|---|---|---|
| 当策略输入 | state 维度变 → 现有 checkpoint 全废 → 重训 | — |
| 当运行时门控 | 不用重训 | 先要有阈值,而阈值要从真实成功/失败样本统计出来 |
现在这一步正是在为第二条路攒数据。
--grip-gate auto 就是从数据集统计阈值而不是硬编码。
所以结果里只有测量值,没有结论:
"grip_current": {
"arms": {"right": {
"gripper_pos_at_close": 10.0,
"raw_at_close": 412, "mA_at_close": 2678.0,
"raw_settled": 398, "mA_settled": 2587.0,
"load_at_close": 233,
"settled_at_end": false
}},
"unit_note": "raw = 舵机原始读数; mA 按数据手册标称 6.5 mA/tick 换算,
本机未标定 —— 定阈值请用 raw"
}
raw,别经过这个系数。
写完先做了不碰硬件的逻辑自测(跟着 arm_home.py --selftest、trial_keyframes.selftest 的惯例,用假舵机)。结果发现:
raw_settled 恒为 None。而抓握恰恰常常就在试验末尾 —— 等于最需要的那个数会系统性缺失。
修法:在 result() 里补一次。试验刚结束、爪子多半还攥着,这时读到的就是稳定持握电流。并且用 settled_at_end: true 标明这次是收尾补读而不是按时读的 —— 两者时机不同,以后统计阈值要分开看。
九个用例全过:按时读、收尾补读、只动左爪、全程没合爪、读失败记 None、关闭开关时零动作、只读对应那条总线。
try 里,失败记 None。一次读不到,远不如把一场试验搞崩严重。bus1,右臂和底盘在 bus2(xlerobot.py:94,129)。自测验证了只读对应那一条。GRIPPER_DIMS 从 trial_keyframes import,不另写一份;那边有 assert_dims_match 兜底。--grip-current 默认开
--no-grip-current 要关才显式给
下次上机试跑不用做任何事,数就自动记进 trials/*.json。
left-sd-p0.2-b16-u20000/020000 实机
2/2 成功,两次都读到了:
| 试验 | 接触 raw | 持握 raw | 倍数 | Present_Load | 帧/CLIP |
|---|---|---|---|---|---|
| 1 | 3(19.5 mA) | 13(84.5 mA) | 4.3× | 92 | 90 / 0.0% |
| 2 | 7(45.5 mA) | 21(136.5 mA) | 3.0× | 216 | 291 / 0.3% |
Present_Load 同向变化(92→216),两个独立寄存器互相印证。
settled_at_end 都是 None,说明第二次读是按时读到的,0.4 秒的等待生效,
收尾补读那条兜底没被用上。raw_at_close 的量级是否合理(空爪应明显低于攥住杯子)。如果读回来是 0 或异常大,说明这颗舵机的固件版本寄存器布局不同,需要改用 Present_Load。
03-software/scripts/grip_current.py(探针)· 03-software/scripts/run_policy_trials.py(接入点:--grip-current、循环里 gcp.observe、结果字段 grip_current)lerobot/motors/feetech/tables.py 的 STS_SMS_SERIES_CONTROL_TABLElerobot/robots/xlerobot/xlerobot.py :94(bus1) :129(bus2)~/lerobot,升级 lerobot 后要重新核对。