Files
linkerhand_retarget_ros2/src/linkerhand_calibration/CALIBRATION_V3_IMPLEMENTATION.md
T
2026-09-16 14:05:08 +08:00

20 KiB
Raw Blame History

按关节零位与实测/复制 JSON v3 实施记录

更新:2026-09-15。范围:linkerhand_calibration。

当前收敛结果

  • G20、L6、O6、O12 右手共用四轮连续扫描,随后执行独立的稳态指令映射训练与验证。原精度门限保留。
  • 正式入口在启动硬件前检查运动覆盖与绝对零位来源。O12 的 thumb_mcp、pinky_pip、ring_pip 按用户确认沿用原始 URDF 零位,记录为 assumed_source_cad_zero;运动继续实测或按声明复制。
  • 收尾先保存 JSON,再由 runtime/artifacts/urdf_from_json.py 读取它重建 URDF。 发布器检查修正元数据能否逐字节重建最终文件;可独立使用 rebuild_calibrated_urdf 命令。
  • 四型号的合成原始观测均已通过实际拟合、文件生成、验收、发布及读取器加载。 合成夹具使用明确的几何真值与范围,这不代表四种实机均完成验证。
  • O6 会话 20260915_134211 实采 36/36 单元一次通过,无重扫、无采集中断,采集耗时 412.35 秒。 同一原始日志已生成候选 JSON 与修正 URDF,独立 JSON 重建得到相同字节;整链几何验收仍失败,未发布。 最大 Tag 位置 P95 为 15.32 mm,小指末节姿态 P95 为 7.76°,不能称为整手精度合格。
  • 随后用棋盘格重新测得 O6 三相机外参:两组各 15 个姿态,最大联合重投影 RMS 为 0.928 px, 最大重复平移差 0.341 mm、旋转差 0.043°,通过原门限。新配置为 config/o6_three_camera_extrinsics_20260915.yaml;旧数据保留原外参身份,不混用。
  • 新外参会话 20260915_144514 完成拇指 24/36 单元后,小指准备求解有两个候选耗尽 150 次迭代。 同一批 120 帧离线复现确认是内部 LSMR 线性求解精度不足;调整 atol/btol 为 1e-10 后, 四候选均在原 150 次预算内收敛。外层收敛条件、分支统计和物理精度门限未变。 test_recorded_pinky_alternatives_converge_within_original_budget 保存该实采回归;相关 34 项测试通过。
  • 恢复会话 20260915_145437 暴露旧日志在运动回调中校验导致的反馈超时报错:离线复现读取约 1.41 秒、完整凭据/单元校验约 13.88 秒。PreparedResume 将静态校验移到回调启动前; 当前固定基准与逐关节安装仍在现场独立验证。后续 20260915_145748 暴露一次性导入大日志 仍会阻塞;改为每个控制周期最多导入 64 条记录,全部持久化后才确认复用。 恢复相关 73 项测试通过,包含导入期间持续处理反馈、接收中止及落盘前不得跳过单元;原 1 秒反馈保护未变。
  • 图像模型零位恢复还需同步导入该零位的 pnp_candidate_frame,不能只复制 joint_zero_sample。 已修复该来源闭合问题;真实中断恢复会话离线核对发现恰好漏掉 10 帧零位图像,按新导入逻辑保留后 原凭据检查通过。原始日志未修改。相关 74 项测试通过,包含连续两次恢复的零位图像回放。
  • 新外参完整会话 20260915_150015 已完成 36/36 单元,采集中无暂停,两次自动重扫均通过。 生成的候选 JSON 可独立重建出字节相同的修正 URDF,并通过标准 ROS URDF 加载。 整链训练阶段的刚性 Tag 安装检查仍失败:拇指 IP 位置 P95 为 9.73 mm,小指 DIP 姿态 P95 为 9.40°。 这不是第四轮整链精度通过的结果,未生成发布 manifest,原始 URDF 未覆盖。 用户已确认 ID4/ID5 安装在平整硬片上,Tag 黑框为 16×16 mm,棋盘格单格为 27 mm。 尚不能据这些残差判定硬件故障。
  • 随后完成三相机原始棋盘格内参核验:正面 34、侧面 25、顶部 24 个不同姿态; 每第 4 张预先留作独立验证。验证重投影 RMS 从旧内参的 0.880/0.955/0.484 px, 降至 0.276/0.465/0.317 px。新内参保存在 config/o6_camera_intrinsics_20260915/。 配套外参使用正面/侧面 25 组同步观测和补采的正面/顶部 15 组同步观测,均无剔除; 最大 RMS 0.575 px、旋转重复差 0.024°、平移重复差 0.269 mm,通过原门限。 O6 产品配置已指向 config/o6_three_camera_extrinsics_intrinsics_20260915.yaml, 并通过无运动的配置及受保护输入校验。旧相机文件与旧整手记录保留原身份。 原图、角点、训练/验证划分、拟合报告及导出哈希保存在 calibration_output/O6_RIGHT_001/camera_intrinsics_audit_20260915/。 相机核验通过后仍须新采整手数据,不能将旧整手会话改挂新相机参数发布。
  • 新相机完整重采 20260915_154743 的拇指 24/36 单元全部完成,无重扫;小指准备的四候选 均在 26–36 次迭代内收敛,但候选区分证据不足,停在 image_motion_families_not_distinguishable。 随后发现统一 launch 将四型号受保护 Tag 配置中的 detector.decimate=1.0 强制覆盖为 1.5。 已移除这层未声明在产品配置内的覆盖,检测设置统一来自受保护 YAML;不修改候选区分或物理精度门限。 新回归实际展开 ROS 组件参数,修复前四型号均复现 1.5 != 1.0;修复后启动及 runner 的 55 项测试通过。 构建和 git diff --check 通过;旧降采样记录不混入新结果。
  • 原分辨率完整重采 20260915_160040 已从三台运行节点读回 detector.decimate=1.0。 拇指 24/36 单元一次通过,无重扫;小指准备的 104 帧、四候选均在 21–35 次迭代内收敛, 仍因 image_motion_families_not_distinguishable 暂停,未生成本次完整候选 JSON/URDF。 离线回放中两个未分开的 ID4 姿态旋转差 P95 为 18.64°,不能当作同一解; ID4 法线与相机光轴夹角在两解下分别约 6–8°、10–11°,运动中一直接近正视。 已请求保持相机、手掌和 ID3 不动,将 ID4 硬片垫斜后重新采集;这不是硬件故障结论。 review/all_view_detections/ 保留本次后 200.7 秒的三机位检测与 SDK 消息。 其中侧摆期间同时看到 ID1/ID2 的匹配样本仅 9 帧、反馈范围 206–255, 不足以判断整个侧摆行程是否引起被动关节联动。所有精度门限保持原值。
  • 上述修复后的四型号文件链、JSON 重建、恢复凭据和图像求解定向回归共 55 项通过(76.55 秒), 构建通过,git diff --check 通过;142 个原始 URDF/mesh 文件仍与 HEAD 相同。

