Skip to content
Joker.Yun
Go back

Piper 机械臂 + LinkerHand 灵巧手:21 个实战踩坑记录(IK / 标定 / 通信 / 仿真)

约 15 分钟读完Edit page

把 Piper 机械臂的末端从两指夹爪换成 LinkerHand O6 灵巧手,再做平掌包络抓取——听起来是把两个 URDF 拼起来的事,实际做下来踩了 21 个坑。每个坑都按 现象 → 根因 → 修法 记录,项目已开源:piper-with-linkerhand。

Piper + LinkerHand O6 整机(点击进入在线 3D 查看器)

🔗 在线 3D 查看器 —— 点这里直接在浏览器里转模型(three.js + URDF,纯静态)

一、机械臂姿态与 IK

1. 手拖可达 ≠ 命令可达

现象:示教读数 J5 = -74.85°,命令下发后机械臂停在 -70.01° 不动,等待到位超时。

根因:断使能手拖可以越过软件限位(-74.85° 是拖出来的),而固件命令限位只有 ≈±70°。

定位技巧:超时打印的 MSE = 3.9034 deg²,反推 √(3.9034×6) ≈ 4.84°,正好是单个关节的偏差——用 MSE 反推”是哪个关节没到位”。

修法:钉死值取命令可达范围(-69.0 ~ -69.9)。

2. 钉错了关节

现象:一开始按”第 6 关节必须向上翻到限位”的思路去钉 J6。

根因:示教数据(3 条采样)显示真正顶死不动的是 J5(三条全 -74.85°),J6 只是 6.75° 的自由滚转,而且 J6 是绕掌心轴的纯滚转、对掌心位置影响严格为 0。

修法:从示教数据里找”多条样本里纹丝不动的那根轴”,别靠猜。

3. 4 自由度无法同时满足位置与姿态

现象:钉死 J5/J6 后,怎么调都有残余误差。

根因:剩下 J1~J4 要同时满足 3 个位置自由度 + 掌面平度,本质是 Pareto 权衡。

修法:量化前沿再取舍——放松 J5 1°(-69.9 → -69.0),换来平度 2.17° → 0.88°、位置 1.9mm → 5.0mm,都在抓取容差内。

4. 硬补偿(垫安装座)不一定划算

现象:想靠”手掌垫 5°“解决 J5 差 4.95° 的问题。

根因:J1~J4 的软补偿已经吸收了大半,垫片等于把收益还回去——模型上垫片方案 4.3°~8.8° 的误差,软件补偿只要 2.2°。

教训:先算再拆机。

5. IK 代价函数在奇异位形”失明”

现象:父类 IK 求出的掌面比最优解差一截(6.04° vs 1.19°)。

根因:代价用 tilt + yaw 项,而工具水平(RY≈90°)时 zyx 欧拉角的 yaw 是奇异分量——绕工具轴的滚转完全不受约束,优化器收敛到掌面翻错的分支。

修法:roll-aware 代价(用旋转测地距离替代 tilt/yaw 项)+ 多起点求解。

二、分析工具自身的坑(最容易骗自己的)

6. kinpy 的四元数是 w 在前

现象:分析脚本算出”固件上报姿态与 URDF FK 差 165°“,据此得出”两边坐标系不同”的错误结论,并把这个错误读法写进了 IK 代价函数。

根因:kinpy 的 Transform.rot 是 scalar-first (w, x, y, z),而 scipy R.from_quat 要 (x, y, z, w)。

修法:统一用 R.from_matrix(fk.rot_mat);修好后 FK 与真机固件差 0.005°。

教训:分析工具本身也要对真机做校验,别拿它当真理去反推模型。

三、手眼标定

7. 平抓标定不是纯单应性

根因:平抓时掌心相对法兰有水平臂长 L,而且偏移方向随抓取方位角旋转,所以”像素 → 法兰”不是单应性。实测纯单应残差是 H+L 联合模型的 10~30 倍。

修法:联合拟合 H(像素→掌心)+ L;L 从标定数据里直接解出来(省了拿尺子量)。

8. 小样本 + RANSAC = 假拟合

现象:6 个点拟合出 L=0、RMS 25.62mm,看起来”精度还可以”。

根因:n=6 时 RANSAC 用 4 点最小集就能零残差”精确”拟合、其余点被当外点丢掉;同时 L 的网格搜索退化到边界 0 却没有报警。

修法:n<15 用全点最小二乘、L 命中边界就报警、打印最差两个点、支持 --drop-points 剔除重拟合。真实 RMS 是 8.48mm(之前那个 25.62 是假象)。

9. 校验阈值把正常操作判成错误

现象:手拖到自然止点 -74.9°,被”目标 -69.0±3°“的校验全判超差。

根因:同一个姿态族内 J5 差 5.9° 对掌心 xy 的影响只有 ~2mm(与标定 RMS 同量级),阈值定得太死。

修法:改成区间 [-76.5°, -67.5°],并把容差的量化代价写进注释。

10. 确认交互走错了通道

现象:姿态超差弹出”仍要记录?[y/N]“,用户按不了 y。

根因:复核用的是终端 input(),而用户所有交互都走远程画面(cv2_display 的 poll_key/鼠标回调),终端根本收不到。

修法:确认提示画在实时画面上、走同一按键通道 + 超时自动跳过。

四、灵巧手通信与手势

LinkerHand O6 张开 → 握拳动画

11. CAN / Modbus 走错链路

