仿真夹爪适配器:服务接口对齐真机,开口按正运动学换算成真毫米
为什么
------
应用层只认 arm_msgs/srv/GripperExecute(开口毫米),真机与仿真必须同一套接口。
但 GN01 的 URDF 里 <mimic> 是**线性近似**:直接按线性系数下发 finger_width_joint,
"命令开 20 mm"在 Gazebo 里量出来是 21.75 mm —— 抓不住工件,而且看起来像算法问题。
实测(2026-09-24,Gazebo 内量两指尖间距):
线性下发 20 mm → 实际 21.74 mm(偏差 1.74 mm,超 2 mm 抓取容差的前半步)
走 FK 反解 → 实际 20.00 mm(≤0.5 mm)
怎么改
------
- 新增 sim_gripper_kin.py:解析 URDF 正运动学,算出"主关节值 ↔ 真实开口 mm"的
单调曲线,反解用二分。曲线非单调/零行程时**主动报错**,不静默退化成线性。
- sim_gripper.py:开口换算全部走该曲线;容差统一改成毫米。
- sim_gripper_adapter.py:反馈改取**从动关节**(见下);is_gripped 按毫米判定。
- 新增 check_sim_gripper.py:三层验收(服务/开口一致/指尖真实几何行程 + 对称性)。
踩到的坑
--------
- finger_width_joint 挂在无质量的 unused_finger_width_link 上,与手指**没有机械
连接**:命令到 0.100,它的 /joint_states 恒为 1.4e-08,6 个从动关节才准到
0.7854。反馈若取它,/gripper/status 会永远报"开口 0 mm"。
- 残留进程会伪造出"代码写错了"的现场:旧 sim_gripper_adapter 与新实例抢话题,
验收读到"指尖间距 0.03 mm"(真实 127 mm);孤儿 static_transform_publisher
用 latched /tf_static 把旧相机外参按在新仿真上。
→ 新增 scripts/lib/sim_procs.sh;run_sim_gz.sh 用 setsid 独立进程组,
PGID 记进 log_sim/sim.pgid,stop 时整组 kill;启动前预检拒绝带病启动。
- 验收脚本自己也错过两次(把 TF 的米当毫米比 → 127 mm 显示成 0.13 mm;
用会转动指垫的中点漂移判对称 → 必然位移 16 mm)。判据改成"两侧各走多远",
单位只在 _dist_mm 里换算一次。
验证
----
python3 -m pytest src/arm_sim_gazebo/test/ -q → 307 passed
python3 scripts/check_sim_gripper.py → 三层全过
第 2 层:请求/反馈开口最大偏差 < 0.5 mm
第 3 层:期望行程 vs 实际行程、左右行程差,均 < 1.5 mm
bash scripts/stop_sim_gz.sh --list → 整组列出(含 static tf)
文档
----
- 仿真手册:新增 §4.4(开口 mm 怎么换算)、坑 12(宽度关节不动、
反馈必须取从动关节);停止一节改写成进程组语义。
- FRAMEWORK_RULES:新增 R4.11(残留进程会伪造现场)、R4.12(验收判据要挑
与实现无关的物理量);R4.9 表补夹爪;清单补 13/14 两条。
- HANDBOOK:新增 §3.4「在仿真里开合夹爪」,补 §4.4 的两条新教训。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+46
-1
@@ -318,11 +318,49 @@ conda 激活不会往 `LD_LIBRARY_PATH` 加东西,而 gz 的插件搜索路径
|
||||
|------|------|
|
||||
| `scripts/check_sim_cameras.py` | 深度反投影必须命中已知平面(地面 z=0 / 台面 z=0.40) |
|
||||
| `scripts/check_sim_scene.py` | 摆放自洽 + 像素门槛与真机一致 + 成像几何达标 + 手外相机**完整**看得见工件 |
|
||||
| `scripts/check_sim_gripper.py` | 命令开口 mm → 两指尖在 `world` 里的**实际间距**变化同样的 mm;两侧行程相等;指尖坐标在 `--verbose` 里打出来(判错时可以回看它到底取了哪两个点) |
|
||||
|
||||
### R4.10 停仿真用 `scripts/stop_sim_gz.sh`,不要 `pkill -f`
|
||||
|
||||
`pkill -f` 会先匹配到**调用它的那个 shell**(它的命令行里含同样的字符串),
|
||||
表现是"命令秒返回、什么都没停"。脚本按进程树精确识别,并在杀完之后**复查残留**。
|
||||
表现是"命令秒返回、什么都没停"。
|
||||
|
||||
**更重要的是:残留进程比"没停干净"严重得多**(R4.11 解释为什么)。
|
||||
脚本走两条路:进程组(主)+ 模式扫描(兜底),杀完**复查残留**并非零退出。
|
||||
|
||||
### R4.11 仿真栈必须能"整组停掉":残留会伪造出"代码写错了"的现场
|
||||
|
||||
实测(2026-09-24)踩到的两个残留,症状与"夹爪换算写错了"**一模一样**:
|
||||
|
||||
| 残留的进程 | 它干了什么 | 现象 |
|
||||
|-----------|-----------|------|
|
||||
| `sim_gripper_adapter`(旧会话的) | 与新适配器**同时**订阅 `/joint_states`、同时发布 `/gripper/status`;服务调用被随机分给其中一个 | 验收读到"开口恒为 0 mm、指尖间距 0.03 mm"(真实 127 mm) |
|
||||
| `static_transform_publisher`(5 个孤儿) | 发 **latched** 的 `/tf_static`,把旧相机外参一直按在新仿真的 TF 树上 | TF 查询返回互相矛盾的数据 |
|
||||
|
||||
所以定下三条:
|
||||
|
||||
1. 仿真必须整栈起在**自己的会话/进程组**里(`setsid`,PGID 记在 `log_sim/sim.pgid`),
|
||||
停止时 `kill -TERM -PGID` 一次带走 —— 不依赖任何"进程名模式表";
|
||||
2. 模式扫描只当兜底,且除点名表外要有**兜底规则**(命令行含 `<仓库>/install_sim/`
|
||||
就算仿真进程),这样"以后新增节点忘了改表"不会再留孤儿;
|
||||
3. **启动前预检 + 验收第一步查重名节点**:宁可拒绝启动,也不给一个
|
||||
"看起来能跑、数据是错的"仿真。
|
||||
|
||||
### R4.12 验收工具自己也会说谎:判据要挑"与实现无关的物理量"
|
||||
|
||||
`check_sim_gripper.py` 的第 3 层先后给过**两个错误结论**,都是脚本自己的问题:
|
||||
|
||||
| 写出来的判据 | 为什么会错 |
|
||||
|-------------|-----------|
|
||||
| 指尖间距差(米)与期望(毫米)比较 | 漏了 ×1000:127 mm 显示成 0.13 mm,**恰好看起来像夹爪没张开** |
|
||||
| "两指中点漂移"判对称 | tip frame 固连在**会转动**的指垫上、走的是弧线,中点必然位移 16 mm;而左右行程其实相等(52.56 / 52.56 mm) |
|
||||
|
||||
推论:
|
||||
|
||||
- 单位换算**只在一个函数里做**(`_dist_mm`),别处不许再乘 1000;
|
||||
- 判据要挑**与机构无关**的量("两侧各走了多远"),别挑带方向的量再投影;
|
||||
- `--verbose` 要把**原始物理量**(指尖坐标)打出来,判错时能回看;
|
||||
- 结论不对时,先怀疑判据,再怀疑被测对象 —— 上面两条都是先怀疑了代码。
|
||||
|
||||
---
|
||||
|
||||
@@ -493,6 +531,8 @@ grep -l ros_gz_sim install/*/lib/*/* | wc -l # 应当变成 0
|
||||
| 10 | 我写进文档的命令,自己跑过一遍吗? | R6.7 |
|
||||
| 11 | 这次构建是在**目标环境**里做的吗?(`head -1 install/*/lib/*/*`) | R6.8、R5.7 |
|
||||
| 12 | 我碰的文件属于"跟随当前臂"还是"按臂各留一份"?改完跑 `check_arm_prefixes.py` 了吗? | R1.1.1 |
|
||||
| 13 | 我验收用的判据,是"与被测实现无关的物理量"吗?单位只在一处换算了吗? | R4.12 |
|
||||
| 14 | 我起过仿真/长跑进程吗?停干净了吗(`stop_sim_gz.sh --list` 应为空)? | R4.10、R4.11 |
|
||||
|
||||
---
|
||||
|
||||
@@ -532,6 +572,11 @@ grep -l ros_gz_sim install/*/lib/*/* | wc -l # 应当变成 0
|
||||
| 代码里一半英文 docstring 一半中文注释 | 没有统一口径,谁写谁定 | R6.5 |
|
||||
| 真机栈哪天突然报"模块找不到",明明装过 | `install/` 的入口脚本 shebang 指向**仿真环境**的 python(哑弹) | R6.8 |
|
||||
| 照着文档敲命令,命令根本不存在 | 写文档时没真跑过 | R6.7 |
|
||||
| 夹爪明明开着,`status` 一直报"开口 0 mm" | 反馈取了"虚拟宽度关节",它在 Gazebo 里根本不动 | R4.11、坑 12 |
|
||||
| 验收说指尖只张开 0.13 mm(其实 127 mm) | 验收脚本自己漏了 ×1000(米 vs 毫米) | R4.12 |
|
||||
| 服务/话题/TF 全在,但读数全是乱的 | 上一轮的 `sim_gripper_adapter` / `static_transform_publisher` 还活着 | R4.11 |
|
||||
| 命令"开 20 mm",两指实际 21.75 mm,夹不住工件 | 上游 `<mimic>` 是线性近似,开口必须走正运动学反解 | R4.4、坑 12 |
|
||||
| 残留进程把旧相机外参按在新仿真上 | `static_transform_publisher` 发的是 latched `/tf_static` | R4.11 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user