本次生成测试见 test_all_model_artifact_pipeline.py 和 test_json_urdf_rebuild.py。 O6 最新采集及暂停证据在工作区 calibration_output/O6_RIGHT_001/20260915_160040/review/; 最近一次完成采集并生成候选文件的证据在 calibration_output/O6_RIGHT_001/20260915_150015/review/; 旧外参会话证据保留在 calibration_output/O6_RIGHT_001/20260915_134211/review/。 下方分批测试数字为各次改造的历史记录。

2026-09-15 最后一次全包回归覆盖 1424 项:1408 项直接通过、15 项旧夹具兼容问题、1 项缺少实测记录跳过。 这些失败项已修复,按失败清单重跑 15/15 通过,没有遗留失败;这是全量运行加失败项复测的结果。 O12 使用当前 CAD 假设的完整生成/验收测试另行通过。构建与四型号安装入口检查通过, 142 个原始 URDF/mesh 文件与 HEAD 内容一致。 完整日志和复测日志保存在上述 O6 review/ 目录的 linkerhand_final_full_tests.log、 linkerhand_final_rechecks.log;software_validation.json 汇总了验证范围。

实现与坐标约定

q_CAD = q_output + delta。每个关节自己的实机 baseline 对应 q_output=0, 不把某次局部参考的完整手姿态解释为全手零位。

