真机落地前的仿真补齐:夹持/腕力/真值位姿,让仿真能验证 S07–S09
这批是 09-24 那轮仿真工作的收尾,一直没提交。内容按目的分四块: 1) 仿真真能「夹住」工件 sim_grasp_lock:给规与夹爪之间加接触锁(默认关,run_gauge_assembly --sim 才开)。 没有它时仿真里规只靠摩擦,S08 棘轮一转就滑出指间,于是把「仿真不真」误读成 「力判据不对」。新增 test_sim_grasp_lock.py。 2) 腕部力传感器进仿真 sim_wrist_ft(+ config/wrist_ft.yaml):S07 的首选判据是腕部 FT 的轴向突变, 而仿真里以前根本没有腕部力,S07 只能退到触觉兜底,两套判据从没在同一场景下比过。 现在两条通道都在,接触判定可对照。 3) 真值位姿可查(不再靠猜) sim_live_pose / sim_pose_service:把 Gazebo 里刚体的实测位姿发布出来, stage2 的真值路径按**实测**位姿算孔位,而不是按场景文件里的标称位姿。 顺带把真值孔位的推导(truth_holes)独立出来并补测。 4) 工具几何成为可配置项 config/sim_tool_geometry.yaml + stage2 的 tool_tip_below_flange_m 启动参数: 仿真里规会在指间下滑,工具长每次装夹都不同(实测见过 243/254mm), 悬停高度与 S07 下探预算必须按实测值算,不能再写死。 应用层同时跟进:gauge_fsm / gauge_config 把「悬停时工具尖端入没入工件」的判据 (hover_z_guard)与拖件判定(drag_verdict)收成纯函数并补测;stage3 搜孔、 site 导出、run_gauge_assembly 与若干文档同步。 新增 test_gauge_exit_lift / test_gauge_gripper_client / test_node_handler_collisions。 测试:全仓库 1085 passed。
This commit is contained in:
@@ -70,6 +70,12 @@ python3 scripts/check_sim_gripper.py # ★ 夹爪的几何验收(开
|
||||
机械臂应当**在重力下保持站住**。若它瘫下去或消失,说明描述有问题 ——
|
||||
见 [坑 1](#坑-1官方-flexiv_description-直接喂-gazebo-会被-sdformat-拒掉)。
|
||||
|
||||
> ⚠️ `check_sim_scene.py` 是**跑前**检查("现在这一轮能不能开始"),不是跑后检查:
|
||||
> 一轮装配跑完之后场景**必然**是脏的 —— 放手时抓取锁释放、规掉在台面上,
|
||||
> 落点可能磕到工件。所以跑完再查会报红,这是正常的。下一轮
|
||||
> `run_gauge_assembly.sh --sim` 开头的上料脚本会把 `scene.yaml` 里**所有**物体
|
||||
> 复位回名义位姿(`sim_load_gauge.py::_reset_scene_objects`),届时就干净了。
|
||||
|
||||
**这几条 `check_*.py` 才是验收标准,"有图像 / 服务有返回"证明不了什么。**
|
||||
它们做的判断分别是:
|
||||
|
||||
@@ -532,6 +538,88 @@ python3 -m pytest src/arm_sim_gazebo/test/test_truth_holes.py -q
|
||||
|
||||
---
|
||||
|
||||
### 坑 14:上一轮把工件推走了 → 下一轮 S04 报"一个视觉目标点都没收到"
|
||||
|
||||
现象(`/tmp/pipeline_stage1.log`):
|
||||
|
||||
```
|
||||
[sim_truth_perception-1] [ERROR] 仿真物体 'workpiece' 已经不在 scene.yaml 说的地方了 —— **停止发真值点**
|
||||
名义 (x,y,z)=(0.5200,0.0000,0.4000) yaw=0.0°
|
||||
实际 (x,y,z)=(0.4923,-0.0717,0.4000) yaw=8.3°
|
||||
偏差 |Δp|=76.9mm、姿态差 8.3°(容差 2.0mm / 2.0°)
|
||||
```
|
||||
|
||||
然后下一轮 `gauge_assembly` 报「一阶段 150s 没锁定:**一个视觉目标点都没收到**」,
|
||||
看起来像"真值节点没起来 / 场景名读不出来"。
|
||||
|
||||
**这条不是 bug,是两条正确行为叠在一起**:
|
||||
|
||||
1. 真值感知是**纯几何**的 —— 它按 `scene.yaml` 的名义位姿算目标点与孔位。
|
||||
物体一挪,算出来的就是假测量,而假测量**不会报错**:现象会是
|
||||
「S05 两孔间距 169mm(真值 58mm)」或「S07 一路下压不接触」,
|
||||
看起来像检测/标定/阈值坏了。所以 `sim_truth_perception` 有漂移看门狗。
|
||||
它**不是一条线,是两条**(2026-09-24 改,判据是纯函数 `pose_deviation()`,
|
||||
有单测;看门狗本身会自检"话题一帧都没收到"):
|
||||
* `pose_info.drift_tol_m`(2mm)→ **告警**,但**照发点**:真值几何已经跟着
|
||||
**实测**位姿算了(见下面 §5.1),2mm 只是"这场戏跟 scene.yaml 写的不一样了";
|
||||
* `pose_info.drift_stop_m`(20mm)→ **停发**:到这个量级说明工件掉了/被搬走了,
|
||||
场景前提没了,再发点等于指挥机械臂去装一个不存在的工件。
|
||||
2. 谁把工件推走的:上一轮 S07 判到接触的那一刻臂上顶着几十牛顿
|
||||
(位置控制的臂撞硬面**不会**在"刚碰到"停住),若卸载没生效,S08 再一转,
|
||||
工件就被推走/推歪了。所以第 1 条其实是在**替你报告真因**。
|
||||
|
||||
**修法**:把场景搬回去。`scripts/sim_load_gauge.py` 开头现在会复位
|
||||
`scene.yaml` 里**所有**物体(不止通止规)—— 所以正常情况下重跑一次就恢复正常,
|
||||
不必重启仿真。若复位失败(比如工件卡在爪里),看门狗会继续拒发点,
|
||||
那时**重启仿真**(`bash scripts/run_sim_gz.sh`)让它按场景重新 spawn。
|
||||
|
||||
排查顺序(按物理可信度):
|
||||
|
||||
```
|
||||
① 看 sim_truth_perception 的日志有没有"不在 scene.yaml 说的地方了"
|
||||
①a 偏差 ~2mm 级 → 只是告警,点照发,不用管(这是落地/蠕动的常态)
|
||||
①b 偏差 >20mm → 场景前提没了:复位(重跑 sim_load_gauge.py)或重启仿真;
|
||||
顺手查"谁推的它"(S07 的卸载有没有生效:日志里那行
|
||||
"接触后卸载:回退 X mm … 残余 Y N")
|
||||
② 没有 → 才轮到查"真值节点起没起来 / 场景名对不对"
|
||||
```
|
||||
|
||||
### 5.1 真值口径:**几何按实测位姿算**,名义位姿只用来报"物体被搬走了"
|
||||
|
||||
(2026-09-24 定;实现 `arm_sim_gazebo/sim_live_pose.py`,谁要谁订阅。)
|
||||
|
||||
真值路径最早是"按 `scene.yaml` 的名义位姿算目标点与孔位",也就是**假设物体没动过**。
|
||||
实测这个假设站不住,而且偏差是**安静**的:
|
||||
|
||||
| 现象 | 真因 |
|
||||
|------|------|
|
||||
| S05 算出的两孔间距 169mm(真值 58mm) | 工件掉进 90mm 通槽、落到地面(z≈0.6m),而真值仍按名义位姿把孔投到像素上 |
|
||||
| S07 一路下压不接触 | 孔位差 2~3mm ⇒ 规尖(φ6)对不上孔(φ7,单边 0.5mm 余量),压在工件面上而不是进孔 |
|
||||
|
||||
而 2~3mm 是**天天都有**的:物体是"在名义位姿上方 spawn、再落下"的,落地就偏
|
||||
2.6mm;工件骑在台面通槽上静置十几分钟还会再挪 1.9mm。
|
||||
|
||||
所以现在的口径是:
|
||||
|
||||
1. **算几何用实测位姿**(`/world/<world>/pose/info` → `LiveScenePose`)。
|
||||
真值感知的目标点、二阶段定位的孔位都走这一条。这也更接近真机 ——
|
||||
真机上的视觉也是"量到哪就是哪",不会拿图纸坐标去算。
|
||||
2. **名义位姿只做两件事**:判"物体还在不在该在的地方"(上面那两条线),
|
||||
和开局复位。
|
||||
3. **开局复位**(`pose_info.reset_on_start: true`):只在节点启动后 20s 窗口内、
|
||||
只做一次,把被盯的物体 set_pose 回名义位姿 —— 专门消掉 spawn 落地的
|
||||
2~3mm。运行中的漂移**必须报错**,不能悄悄搬回去(那会抹掉"谁把它撞歪了"
|
||||
的现场,还可能把工件从爪里拽走)。实现见
|
||||
`sim_truth_perception._maybe_startup_reset` 与 `sim_pose_service.set_model_pose_yaw`。
|
||||
|
||||
```
|
||||
⚠️ 复位/搬运用的是 Gazebo 的 `set_pose` 服务,命令行调用时 `--reqtype` 必须写
|
||||
`ignition.msgs.Pose`(写 `gz.msgs.Pose` 会报 "Unable to create request",且不说是哪错了)。
|
||||
实现只有一份:`arm_sim_gazebo/sim_pose_service.py`。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 现状与进度
|
||||
|
||||
### 已就绪(端到端验证过)
|
||||
|
||||
Reference in New Issue
Block a user