6.4 KiB
O30 右手:五张真机照片与零位联合校准(第二版)
2026-09-21。原来的四组 SDK 指令不变:照片 1~4 分别对应拇指对食指、中指、无名指、小指。照片 5 的实测零位指令为:
[0, 0, 255, 195, 100, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
在项目根目录执行原命令即可,启动时直接使用上述零位:
./hands/O30/scripts/run_o30_right_mujoco_plan.sh
默认 URDF 已切换到模型目录中的 linkerhand_o30_right_photo_calibrated.urdf。仍然订阅 /cb_right_hand_control_cmd,消息类型和 20 路顺序不变。零位写入各关节的 origin 旋转,行程写入关节上限;SDK 映射从本次实际生成的 MJCF 读取,不重复叠加零位偏移。
输入与方法
- 四组捏合原始指令:
samples.json。 - 五张照片的可见关节位置与零位数组:
photo_landmarks.json。 - 照片没有可读的本地原文件,本轮从聊天中显示的 540×720 图片人工估计了关节中心和可见指尖位置。标注的像素不确定性为 5~15 像素;捏合时被遮挡的指尖端点不参与图像拟合。
- 对五张照片分别拟合相机位置、方向,并估计一个共享焦距,避免把拍摄视角变化当成关节误差。没有相机标定、畸变参数或实测三维点。
- 同时优化一套共享的 20 个零位偏移、20 个行程比例,以及有限指腹区域内的接触点。所有动作使用同一 URDF 和同一 SDK 映射。
- 目标包括照片关节投影、实测零位下的伸直姿态弱先验、四组指腹相对接触、手指与手掌的穿透限制,以及对原模型的小改动正则。零位照片并不等于测得每个关节恰为 0°,因此先验保留了角度余量。
- 使用多起点、有边界的非线性优化,在 MuJoCo 中反复计算误差。最后三轮局部搜索共 9,088 次目标评估;这是自动试错标定,没有训练强化学习策略。
- 连杆长度、关节轴和 STL 几何未变。新模型使用银灰色外壳和灰色指腹,便于和照片比较。
结果与限制
对相同人工标注,上一版和新版都分别拟合相机后进行比较。五张照片的综合关节投影 RMS 从 11.63 px 降到 8.02 px,约降低 31%。这是这些标注上的拟合误差,不是真机精度或独立测试结果。
各张照片的 RMS:
- 食指:9.91 → 9.07 px。
- 中指:11.78 → 9.06 px。
- 无名指:6.17 → 7.07 px;联合零位和其他姿态约束后,这一张有小幅回退。
- 小指:17.87 → 8.30 px。
- 零位:9.46 → 6.52 px。
实际启动脚本生成的 MuJoCo 模型中,四组指腹碰撞网格有符号距离分别为 −0.054、−0.042、+0.024、−0.025 mm(正为间隙,负为穿透)。拟合指腹法向误差均小于 1°,这五组姿态的最大网格穿透小于 0.06 mm,零位没有检测到穿透。这些是仿真内部的接触指标,不是真机达到亚毫米精度的证明。
保留的行程比例边界为 0.5~1.5。拇指根部 roll 和小指 PIP 的行程拟合触及边界,说明这些参数仍比较敏感。额外尝试 0.35~1.8 的范围后,图像 RMS 只从 8.02 降至 7.86 px,约 0.17 px 的改善小于人工标注误差,而且小指 PIP 仍触及新边界,因此保留较保守的版本。
单视角照片不能唯一确定所有关节的真实零位和完整机械行程。食指 DIP 在已有动作里始终为 0,行程没有得到观测支持,保留原值。仍需更多视角、关节端点角度或额外动作验证未见姿态与全行程;本次没有做真机操作或 PD 动力学标定。
查看和复现
第二版对比图:reports/o30_right_photo_calibration/photo_fit_comparison.png。上排为上一版只拟合接触的模型,下排为照片与零位联合拟合;每列使用相同相机和中性材质。最后一列是第五张照片的零位。
报告目录还包含五张单独姿态图和 *_landmarks.png:青色圈是人工照片标注,红点是模型投影,黄色线表示偏差。这些是模拟图上的标注诊断,不是原始照片叠图。参数、相机与逐点误差保存在模型目录的 o30_right_photo_calibration.json,当前生成模型的验证结果在报告目录的 validation.json。
独立预览零位或某组捏合,不发送 ROS 控制指令:
venv/bin/python tools/o30_right_calibration/fit_photos.py --preview zero
venv/bin/python tools/o30_right_calibration/fit_photos.py --preview pinky
也可将 pinky 替换为 index、middle、ring。
只生成模型并验证/渲染:
O30_RIGHT_PREPARE_ONLY=1 ./hands/O30/scripts/run_o30_right_mujoco_plan.sh
OPENBLAS_NUM_THREADS=1 venv/bin/python tools/o30_right_calibration/fit_photos.py --check --render
重新优化第二版,再生成模型:
OPENBLAS_NUM_THREADS=1 venv/bin/python tools/o30_right_calibration/fit_photos.py --fit --starts 3 --max-nfev 250
O30_RIGHT_PREPARE_ONLY=1 ./hands/O30/scripts/run_o30_right_mujoco_plan.sh
venv/bin/python tools/o30_right_calibration/fit_photos.py --check --render
当前版本请使用 fit_photos.py。旧的 calibrate.py 是第一版接触拟合的实现和公共函数,不包含五张照片的约束。
运行消息回归测试:
source /opt/ros/jazzy/setup.bash
PYTHONPATH="$PWD/venv/lib/python3.12/site-packages:$PYTHONPATH" python3 tools/o30_right_calibration/test_calibration.py
已验证实测零位初始化、四组真实 JointState 类型消息经过桥接回调后到达正确姿态、模型切换后的 SDK 行程同步、无效模型限位拒绝。另检查了四组捏合、实测零位、全 0、全 255 和 30 组随机输入,共 37 组;导出后所有连杆位置与优化模型的最大差异约为 3.9e-10 m。随机输入仅验证导出与映射一致,不代表其与真机一致或运动过程无碰撞。
备份
原始模型仍保留为 linkerhand_o30_right_stock.urdf 和 linker_hand_o30_right_stock.xml。上一版保留为 linkerhand_o30_right_pinch_calibrated_v1.urdf、o30_right_pinch_calibration_v1.json;上一版说明见 README_v1.md。
临时使用上一版模型:
O30_RIGHT_URDF="$PWD/src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/O30/linker_hand_o30_right/linkerhand_o30_right_pinch_calibrated_v1.urdf" ./hands/O30/scripts/run_o30_right_mujoco_plan.sh
之后正常运行原启动命令,就会再次生成并使用第二版照片校准模型。