生产流程仍为统一 runner、ROS 接线、协调器、任务执行器、公共拟合与发布器。 新增型号通过 Profile、SDK Adapter、Tag 观测关系与避让动作接入,没有新增型号专用算法。

职责 实现
显式关节零位配置、已知几何依据 core/domain/profile.py、profiles/loader.py、profiles/validator.py
不可变零位证据及持久化校验 core/domain/reference.py
避让后按声明姿态采集零位 runtime/execution.py、runtime/motion_execution.py、runtime/capture.py
会话/运动版本核对及冻结提交 runtime/coordinator.py
原零位核验后逐任务恢复数据 runtime/joint_resume.py
冻结参考下的相对运动与指令映射 core/fitting/motion_fit.py、command_mapping.py、anchored_mapping.py
明确声明的运动复制、零位迁移及近似来源 core/fitting/motion_transfer.py
独立线性 mimic 近似 core/fitting/mimic_approximation.py
空间零偏及授权 URDF 修正 core/fitting/session.py、spatial_solver/、core/urdf/result_plan.py
v3 序列化、读取和最终文件验收 runtime/artifacts/、core/urdf/measured_acceptance.py、tag_acceptance.py

Start 清空预览后只锁固定参考。运动 Tag 轮到相应关节时才成为必需观测。 Profile 必须声明完整零位指令和非零的最终到达动作;扫描起点不等于 baseline 时先访问零位。 参考保存完整指令、反馈、到达方向、父子相对/公共位姿、图像身份、触发任务和会话/运动版本。 其他手指的非零避让姿态保持在证据中。无法分离同通道多个关节影响的观测在启动检查时拒绝。

同一关节只冻结一次。重复任务、方向、轮次、重扫及恢复不替换参考。 恢复先验证整场指纹,再在各关节声明姿态验证旧安装,保留原参考身份,通过后才导入已完成单元。 原生 rad 输入的第一轮行程统计也随已验证单元恢复,避免跳过第一轮后丢失覆盖依据。 迟到的预览、暂停前观测或旧运动结果不能进入当前参考。 零位稳定性判断使用各机位最近的足量独立帧,短暂异常帧会随新帧退出判断窗口; 原始记录保留,稳定性门限不变。

JSON 关节来自自己的视觉样本,或 Profile 明确声明的对应实测来源;同一 SDK 通道允许绑定多个关节。 接收关节保留自己的 SDK 通道,复制来源的指令/反馈节点及角度,不再拟合。 接收关节没有独立零位帧和第四轮指标,报告标记 independently_measured=false 并引用来源证据。 运动曲线只用前三轮,第四轮分别验证两方向;独立稳态分区用于指令映射训练与验证,约束 baseline=0。 O6 侧摆按已确认策略输出正反向指令曲线;其他关节沿用单值指令曲线。两方向共用同一参考。 验证数据不能用于重新拟合曲线、零位、Tag 安装或基座。 序列化只导出冻结结果;u8 必须实测支持 0–255 才能生成 256 项,rad 只在 input_values 范围内插值。

修正 URDF 保留各型号、本侧的原始 mimic,单独使用时提供近似联动;完整非线性角度由配套 JSON 提供。 urdf_correction v4 明确 preserve_source_mimic,正式标定不再拟合倍率或偏移, 不复制其他手指的 mimic 参数,也不因修正 origin 而重写原 mimic offset。准确 FK 显式使用 JSON 的各关节角。 mimic_envelopes 与实测 motion_range_evidence 分开:只在明确声明的实测范围策略下, 取实测范围与原始联动可达范围的最小包络;禁止突破独立安全界限及默认 CAD 硬界限。 多级 mimic 使用父关节真正的联动可达范围递推,不将其更宽的 JSON 运动范围继续放大。 JSON 重建与最终文件验收均核对原 mimic 元素、最小包络、通道一致性及非线性表。 旧 correction v1、移除 mimic 的 v2、拟合近似的 v3 保持原有重建/读取语义。 既无运动观测也无显式复制声明,或缺少绝对零位来源时,保留相对运动诊断并拒绝完整发布。 旧 mimic 及旧迁移字段不能自动授权 v3 复制,不用默认零偏补全未知几何。 接受原 CAD 末节零位的前提单独记录在 cad_zero_assumptions,不作为独立实测证据; 仅明确列出的关节取 δ=0,保持其自身 origin;O12 的三个额外关节假设见上方当前结果。