现象:手一直没反应。

根因:config 里 linker_hand_modbus 被注释掉 → 代码一直走 CAN 路径,而实际的手是 RS485/Modbus 版。

修法:以 SDK 自己的 LinkerHand/config/setting.yaml(RIGHT_HAND.MODBUS=/dev/ttyUSB0)为部署真值。顺带排掉一个常见坑:Modbus 与 CAN 的关节值都是 0~255,不需要换算。

12. 左右手不能靠镜像

根因:SDK 不做任何镜像,手别只决定 CAN id(左 0x28 / 右 0x27)与手势表;官方 yaml 里 O6 右手没有定义”握拳”(只有张开 [255,70,255,255,255,255])。

修法:右手握拳只能用官方示例的通用值 [102,18,0,0,0,0] 起步,且必须实机验证(可 config 覆盖)。

13. 失能 = 重力事故

现象:折叠 + 平掌的姿态下直接 disable_torque(),臂会下坠。

修法:走”先移动到可安全失能姿态(J5=+25°)再失能”的流程(就是 disable_arm 那套)。

14. “原地固定手腕” ≠ “带位姿翻腕”

现象:启动时为了保持平抓构型,结果在 home 姿态把腕转了 128°。

修法:区分”原地钉手腕(joint 空间)“和”带位姿移动中翻腕(IK 插值)“,启动用前者。

五、抓取动作

上方 GIF 为仿真中的抓取序列;下方视频为真机实拍完整流程(约 29 秒):

平掌包络抓取完整序列

15. 侧向滑入会把物体推走

现象:水平滑入到位的过程中,大拇指从侧面把物体扫走了。

修法:加 direct_down 模式——正上方定位后垂直下抓;两种方式最终位姿完全相同,只改路径(一行 config 切换)。

16. 抓取高度与配置的桌面高度对不上

现象:配置里桌面 0.15,实际在 test 模式验证 0.11 才对。

修法:改用绝对高度键作为唯一来源(test 与抓取统一),避免”桌面 + 偏移 + 临时 delta”三层算术。

六、开源与仓库

17. 公开仓库前的清理清单

本机 config.yaml、标定数据、密码/内网 IP、Windows 本地绝对路径(D:/study/code/...)、几百 MB 的本地 SDK 副本——都不能进仓库。清完再复查一遍(这次扫出 4 处私网地址 + 1 处账号密码)。

18. submodule 必须”上游可达”

钉住的 commit 如果只存在本地(比如本地打了个补丁),别人 clone --recurse-submodules 直接失败。推之前一定要验证:

git -C <submodule> branch -r --contains <sha>

19. uv.lock 会记录解析时的索引 URL

本机用清华镜像 uv lock,锁文件里 1722 条 URL 全指向镜像。功能没问题,但公开仓库更规范的做法是在能直连 PyPI 的机器上重锁。

七、浏览器 3D 查看器与仿真一致性

20. URDFLoader 的 loadMeshCb 必须返回 Mesh

现象:所有网格加载失败,报 Cannot read properties of undefined (reading 'set'),而且只显示最后一个网格文件名——极易误判成跨域/环境问题。

根因:回调里 done(geom) 传了裸 BufferGeometry,而 URDFLoader 内部会对回调结果执行 obj.position.set(0,0,0)。

验证方法:把 vendored three 载进 node,裸几何体复现出的报错信息一字不差,包成 THREE.Mesh 后正常。

顺带:file:// 双击打开永远不行(同源策略),必须起 HTTP 服务或走 GitHub Pages。

21. 模型”看起来一样”其实不一样

现象:查看器里的机器人和 MuJoCo 仿真对不上(夹爪残骸、无连接件)。

根因:查看器用的是官方 piper URDF,而仿真用的是另一套模型——不仅 link6 网格自带夹爪、缺连接件,连 link3~link6 的世界位置都差最多 11.2mm(关节原点定义不同),关节限位也有 8 处不一致。

修法:别手工对齐——写脚本从 MJCF 生成查看器 URDF(sync_viewer_urdf.py),内置 FK 比对(修完偏差 1.1e-6 m),仿真一改重跑脚本即可。

MuJoCo 仿真中的平抓姿态

在线核对入口(免安装):3D 查看器 / 仓库主页。

一个相关教训:给仿真换末端时,一定要把整个夹爪关节链从臂末端彻底分离,再挂新手;只把最后一段的网格换成手、没有清理对应的关节/连杆层级,会留下”残骸”(网格自带夹爪、缺连接件),后面查起来非常费劲。分离要彻底,挂接要干净。

结语

回头看,这 21 个坑里最贵的教训是第 6 条:工具会骗人。kinpy 的四元数序读写反了,一个”坐标系不同”的错误结论就会传染到整个 IK 代价函数。后来我们养成的习惯是:任何分析工具的输出,都先拿真机数据校验一遍再信。

其次是第 8 条:小样本 + RANSAC = 假拟合。25.62mm 和 8.48mm 之间差的不是数值,是”你以为你解决了”和”你真的解决了”的距离。

仓库地址:github.com/cloud666666666/piper-with-linkerhand,包含平掌包络抓取、2D 手眼标定、MuJoCo 仿真场景与 three.js 静态查看器。


Edit page
Share this post:

Previous Post
我的秋招 AI 岗位每日雷达:牛客+龙哥表双源监控
Next Post
最近看到一个挺有意思的 AI 旅游项目:把 TripStar 拆了拆