← reports index
验收单 · 2026-09-06
2026-09-06 改动验收单 —— 归位、跑测面板、杯位分布
今天一共改了三块东西。每一块下面都写了:改了什么 · 我这边已经用什么证据验过 · 你要按哪几步验收 · 验收通过长什么样。凡是我这边验不了、必须你在浏览器里看的,都单独标出来了。
先记住三条硬规则(今天踩出来的):
① 页面改过就要硬刷新(Ctrl+Shift+R / 手机关标签重开),否则跑的是旧 JS;
② 你在跑测时我不会再重启 8770 —— 今天两次重启把阶段打成 orphan、横幅全变,是我造成的干扰;
③ 面板行为查不清,先看 journalctl --user-unit=robot-status | grep '\[panel:',页面每次决定画什么按钮都回传一条信标,不猜。
一、归位:开跑前把手臂放回示范起点
改了什么
| 文件 | 内容 |
03-software/scripts/arm_home.py(新) | 独立模块。起点读死、按墙钟时间插值(smoothstep,首尾速度 0),峰值 22°/s,终点连发 0.6 s,到不了位打 没到位。左/右/双臂由配置决定;录制时没上电的手臂(全 0 且从没变过)识别为不可用,不碰它、维持原姿。 |
03-software/configs/trial_start_pose.json(新) | 示范起点 = 40 集每集第一帧的中位数,带数据集出处。换数据集用 arm_home.py --recompute <数据集目录> 重算。 |
run_policy_trials.py | 连上后归一次位 + 每次试验开跑前再归一次;结果 JSON 每次记 homed: true/false;--no-home-start 关。面板「跑测参数」里有开关,默认开。归哪条手臂、归到哪:按这个模型训练用的数据集算(每集第一帧的中位数,缓存到 configs/trial_start_pose-<数据集>.json)—— 右臂模型归右臂(0826:肩抬 −17.1°),左臂模型归左臂(sep3-clean:肩抬 −29.5°),录制时没上电的那条不碰。另:--help 原来会崩(帮助文本里的 %),顺手修了。 |
为什么第一版没动:写成「每拍从实测位置 +0.75°」。负重手臂克服不了静摩擦,舵机不动;下一拍又从同一个实测位置算,目标永远不累积 —— 两次归位返回的六个关节角完全相同。同样写法在 leader-follower 里能用,因为那边动的是被牵引的 leader 臂(几乎不承重)。
我已验的证据
| 验证 | 结果 |
arm_home.py --selftest(带静摩擦模型的假机器人) | 8/8 PASS:到位偏差 0.99°、峰值 22.0°/s、起步 0.3°/s、左臂/头/底盘一个数不动、双臂各到各位、dry run 一帧不发、到不了位会报、缺维度拒绝、左臂识别为不可用 |
arm_home.py --arm right --go(真机) | 到位:shoulder_lift +51.3° → −17.1°,68.4° 行程 4.7 s,最大偏差 2.29°(容差 3°) |
你要验收的
1
面板起一批实跑(次数选 1),按确认。
预期:日志出现 [home] right span 6x° over 4.xs 然后 [home] 到位,最大偏差 x.x°;手臂真的抬到示范起点(肩抬 −17°,肘 74°,腕 −59°)。如果出现 ! 没到位,把那行发我。
2
按「开始这一次」。
预期:再归一次位(手臂已在位,几乎不动),然后策略接管。跑完看结果 JSON 里 "homed": true。
二、跑测面板:按钮必须跟着进程走
今天到底出了什么问题(信标抓到的,不是猜的)
根因:opTrialsTick 在 await 之后写着 if (gen !== OP_TR_GEN) return;,而模块每 3 秒重画一次就换一次代号。你在国内经 Tailscale 访问,一次往返常超过一秒 —— 请求还没回来代号已经换了,取回来的数据整个丢掉,没有任何一跳活到渲染那一行。面板永远停在最后一次成功的那屏,没有报错、没有异常,本机毫秒级延迟永远复现不了。journal 里那串每秒一条的 watchdog: panel untouched for 46s…85s 就是现场。
| 你看到的 | 机制 | 修法 |
| 确认/开始按钮不出现;卡片停在上一状态 | 代号竞态丢弃新鲜数据(上面那条) | 新鲜数据一定画;代号只决定续不续链;Ctl(true) 只在链 5 s 无心跳时才换代号 |
| 同上 | 渲染一抛,下一行的 setTimeout 到不了,链静默死 | finally 续链;渲染整段 try,抛了画红框 + 回传 |
| 同上 | 模块门一失败 OP_TR_TICKING=false,链永不复活 | 门只跳过渲染不杀链;看门狗按心跳复活 |
| 状态到 TRIAL 1/1,按钮还是「确认」 | d.context 没查 null,抛在两块 DOM 之间 | 已护;selfcheck 新规则 ⑥ 专抓这类(今天扫出 6 处同形状,全补) |
| 进程退了,底部条还挂着 成功/失败/不计 | 底部条被 tctlRender 顺带驱动,那条链停了 | 底部条独立定时器驱动,取不到数据 = 没按钮 |
| 过期按钮按下去「像生效了」 | 服务端不校验 | 409 闸:/api/trials/control 只放行进程此刻在等的命令(abort 例外),页面弹「进程现在在等 X」并重画 |
| 横幅变成「面板服务重启过」 | 我在你等门时重启了 8770 | 不再这么做 |
我已验的证据
| 验证 | 结果 |
trials_gate_selftest.py(起真实空跑走完全部状态) | 20/20 PASS:无进程 → 全 409;等开始 → 只放 start;运行中 → 全 409;等判定 → 只放 s/f/x;退出后 → 409 |
selfcheck.py(HTML 配平 / JS 语法 / 作用域泄漏 / 负载裸取 / 下发一致) | 全过,dashboard 与 data 两页 |
| 浏览器里按钮跟着状态走 | 待你验 —— 这是我这边唯一验不了的 |
你要验收的
3
硬刷新主面板,停在
操作页 → 试跑模块。
预期:底部没有操作条(没进程);journal 里不再出现 watchdog 行。
4
点「空跑 1 次」(手臂不动),一路等到它要你按「开始这一次」。
预期:按钮区在1 秒内从「启动按钮已收起」变成「▶ 开始这一次」,底部同时弹出同一颗按钮。journal 出现 [panel:op] phase=ready waiting=[start] running=true btn=true。
5
按开始,等它跑完到判定。
预期:按钮区只剩 ✅ ❌ —,底部条同步;journal waiting=[f,s,x] btn=true;没有 ASSERT buttons≠waiting 行。
6
故意用命令行发一个过期命令:
curl -s -X POST http://127.0.0.1:8770/api/trials/control \
-H "X-Auth-Token: $(cat ~/.robot_server_password)" \
-H 'Content-Type: application/json' -d '{"cmd":"confirm"}'
预期:HTTP 409,返回 "waiting_for": ["f","s","x"]。这就是「过期按钮按了也不会误触发」。
7
按「— 不计」,等进程退出。
预期:底部条在 3 秒内消失,按钮区回到「空跑 / 实跑」两颗。
命令行路径永远可用,和按钮走同一个接口:confirm / start / s f x / abort。
三、杯位分布:并进数据页总表
改了什么
| 文件 | 内容 |
demo_coverage.py | 返回逐集的点(points,含全链 FK 的指尖坐标)+ 两条手臂的参照(reach:肩轴、伸直平放时肩→肘→腕→指尖的骨架、指尖最远触及弧)。按真正在动的那条手臂算(夹爪开度方差),原来右臂下标写死,左臂数据集 54 集缩成一个点。 |
robot_server.py | 新接口 /api/demo_coverage?ds=<短名>;另加 /api/jserr(页面报错回传)。 |
robot_server_data.html | 总表每行一颗 📍 杯位,展开在该行下画该数据集的分布。单独标签删了。 |
配色是算出来的不是挑的:四个杯位四种颜色在暗色模式下过不了色盲分离硬地板(蓝 vs 紫 ΔE=1.9),改单系列一色,分组用簇顶 n= 标注。这台机器没有 node,官方验证器移植成了 .claude/skills/self-debug/validate_palette.py。
你要验收的
8
打开
/data(刷新),在总表里点
xlerobot-left-pick-cup-sep3-clean-20260905 那行的 📍 杯位。
预期:标题「干活的是左臂 · 54 集」;54 个点散开(横向跨度约 23 cm),不再是一个点;图上有两条手臂骨架(■肩轴 → ○肘 → ○腕 → ○指尖,正前方加粗带字,±60° 淡)和虚线触及弧;还有一条强调色的「示范起点」骨架(每集第一帧中位数,就是试跑归位的目标)和每集起点指尖的小空心点,侧栏写着肩抬/肘/腕的角度。
9
再点
xlerobot-right-pick-cup-20260826-0042 那行。
预期:「干活的是右臂 · 40 集」,点落在右臂骨架前方、贴近触及弧的一带。
10
看四个合并数据集那几行(
lr-merge-20260904 202 集、
lr-sameview-20260904 100 集、
lr-clean-20260905 94 集、
sep3-clean-20260905 54 集)。
预期:每行都有「🔗 合并自 N 个」,每个子集名是链接,点了跳到该子集那一行并高亮(被筛选/归档藏着的会自动放开);「详情 ▾」展开一张表:每个子集的集数 / 帧数 / 语言指令 / 位置 / 健康,末行核对「子集之和 = 合并后」。
这些 100 集、202 集是哪来的
都是面板「合并数据集」作业(aggregate_datasets.py)产的,来源写在每个数据集的 meta/merge_sources.json 里,现在页面上直接能点开看。时间线(芬兰时间):
| 数据集 | 集 | 合并时间 | 由什么合并 |
lr-merge-20260904 | 202 | 09-04 17:39 | 右臂 0826(40)+ 右臂 sep2 四批(51)+ 左臂 sep2 两批(51)+ 左臂 sep3 六批(60)= 全部 |
lr-sameview-20260904 | 100 | 09-04 17:47 | 右臂 0826(40)+ 左臂 sep3 六批(60)—— 去掉了 sep2(那批头是抬着的,视角不同) |
sep3-clean-20260905 | 54 | 09-05 01:35 | 左臂 sep3 五批(去掉 1824 那批 6 集) |
lr-clean-20260905 | 94 | 09-05 01:45 | 右臂 0826(40)+ sep3-clean(54) |
四个的「子集之和 = 合并后」全部对得上,没有合并时静默丢集。
四、一次试跑的数据是怎么说的(顺带)
今天 16:58 那一次(判据错、标 void):安全层 46/46 放行、拒帧 0;光照 G/R 0.94 与白天数据集一致;头部姿态与训练集差 <0.6°;全程关节值都落在训练集范围内。唯一对不上的:起始 shoulder_lift = +21.03°,而 40 集示范起点最高只到 +13.41°。指尖 19.5 秒总位移 3.2 cm,夹爪来回开合,没朝杯子去 —— 这就是「归位」这块工作的由来。证据等级:1 次,void,只能说这一次长这样。
五、还没做 / 你要决定的
- 判据默认值不改(操作者定):粘着杯子的批次就是探索性的,跑完在数据页「跑测数据」里补记备注/失败模式(
/api/trials/annotate,只写标注不动成绩)。
- 今天所有改动未提交。验收通过后由你
git add / commit。
- selfcheck ⑥ 还有 9 处「只写一处 DOM」的裸取属性(⚠ 级),不会造成半屏冻结,留着没动。