验收与消费

发布器从最终文件回读并分别检验:

  1. 最终 JSON 对第四轮原始视觉角度;逐实测关节、逐方向检查;复制表另核验来源与一致性。
  2. 最终 JSON+修正 URDF 的完整 FK 对第四轮 Tag 位姿;显式使用所有被动角。
  3. 相同主动映射配合标准 mimic 的独立近似损失,单独报告。

前两项精度门限仍为角度 MAE ≤1°、P95 ≤2°、最大 ≤3°,Tag 位置 P95 ≤3 mm。 三文件哈希、保护输入、观测覆盖和通道绑定一并检查;拟合失败不自动运动,取消后不能提交。 混合产物的 manifest acceptance 为 measured_and_transferred_json_urdf_v3, 明确记录观测关节、复制关节和 CAD 零位假设。未观测接收关节不具有独立精度结论。

公共读取器和 calibrated_joint_state_bridge 支持 v3 的完整关节映射及历史 v1/v2 原语义。 完整 JSON 回放使用 independent_mimic_angles=True,防止线性近似覆盖实测被动角。 标准 robot_state_publisher 和普通 URDF 网页查看器提供线性联动预览,不据此宣称实测非线性精度。 本轮未修改 MuJoCo、相机时间同步和接触动力学;现有 MuJoCo 脚本尚不能视为支持 v3。

当前型号的发布边界

型号 独立运动观测 声明的运动复制 绝对几何边界
G20 21 / 21 个运动关节 无 五个末节的原 CAD 零位为明确假设
L6 5 / 11 其余三指 MCP、DIP ← 小指对应关节 MCP 零偏明确迁移;五个末节 CAD 零位为假设
O6 5 / 11 其余三指 MCP、DIP ← 小指对应关节 同上
O12 16 / 19 无名指 MCP/PIP/DIP ← 小指对应关节 pinky_pip、ring_pip、thumb_mcp 及末节沿用已确认的 CAD 零位假设

--validate-only 的 independent_measurement_coverage 返回具体关节列表。 配置检查通过表示合同可执行,不能解释为全关节精度通过。 上述运动策略已经覆盖各型号全部运动关节;完整发布仍要求实测来源验收通过、零位有来源且范围合法。 四个产品配置的零位来源均已明确;L6/O12/G20 的原 CAD mimic/限位冲突保留诊断, 收尾保留限位约束内的线性近似;所有 JSON 查表角度和 URDF mimic 全行程都必须处于合法范围内。 现有 Tag 布局没有被补写成未经验证的新布局,新增参考动作也没有标为实机已验证。

原按关节零位改造的软件验证记录

修改前在原工作区测试路径上加载备份代码:582 passed、1 skipped。 固定基线保存在 /tmp/calibration_v3_baseline_ck_q_c99/,包含原包、输入哈希和运行日志。 旧数值回归继续显式使用 v2 语义,未通过重写黄金输出或放宽验收门限消除差异。

最终代码全量回归:614 passed、1 skipped,485.43 秒。 日志:/tmp/calibration_v3_final_verified.log。 包含最后补入的零位异常帧恢复用例;未放宽原有空间求解或精度门限。

已完成的额外检查:

  • colcon build --packages-select linkerhand_calibration --symlink-install 成功。
  • 安装后的 12 个 console entry point 均能导入;四个型号的安装入口 --validate-only 均以 0 退出。
  • 两种合成布局生成的 v3 修正 URDF 另经实际 ROS robot_state_publisher 隔离加载通过。
  • 对比修改前备份,142 个原始 URDF/mesh 文件与 16 个其他相机、Tag、设备配置文件内容不变。
  • 四份 Profile 新增参考动作、输出版本及采集策略,对应 product 仅更新 Profile 哈希。
  • git diff --check 通过;新实施记录随包安装,安装后的 README 文档链接检查通过。

主要回归场景:

场景 测试
四型号先锁固定参考、逐关节仅一次归零,食指在其他手指第四轮后开始 test_joint_zero_runtime.py
起点与 baseline 不同、非零保持姿态、错误到达/禁用通道、跨任务复用、rad 断点行程恢复 test_joint_zero_runtime.py
零位只采自身观测、短暂缺 Tag 丢帧、固定 Tag 缓存、姿态候选不重选冻结参考 test_observation_capture.py、test_o12_observation_resolution.py
任意固定 Tag 安装旋转、冻结零位、非线性被动曲线、第四轮滑移、共零点约束 test_measured_zero_mapping.py
最终非线性组合达标但 mimic 超差、文件篡改、通道错误、迁移/几何缺失禁止发布 test_measured_finalization.py
更换通道顺序、机位名、任务顺序的虚拟型号走同一完整发布链 test_measured_finalization.py、measured_capture_fixture.py
旧格式读取、标准 URDF 授权和结构、READY 后断流、迟到观测、后台反馈、中止与提交竞争 原有相关回归及扩展的 test_calibration_coordinator.py

本轮观测策略扩展

全量回归:700 passed、1 skipped,495.34 秒,日志 /tmp/calibration_transfer_full_verified.log。 全量收集后补入的两项反馈范围一致性用例另行通过(/tmp/calibration_transfer_feedback_domain_verified.log), 本轮共覆盖 702 项通过、1 项缺少实测数据而跳过。未放宽任何空间求解或最终精度门限。 构建成功;安装后 12 个 console entry point 均能导入,四型号 --validate-only 均以 0 退出。 对照当前提交检查 142 个原始 URDF/mesh 文件,内容全部不变;四份产品配置仅更新对应 Profile 哈希。

新增回归见 test_observation_strategy.py、test_motion_transfer.py 和 test_transfer_artifact_contract.py。 覆盖三种观测策略、虚拟型号改名/重排、接收通道、非线性被动表、来源与假设篡改拒绝、 O12 PIP 不补零、末节自身 origin 和各指 CAD 几何保持、机械范围及混合产物读回。 历史全实测合成夹具显式清空新的产品 CAD 假设,继续使用原非零合成真值;未修改其数值期望。 test_measured_transfer_finalization.py 进一步覆盖 O6 及重排后的虚拟 O6: 由 5 个关节的原始合成记录实际拟合,展开为 11 个关节,经过最终文件验收、发布及 manifest 读回; 6 个复制关节保留自己的通道,且没有被制造为独立观测。完整组合通过时仍报告 mimic 近似损失。

合成数据的已知 baseline 几何明确标注 synthetic_fixture_truth,不写入产品 Profile。 完整 v3 非线性回放覆盖合成 G20、O6 拓扑及重排后的虚拟型号;四型号执行器测试不等于四型号都有完整实测回放。 原 G20 两种布局的合成回放均得到:最终关节映射最大误差约 0.079°,完整 FK 的 Tag 姿态最大误差约 0.078°、 最差 Tag 位置 P95 约 0.025 mm;标准 mimic 的姿态最大误差约 5.465°,仍被如实报告。 这些数字用于检验软件是否分开评价非线性映射与线性近似,不是相机或实机测量结果。 O12 实测回放因未提供 O12_REPLAY_RAW 和 O12_REPLAY_REFERENCE 跳过。 没有运行实机动作,没有整只手精度、USB 延迟或多关节接触工况的实测结论。

复现命令:

source /opt/ros/jazzy/setup.bash
source install/setup.bash
PYTHONPATH=src/linkerhand_calibration:$PYTHONPATH python3 -m pytest -q src/linkerhand_calibration/test
colcon build --packages-select linkerhand_calibration --symlink-install
ros2 run linkerhand_calibration calibrate_hand --config src/linkerhand_calibration/config/o6_right_product.yaml --validate-only