Files
linkerhand_retarget_ros2/src/linkerhand_calibration/README.md
T
admin af2dc9c38f 新增 O30 右手行程标定功能包并完善相机恢复
支持三机位 Tag 两端边界标定、滑块示教、顺序避让恢复、同步四指扫描和 JSON 离线复算;修复相机时钟异常导致采集线程退出的问题,并忽略本地行程标定输出。
2026-09-17 12:41:53 +08:00

65 KiB
Raw Blame History

LinkerHand 多型号统一标定

G20、L6、O6、O12 使用同一个产品启动器、在线状态机、采集器、拟合器、标准 URDF 验收和发布器。型号差异来自 config/profiles/*.yaml 和 SDK Adapter,不再调用型号节点。

本包按观测关节冻结实机 baseline 零位,输出实测曲线及明确声明的复制曲线组成的 JSON v3, 以及保留线性 mimic 联动的修正 URDF;报告区分独立实测、迁移和 CAD 零位假设。 保留 CAD 连杆尺寸、轴位置、mesh、惯量。完整运动由 JSON 的全部关节角度配合修正 URDF 计算。 软件仿真通过不等于实机精度通过;当前原始 G20/L6/O12 存在需要确认的 mimic/限位冲突。 本轮实现与验证见 v3 实施记录

启动

cd /home/lxp/projects/linkerhand_retarget_ros2
source /opt/ros/jazzy/setup.bash
colcon build --packages-select linkerhand_calibration --symlink-install
source install/setup.bash

# 无运动、无 SDK 启动的配置检查
ros2 run linkerhand_calibration calibrate_hand --config \
  src/linkerhand_calibration/config/o12_right_product.yaml --validate-only

# 正式启动:自动启动 SDK、相机、检测及标定,READY 后自动开始运动
ros2 run linkerhand_calibration calibrate_hand --config \
  src/linkerhand_calibration/config/o12_right_product.yaml

其他型号替换为 g20_right_product.yamll6_right_product.yamlo6_right_product.yaml。 AprilTag 检测参数直接读取产品指定的受保护 Tag YAML,统一启动文件不再覆盖其中的采样分辨率。 calibrate_g20_right 是同一 runner 的旧命令别名。不要同时运行 GUI、单独 SDK 或其他控制器。 --commands-disabled 只预览,不发送运动;--no-resume 强制新采集。Ctrl+C 中止,不自动快速张手。 重新开始时先安全回基准,仍必须确保现场没有障碍物。

JSON 到修正 URDF

统一收尾顺序为:拟合与独立角度验证 → 保存标定 JSON → 重新读取 JSON 修正原始 URDF → 文件回读与整链验证 → 发布。 JSON 的 urdf_correction 保存原始 URDF SHA256 和必要的关节字段修正,重建不依赖内存拟合对象。 原始 URDF 不被覆盖;关节拓扑、字段授权、完整 mimic 范围和 JSON 查表范围在文件安装前检查。 新 v3 使用 urdf_correction.schema_version: 4joint_angle_source: calibration_json 并记录 mimic_policy: preserve_source_mimic:所有型号保留本侧原始 <mimic> 元素, 不改来源、倍率、偏移或省略的默认属性;正式标定不再拟合 mimic 参数。JSON 保留各关节 非线性曲线及方向分支,精度验收使用 JSON 提供的全部关节角。父子链、轴和连杆不变, 被动关节与驱动关节仍绑定同一 SDK 通道,所有数值编辑仍按 Profile 授权。 mimic_envelopes 单独记录基础范围、原始联动可达范围和导出范围;在明确的实测范围策略下, 导出范围取二者的最小包络,实测范围证据与查找表不变。未声明该策略的 CAD 限位,以及 另行声明的硬件安全限位,仍是硬边界;冲突时报告具体关节和所需范围,不压低倍率或放宽容差。 关节 origin 按标定零位修正后,原始 mimic 作为输出坐标中的近似预览,不作为准确物理约束。 旧 correction v1、移除 mimic 的 v2、拟合线性近似的 v3 产物继续兼容读取与重建。 导出规则单独版本化,不改变采集配置指纹或旧原始记录,已发布产物不能原地覆盖。

已生成的 JSON 可单独重建对应 URDF,无需相机或 SDK:

ros2 run linkerhand_calibration rebuild_calibrated_urdf \
  --config src/linkerhand_calibration/config/o6_right_product.yaml \
  --calibration-json /path/to/calibration.json \
  --output /path/to/new_corrected.urdf

输出路径必须尚不存在。没有 urdf_correction 的历史 JSON 不会被猜测或自动补全。 同一 JSON 和源 URDF 可逐字节重建相同修正文件;发布器会核对此项一致性,读取器要求相应验收记录。 历史无修正元数据的已发布文件继续沿用原有读取规则。 完整文件验收失败时保留候选 JSON/URDF 和诊断,便于复核;只有 release_manifest.json 和成功发布指针才代表通过,运行时读取器拒绝把未发布候选当作合格标定。

Profile SDK 位置通道 扫描任务 Tag 输出格式
G20/right/g20_right_19/v1 20 16 19 unified v3256 项指令查表
L6/right/l6_right_8/v1 6 3 8 unified v3256 项指令查表
O6/right/o6_right_8/v1 6 3 8 unified v3256 项指令查表
O12/right/o12_right_16/v1 12 11 16 unified v3rad 输入节点查表

任务数、主动关节数和实测关节数不必相等。一条运动可观测多个主动/被动关节。 观测策略由 measurement.transferred_motion_sources 声明,目标关节指向直接实测的来源关节:

型号 独立采集 复制的运动映射
O6/L6 拇指、小指 食指/中指/无名指的 MCP、DIP 分别复制小指对应关节
O12 拇指、小指、中指、食指 无名指 MCP/PIP/DIP 分别复制小指对应关节
G20 / 全关节模式 每个关节 不声明复制关系

复制保留接收关节自己的 SDK 通道、URDF 名称与 CAD 几何,不要求接收关节另贴 Tag 或采集零位帧。 复制参数不等于接收关节独立实测;接收关节仍须有明确的绝对零位来源及合法机械范围。 zero.cad_zero_assumptions 显式记录“实机 baseline 与原 CAD 末节零位一致”的使用前提, 相应 DIP(及拇指末节 IP)δ=0、自身 origin 不改。这是假设,不是视觉测得的绝对 CAD 朝向。 按用户确认,O12 的 pinky_pipring_pip 和主动 thumb_mcp 也采用原始 URDF 零位作为 baseline 假设。 运动曲线仍按实际观测/明确复制关系生成,报告标记 assumed_source_cad_zero,不称为独立实测零偏。 正式入口在驱动硬件前检查运动覆盖及零位来源,缺失依据时直接指出缺项,不采完才发现无法发布。 启动检查分别列出 unmeasured_joints(未独立观测)、unresolved_motion_joints(也未声明复制) 及 missing_baseline_geometry。旧迁移字段单独存在不会自动授权新的 v3 曲线复制。 本次沿用现有 Tag 和避让布局声明延后零位动作,没有把新增动作或缺失 Tag 布局标为实机验证通过。 每任务的运动模型采集四个往返,共八趟单程:前三轮训练,第四轮独立双向验证。 四个产品配置统一使用 command_capture_mode: separate:这八趟均为完整连续扫描,随后另做一组稳态映射训练往返和一组独立验证往返。 主扫描不逐点停顿;稳态点直接执行移动、稳定反馈、新图像采样,不重复等待两次到位。 稳态训练点由前三轮连续数据生成 command_sampling_plan:在原节点网格上寻找最少的 保留节点,各关节、各方向的预测曲线插值变化均不超过 0.25°;端点、基准、中间支撑点及 显式增加的非线性节点保留。缺少完整训练数据或曲线噪声过大时使用原网格。 这份计划只决定停在哪里,每个保留点的静态角度仍由新图像独立测量,不用连续运动角度 冒充静态角度。独立验证网格保持原样,不参与节点选择。计划及训练来源哈希随原始记录保存, 拟合、离线回放和断点恢复均重新核验;缺点、篡改或最终误差超限仍禁止发布。 上一段已经完成到位/稳定检查且下一准备目标完全相同时,可复用一秒内、反馈未变化的 完整保持姿态,省去重复准备等待;有实际位移、反馈变化或记录过期时执行原准备流程。 原训练网格每方向通常为 9 点,并保留行程内的 baseline;O6 小指额外包含 239,共 10 点。 新流程在这份网格上按上述规则精简训练停点。 验证使用交错点加端点(O6 小指包含 247),两类数据不混用。 日志中的运动轮编号为 0–3,独立指令映射训练和验证编号为 4、5;界面分别显示“稳态指令映射训练/独立验证”。 其他采用 interleaved 的配置仍在四轮各方向中穿插稳态点,并使用三轮稳态训练、第四轮稳态验证。 行进图像用于旋转/几何拟合,停稳后的新图像用于 SDK 指令映射;同一张图像不会复制成两类样本。 每点至少 3 个稳定同步图像,轨迹结束后最多等待 2 秒,不能用精确指令/反馈相等判断稳定。 关节零位冻结另需至少 10 张独立有效图像。acquisition.joint_zero_timeout_seconds 单独限定首张合格零位图像后的采集窗口,四个产品配置均为 5 秒;几何求解后的跟踪恢复仍最多等待 2 秒。 反馈不稳定会清空当前零位候选,但不会重置总采集期限;图像数量、1° 稳定性及分支证据要求不变。 稳定窗口使用每条新收到的 SDK 反馈,按消息时间戳去重,不再只取控制定时器看到的最新一条。 定时器延迟不能制造“独立反馈不足”;期间真实运动仍会使窗口失效。 候选清空时的 steady_capture_reset 保存反馈窗口和丢弃样本数量,便于区分抖动、断流和调度问题。 零位样本的 capture_timing 记录接收、历史快照和提交时间,用于检查处理延迟。 图像数量未齐时先返回缺样结果,不在控制定时器中反复校验全部姿态。 四轮及稳态采集完成、数据落盘后关闭图像采集;拟合只使用已保存的相机和参考证据。 之后的新图像和相机消息不再改变拟合输入,也不触发采集暂停;SDK 反馈、故障保护及操作者中止持续生效。 拟合或文件验收失败结束为 FAILED,保存 failure_diagnostic.json 中的失败阶段、原始数据路径和异常堆栈。 这类失败应先离线复算,不能仅因状态失败就重复四轮运动。运动采集期间的固定参考检查保持生效。

O6 的顶部 ID7 与正面 ID2 实际固定在同一末节,Profile 将 ID7 明确绑定到 rh_thumb_distalthumb_yaw 是它承担的侧摆观测角色,不是其安装连杆名称。整链回放包含该 Tag 上游的侧摆、弯曲和末节角度, 保持关节沿用记录的到达方向。新绑定改变了 Profile 哈希,旧会话不会被自动复用为新配置的通过结果。

固定 Tag 的 ID 解码成功不代表边框完整。独立的视觉边框检查进程按完全相同的图像时间戳配对整流图像与检测结果, 检查当前检测四边的黑白边界支持;部分遮挡产生的畸变四边形会被记为 detection_quality_rejected, 不能进入 PnP、关节样本或固定基准移动计数。配对缓存有界,不用邻近时间的图像替代。 检查使用当前检测四边形,清晰 Tag 发生真实位移仍进入原有 5 px/连续 10 帧保护。 控制进程只订阅 detections_checked 小体积观测;三路大图像的传输、配对和边框处理不占用 SDK 控制节点的回调队列。 ROS 宿主将准备几何求解和逐帧图像角度求解交给独立计算进程,避免与 SDK 反馈争用 Python 执行资源。 准备请求仍限时 120 秒;逐帧请求最多三个在途,超时或工作进程不可用的图像不生成有效样本。 工作进程只接收不可变图像/模型输入,没有 ROS 或 SDK 控制接口。返回后仍检查当前图像、跟踪版本和会话版本, 不能将迟到结果提交到下一运动段。motion_solver_diagnostic.jsonl 记录请求、计算耗时、线程 CPU 时间及提交结果。 当前任务需要的固定 Tag 无有效观测时,仍按原采样规则等待或暂停;其他机位的遮挡不误报为移动。 稳态点缺失与连续扫描不足统一在方向结束处理,最多同速重扫一次。 首次关节零位准备、避让及收尾属于独立的必要动作,不计入四个正式往返;不会每轮重新锁定零位。 分段加减速和停点仍有耗时,八趟行程不等于耗时恰好降为原来的三分之一。 新产品会话记录 capture_schedule_version=unified_schedule_v3_separate_mapping;旧 interleaved 配置仍记录 v2。 采集模式或保护配置不同的旧断点不与新流程拼接, 历史原始数据仍保留原语义供对应配置离线读取和分析。训练/验证身份与精度阈值保持不变。 字节拟合不再改写原始 command/feedback,也不将接近端点的读数伪装成 0/255。

O6 右手拇指短程诊断

当前 O6 实物在 2026-09-16 复测的 Tag 黑色码区边长为 16.5 mm,棋盘格边长仍为 27 mm。O6 Profile、检测器配置和标定节点配置已同步采用 0.0165 m,产品配置记录 对应文件指纹。该数值属于这套实物标记,不是所有型号或所有打印批次的统一尺寸。 更换标记时应实测黑框,并同步维护上述配置;旧 16 mm 会话保留原尺寸记录,不作为 新尺寸的恢复断点。Tag 尺寸修正不改变 JSON 中非线性运动曲线与修正 URDF 的配合方式。

本套 O6 的 2026-09-16 实采数据已生成通过验收的产物,目录为 calibration_output/O6_RIGHT_001/20260916_134154_source_mimic_165mm/ latest_partial_passed 已指向该目录。JSON 和修正 URDF 应配套使用;当前 8 Tag 布局 直接测量拇指、小指,其余三指沿用配置中的曲线迁移关系。采集完成后的在线收尾曾发生 系统 OOM,最终产物由完整原始数据的正式离线流程验收;日志读取内存优化另经独立进程 验证,不能将此记录描述为在线全过程无中断。详细证据见 CALIBRATION_PIPELINE_REVIEW.md。 此目录用同一份原始数据重新验收,5 组 mimic 的完整元素与原始 URDF 一致: 拇指倍率 1.86、其他四个末节 0.89,偏移均为 0。 11 个关节的指令/反馈查找表及几何零位保持不变;仅拇指 IP 的 URDF 下限从 0.115187° 调整到 −0.183816°,覆盖原始联动所需范围,实测范围证据仍为原值。 拇指 SDK 指令 0 时,IP 的 URDF 预览为 63.68°JSON 为 66.35°; 这是线性预览与实测非线性的区别。完整 JSON+URDF 和独立原始图像继续满足原精度门限。 20260916_123347_json_driven_165mm/(移除 mimic)及 20260916_124718_mimic_json_165mm/(拟合倍率和偏移)为历史版本,已被本次发布替代。 旧目录均保留;始终使用同一 manifest 指定的 JSON、报告和 URDF。

定位 Tag 姿态跳变时,可沿用相同产品配置添加诊断选项:

ros2 run linkerhand_calibration calibrate_hand \
  --config src/linkerhand_calibration/config/o6_right_product.yaml \
  --diagnostic-thumb

诊断选择原 Profile 中的 thumb_yaw_topthumb_pitch_ip_front,各采一次往返, 观测拇指 CMC yaw、CMC pitch、IP 三个关节。保留原速度、完整扫描行程、准备/避让、 固定基准锁定、按关节尝试零位确认和安全收尾;其余手指不执行扫描任务,但开始时仍会安全回到 baseline。 本次诊断只启动正面和顶部机位:开始时一次锁定正面 ID0、顶部 ID6;运动观测使用顶部 ID6/ID7、正面 ID0/ID1/ID2。 侧面不参与本次诊断,ID3 缺失或双解、侧面相机未连接均不会阻塞;因此无需为拇指诊断调整 ID3 或改动相机外参。 范围由本次全部任务的主/辅观测依赖统一确定,启动、ROS 订阅、设备检查、基准锁定、指纹和进度使用同一范围。 它不会随当前任务切换而重新锁定;正面和顶部的配置变化、固定基准漂移及所需 Tag 数据不足仍按原策略处理。 正式全手标定继续使用 Profile 声明的全部机位和固定基准,外参文件及保护输入的完整校验保持不变。 “短程”缩短的是任务和轮次数,不截断需要复现异常的运动行程。

本模式跳过稳态映射采样与三轮训练/第四轮验收,收尾后显示 DIAGNOSTIC_COMPLETE 并正常退出。诊断按当前运动 Tag 的原始图像覆盖推进,持续姿态歧义不阻断原始候选采集; 缺少当前运动 Tag 角点或同步反馈导致方向覆盖不足时,仍最多同速重扫一次,严重故障和中止行为不变。 归零准备证据为 unresolved 时,仅诊断模式允许继续收集原始观测:必须先到达 Profile 声明的零位指令, SDK 反馈稳定,当前运动 Tag 具有足量、带会话和运动版本的图像,才保存独立的 diagnostic_joint_zero_attempt。 它表示访问并观察了声明姿态,不表示已建立关节零位;不写入 zero_references,方向切换及重扫不重新归零。 该组关节后续仅保存原始角点和候选,即使普通跟踪恢复,也不能生成 joint_sample 或伪造有效角度。 缺少零位原始图像、SDK 不稳定、固定基准移动及已冻结参考冲突仍按现有策略暂停;正式标定仍须先确认分支和冻结零位。 会话目录保存 raw_samples.jsonl(含角点和 IPPE 候选)、实际成功冻结的关节零位与 diagnostic_capture.jsondiagnostic_observation_frame 在零位尝试和扫描阶段单独记录各帧候选,包括未接受的姿态,不能当作关节角度样本。 报告 unit_quality 分别记录原始观测覆盖与 accepted_pose_quality 可信姿态覆盖; joint_zero_statusmissing_joint_zero_references 列出各关节真实零位状态,motion_initialization_summary 保留各候选的关节残差。 进度显示原始观测覆盖,并单列可信姿态数量和未确认关节;暂停时保持“已暂停”,不会被零位动作名称覆盖。 可信姿态不足会在退出时明确提示。相邻帧姿态跳变和图像身份用于定位问题,诊断完成不代表标定精度通过。 每方向的 observation_timing 分别统计原始/可见/同步图像数、缺命令与缺反馈图像数、最大图像及同步观测间隔; 缺命令与缺反馈可以发生在同一帧,不能相加当作总丢帧数。少于两帧的间隔记为 null。 方向失败时这些信息同时进入暂停诊断。反馈跨度 100% 仅表示两端都有观测,不代表中间没有缺口。 原始帧的 timing 保存回调接收、历史快照及最新命令/反馈时间戳,用于区分消息延迟与状态匹配问题; 接收到快照的时间包含线程调度和锁等待,不能单独解释为写盘耗时。未匹配反馈时同步误差为 null,不会显示为 0 ms。 可额外加 --record-bag 保存现有录制话题;仅检查入口与配置可加 --validate-only,不会启动硬件。

诊断始终完整新采,不复用断点,不拟合或发布标定 JSON/URDF,也不更新发布指针。 原始会话带有诊断标记,正式离线拟合和断点发现会拒绝将其用作正式标定数据。 当前选项只开放给 O6 右手;其他型号仍走原来的正式流程。原始 URDF、Profile 和设备配置无需修改。 诊断任务预设在 profiles/diagnostics.py 声明,运行时只选择既有 Profile 任务,复用公共调度与采集组件。

诊断采集完成后,runner 先关闭相机和 SDK 进程,再自动运行公共图像几何分析,生成独立的 diagnostic_analysis.json 和可直接阅读的 diagnostic_analysis.md。耗时优化不进入实时状态锁或相机回调。 分析按任务、机位、方向和尝试分别保留原始图像身份,比较所有候选族的三维 PnP 残差、深度误差比例、 图像域关节链拟合以及隔帧和前后区间验证。验证时几何参数冻结,只从当前图像反解几何角度,不使用 SDK 角度映射。 图像误差小不等于毫米精度通过,优化收敛也不代表分支唯一;分析结果不能写回跟踪器、零位或正式标定产物。 未采到的方向和旧会话缺少的归零前证据会明确列出,不拼接不同会话,不使用第四轮重新拟合。 图像分析失败或被取消时,已完成的采集及原始文件仍保留,不会改标成 SDK 故障。

已保存的数据可直接离线复算,无需启动相机或重复运动:

ros2 run linkerhand_calibration analyze_calibration_capture \
  --config src/linkerhand_calibration/config/o6_right_product.yaml \
  --raw calibration_output/O6_RIGHT_001/20260914_192637/raw_samples.jsonl \
  --output calibration_output/O6_RIGHT_001/20260914_192637/image_review

这些修改解决诊断采集被未确认零位阻塞及报告不完整的问题。现有真实 IP 数据仍有分段几何不一致, 正式分支门槛、拟合器、URDF 修正和独立精度验收保持原要求;软件回归不能替代实机精度结论。

O6 右手 yaw 方向差保持诊断

需要区分“到达指令后仍在缓慢稳定”和“保持后仍存在方向差”时,使用独立选项:

ros2 run linkerhand_calibration calibrate_hand \
  --config src/linkerhand_calibration/config/o6_right_product.yaml \
  --diagnostic-yaw-hold

此模式仅选择 thumb_yaw_top,只启动顶部机位,使用固定基准 ID6 和运动 Tag ID7。 每次 Start 都重新采集会话基准并尝试确认 yaw 零位,不复用旧会话;同一会话的两个方向共用首次冻结零位, 不会分别归零。未能确认零位时按诊断规则保留原始观测,并明确标记无法生成可信角度。

在原速度和行程下执行一次往返。每个方向扫描后先回到该方向的起点,再沿同一方向访问三个保持点: 递减方向为 255 → 192 → 128 → 64,递增方向为 0 → 64 → 128 → 192。 每个保持点在指令轨迹完成后至少保持 10 秒,两方向合计六次保持;反馈确认可能使实际等待稍长。 保持过程继续记录图像角点、候选、实际指令和同步反馈,用于比较到达后的角度随时间变化及双向最终差异。 保持阶段的原始观测单独标记,不计入连续扫描的覆盖统计;遮挡、设备故障、中止及安全收尾仍沿用公共策略。

--diagnostic-yaw-hold--diagnostic-thumb 互斥,不能同时使用离线拟合、发布或禁用运动选项。 可加 --validate-only 只检查入口与配置,或加 --record-bag 留存现有录制话题。 结果保存在 raw_samples.jsonldiagnostic_capture.json,记录保持指令及时间; 不拟合或发布标定 JSON/URDF,不形成正式断点,也不算精度验收。 本诊断用于查清实机稳定时间和方向差,不改变最终单值 angle_rad 的验收要求。

启动前相机检查

--validate-only 检查相机配置文件、序列号、内外参指纹和已有外参质量记录,不采集现场图像。 正式启动还检查实时 CameraInfo 的宽高及 K/D/R/P 是否与受保护外参中的记录一致; 参数不匹配、无效或消息过期时不能开始,并显示具体机位和原因。

配置匹配不等于现场内外参有效。 当前只有手上的 Tag,各机位使用不同 Tag, 没有声明跨机位共视或已知的 Tag 间几何关系;手掌和贴纸又允许在开始前移动、重贴。 因此光学内参与相对外参的现场有效性记录为 unverified,不能用重新锁定掌心 Tag、 较小的 PnP 残差或未变化的配置哈希代替实测,也不能由 Tag 位置变化断定相机被碰。

READY 只表示设备与消息满足现有开始条件。显示上述能力提示后沿用现有启动行为, 不要求各手指 Tag 可见,不增加人工确认或新的暂停条件;READY 不表示物理相机参数已验证通过。 会话目录中的 camera_preflight.jsonraw_samples.jsonl 保存检查结果与依据, 开始前仅在结果变化时记录,接受 Start 前再检查并冻结一次;后续状态中的检查结果指这次启动检查。 单凭这些记录不能判定是否需要重新标定。expected_cameras 是配置期望身份,不是现场实测结果。

镜头、焦距或成像设置变化后应重新核验内参;相机之间相对位置变化后应重新核验外参。 未来同一个物理 Tag 被多个机位共同看到时,可以检查相对外参一致性,但当前尚未实现该能力。 本次检查不修改 Profile、SDK 或原始 URDF。

基准、避让与恢复

  • 开始前可以调整手掌、相机和 Tag;预览数据不会成为正式参考。
  • 开始后清空预览缓存,先安全回基准,再为每机位固定 Tag 收集至少 10 个有效帧。 缺少基准时保持并显示缺少的 ID,不因为等待时间长暂停。
  • 锁定后禁止人工移动手掌、相机、支架、Tag 粘贴位置;程序驱动关节和避让仍是允许的。
  • 全手 baseline 时只要求固定参考可见,不要求四指运动 Tag 同时可见。 每关节在 motion.joint_zero_references 指定任务前,按声明的到达姿态和避让分组回到自身 baseline, 反馈稳定后收集至少 10 个稳定视觉帧,冻结 JointZeroReference。此后方向、轮次、重扫均不重新归零。 例如 G20 食指开始前,已完成四轮的其他手指可以保持非零避让;这些完整指令和反馈写入参考。 task.start 不代表零位,例如侧摆 baseline=127、扫描起点=255,会分别执行两种姿态。
  • 相机相对位置改变时须重新确认/标定外参;仅重新锁定掌心 Tag 不能替代外参标定。
  • 固定 Tag 被避让遮住时可用已锁定参考继续采集;软件不能保证检测完全不可见对象的移动。 运动 Tag 的刚性和几何一致性仍须通过最终独立验证。
  • 采集断点版本为 unified_engine_v8_all_view_images;另以 pose_tracking_policy_version=confirmed_branch_v8_training_model_selection 记录姿态分支策略。缺少此字段或版本不同的旧断点不能自动或手工恢复到新会话,历史文件保持原样供离线诊断。 首先检查配置哈希与整场固定参考;各关节轮到时,在同一完整保持姿态核对原零位与父子 Tag 位姿 (5 px、2°、5 mm),通过后才导入对应已完成扫描单元,并保留原零位记录。 整场参考不匹配则放弃旧数据;逐关节安装验证失败则暂停,禁止在旧样本上新建零位拼接。 排除原因后用 --no-resume 开始新会话。旧会话文件不修改。 旧日志的读取、凭据回放与单元质量检查在实时回调启动前完成,不占用运动/反馈回调。 现场验证通过后,每个控制周期最多导入 64 条历史记录;全部落盘后才跳过对应单元。 导入期间持续处理反馈、固定基准监测和中止,原 1 秒反馈超时保护不变。

连续图像、候选和样本按图像批量追加并刷新到系统缓存,避免持有控制状态锁逐条 fsync 阻塞反馈和运动定时器。 固定参考、关节零位、方向完成和暂停记录仍强制同步到磁盘;拟合、报告哈希、发布和关闭前另有同步屏障。 断点只复用通过且持久化的完整方向,意外断电时未完成方向不能拼入旧数据;同步失败不得发布。 此存储策略不改变 50 ms 图像/状态配对门限、方向覆盖门限、拟合公式或精度验收条件。

O6/L6/G20 标定读取 SDK 的 *_hand_state_timed 反馈接口;位置与每个通道的 CAN 接收时间 一同保存。G20 五指的分帧反馈分别插值到图像时间,重复发布的缓存不能刷新反馈超时或增加 稳态独立样本。标准 JointState 仍可供其他程序使用,其时间戳保留测量时间。 sdk_feedback_sample 原始记录支持复查时间配对;旧版本只保存发布时间,不能事后补成真实 CAN 测量时间,也不能与新版断点混用。O12 继续使用独立 HCAN 同步读取接口。

共同坐标约定:q_CAD = q_output + delta;每个关节在自身冻结 baseline 时 q_output=0。 局部零位通过采样时的完整保持姿态和运动链进入同一掌部参考。固定 Tag 不提供未知末端连杆的 CAD 朝向。 zero.known_baseline_geometry 可声明有独立依据的 CAD 角度、误差界与来源;不能填入猜测的 0、旧 mimic 或 Tag 安装偏角冒充几何证据。相应 origin.rpy 和限位修正仍须在 Profile 中明确授权。

O12 避让:小指和无名指到 Profile 的最大弯曲指令;标定食指前,中指 MCP/PIP 到最大弯曲指令, 中指侧摆为 0 rad。到位采用完成轨迹、正确方向、至少 80% 请求位移和短时稳定, 不要求反馈数值精确等于命令。SDK 指令最大值与实测 URDF 角度不是同一个量。

O12 使用 HCAN device 0/channel 0 的已锁定 vendor Python wheel,不检查 can0。 连接参数由本包的 config/o12_sdk.yaml 保存,并通过产品配置中的 SHA256 校验; 不依赖第三方 SDK 子仓库中未提交的示例 YAML 修改。 只读反馈桥可在第一次运动命令之前取得状态,不会发送零位命令来“激活回读”。 POSITION 和活动故障由 SDK 确认;无法单独回读温度时使用 SDK 错误位过热保护,不伪造温度。 字节 SDK 当前没有独立硬件故障遥测,状态会明确注明这一能力边界。

哪些情况暂停

实时仅因人工中止、竞争控制器、活动硬件故障/模式错误、真实失联、反馈超过 1 秒未更新、 物理越限、明显运动要求下连续 2 秒无推进、已锁固定基准连续 10 帧漂移超过 5 px 而暂停。 开始后相机投影参数变化也会拒绝继续使用混合坐标数据。 当前关节持续缺失零位观测会暂停并列出关节、机位和所需 Tag;尚未轮到的手指遮挡不阻塞当前任务。 零位和稳态点的反馈稳定窗口以最新独立反馈为终点,并保留跨越窗口起点的一条反馈。 控制定时器的调度抖动或重复读取同一反馈不会清空稳定观测;真实反馈波动或反馈过期仍会使当前窗口失效。 零位仍需至少 10 帧且姿态波动不超过 1°;拟合验收门限不变。

短时 Tag 丢失、PnP/同步失败、低检测率/低反馈 Hz、正常跟随滞后、固有耦合和非目标小幅运动 不会实时中断轨迹。无效视觉帧直接丢弃。

所有型号的独立 Tag 姿态筛选共用 core/geometry/pnp.py 中的近似同误差阈值,默认 0.03 px。 ROS 和离线采集使用相同默认值,型号 YAML 不重复声明该值。1.5 px 是图像质量拒绝阈值, 不能用它判断两种姿态是否同样可信;仅在误差差值不超过近似同误差阈值时使用时间连续性。 分支容差覆盖值必须有限、非负且小于图像质量拒绝阈值。

独立 Tag 首次接受姿态前,需要默认至少 5 帧、跨越至少 0.1 秒的图像证据确认。 近似同误差的候选只有旋转相差不超过 1°、平移相差不超过 1 mm 时才视为同一姿态;这些参数统一定义于 core/geometry/tag_pose/parameters.pyROS 通过 pnp_branch_confirmation_framespnp_branch_confirmation_minimum_secondspnp_branch_equivalence_deg 加载,离线采集使用相同默认值。 这只是图像分支的一致性确认,不能把多帧低重投影误差视为物理姿态真值。

以下独立图像跟踪规则保留用于预览和历史兼容;新会话的运动 Tag 首次零位还必须通过后述公共运动几何确认。 参考冻结前,明显更优但与历史姿态发生大跳变的新分支也必须经过确认,过渡帧不进入零位或拟合样本。 运动 Tag 参考冻结后,若原分支仍有连续、图像质量合格的候选,但另一候选的重投影误差明显更低, 该帧标为 pose_branch_ambiguous 并丢弃,内部保留原分支轨迹等待图像证据恢复; 不能仅凭另一候选连续数帧得分更高,就判定冻结参考损坏。不会切换分支、重建零位或把歧义帧送入拟合。 如果持续不存在符合原分支连续性约束的候选,仍在确认后暂停;长期失联后无法确认分支一致时也会明确拒绝。 正式标定方向结束仍检查可信样本、覆盖与稳态点,最多同速重扫一次;诊断模式另报原始观测与可信姿态覆盖。 需要更换分支或重新安装 Tag 时,必须重新开始新会话, 不能把旧冻结参考与新分支数据拼接。原有 0.03 px 容差、35° 跳变、75° 倾角及 1.5 px 重投影质量阈值保持不变。 真实角点噪声、采集时序和最终运动精度仍须通过独立实测验证。

首次零位前的公共运动几何确认

新生产会话复用 Profile 已声明的最后一段归零准备运动,在 zero_approach 中保存父子 Tag 的原始候选, 到 joint_zero 后先确认分支,再采集原有的稳定零位帧。没有增加扫描轮次、额外试探动作或型号专用求解器。 几何确认与 SDK 反馈稳定分别检查;几何通过不会提前开启零位采集窗口。 计算在状态锁外进行,期间持续接收 SDK 反馈并保持声明姿态;计算本身有 120 秒上限。 几何确认后,先在原有 2 秒观察窗口内等待恢复后的首个有效零位样本,再用 joint_zero_timeout_seconds 限定的独立窗口采齐稳定帧(字段默认 2 秒,四型号产品配置均为 5 秒)。 窗口不会无限延长。正式采集中,每组尚未冻结零位的关节最多自动恢复一次:可恢复的图像 准备失败重新执行该组原有的归零准备路径并采集全新图像;几何已经冻结但零位图像不足时, 保持原姿态和模型,只重新开启一次零位图像窗口。原始失败和恢复动作写入 joint_zero_recovery。 已冻结的零位、部分已经提交的多视角几何、保持姿态变化、设备故障不重新拟合;恢复后仍 不足时明确暂停,保留原有反馈、固定基准保护及用户中止能力。 中止、暂停或运动版本改变后,迟到的计算结果不能提交。 前面的避让段、预览、训练扫描和第四轮不参与首次确认。准备段同时核对其他通道的完整保持指令及反馈; 开始后不能改变相机、固定基准或 Tag 安装关系。

生产入口 core/geometry/tag_pose/production_image_motion.py 枚举 IPPE 候选家族, 使用父子转动副 R = exp(theta × axis) R_reft = pivot + R × mount 拟合原始角点的重投影残差。 稀疏线性子问题使用 atol=btol=1e-10,避免近似计算误差导致外层耗尽迭代预算; 外层收敛条件、默认 150 次迭代预算和各项精度门限保持不变,记录每个候选的求解状态及迭代次数。 父 Tag 本身运动时联合求解整条已声明的观测链;已冻结的上游几何不会重新拟合。 角度来自当前图像,不使用 SDK 到角度的先验、旧 mimic 或 CAD 零位假设选择分支。 单帧 PnP 深度噪声较大时,不再将其三维平移估计直接作为关节运动的真值。

当源 URDF 中两个转轴相邻且平行,并且观测 Tag 实际固定在对应连杆上时, profiles/observations.py 提取零位修正后仍不变的平行关系和垂直轴距。 CadImageHingeBundle 消去三个多余几何变量,使准备模型遵守最终 URDF 保留的几何; 不修改原骨长/轴向、不增加权重或 CAD 零位假设。不满足条件的观测链使用原模型。 新冻结模型保存源 URDF 哈希和约束,最终处理从源文件重新推导核验;旧模型保持可读。 修改前的原始图像对照实验及适用边界见 流程评估

准备段至少需要 24 帧,训练与留出图像身份分离,默认各最多 72 帧。 训练帧取交错序列,未进入训练的图像才可留出;不因训练帧增加而复用验证图像。 局部几何可观测性检查计入逐帧角度的不确定性,轴线与转心的不确定度界分别不超过 3° 和 3 mm。 候选只按训练图像的平方误差选择;留出数据不用于选优,所选候选验证失败不能改选另一候选。 候选家族还须在准备段留出图像上区分:按运动角度分组,比较与拟合一致的平方误差损失, 差值为 alternative_rms² - (candidate_rms + 0.03 px)²,保留原像素容差。 同时通过单侧均值和符号秩检验,再对完整比较集采用 Holm 逐步校正,名义族错误水平保持 0.01。 image_model_selection.py 统一负责候选选择、同帧角色对齐、损失比较和多重校正; 诊断记录原始/校正后概率、选择策略和实际源 URDF 约束,失败时也保留这些证据。 缺失、未收敛或无法评估的替代家族不能被当作已排除;几何等价的家族才可合并。 这些检查是图像几何的条件性证据,不是独立毫米精度证明,不能替代第四轮 JSON/URDF 验收。

确认成功后冻结轴线、参考旋转、转心、安装关系及其来源图像。 image_motion_projection.py 后续每帧只反解当前图像角度,不重新拟合这些几何参数。 保留 1.5 px 重投影、75° 倾角及单帧角度不确定度检查;信息不足或角度仍有歧义时丢弃该帧, 按现有方向覆盖和最多一次同速重扫处理。35° 连续性和失联后的确认仍由公共 tracker 检查。 Tag 中心接近转轴本身不再是必然失败条件,是否可解取决于全部角点提供的几何信息。 准备段暂时缺帧或候选不确定时,每组尚未冻结零位的关节最多沿原归零路径补采一次; 已确认几何但零位图像短缺时,保持几何和姿态,只重新打开一次图像窗口。 两个恢复方式共享一次额度,保存 joint_zero_recovery,不延长成无限重试。 多机位已有部分几何冻结、保持姿态改变、设备故障和诊断采集不会触发重新拟合。 恢复后仍无法确认时不冻结零位、不发布;已有零位和旧数据不能被重新解释后拼接。

motion_branch_observation 保存准备段图像、角点、候选及 SDK 状态;motion_branch_initialization 保存各假设残差、 冻结模型、来源图像和内容哈希。新姿态来源为 image_constrained_current_image,保留原始 IPPE 候选, 同时记录 frozen_image_modelimage_model_sha256。每个运动 Tag 的 motion_evidence_id 绑定首次证据, JointZeroReference.branch_references 将它与零位、机位和分支版本关联。 正式读取与收尾校验这些关联,缺少证据、篡改来源或删除表头均不能降级为旧图像确认; motion_branch_provenance.json 只证明证据链完整,不是精度证书。 恢复时共享来源只导入一次,关节零位及历史验证记录在该关节实际验证后导入,支持恢复期间再次中止。 正式回放用同一冻结模型复算同帧角点,核对父链、相机、Tag 尺寸及实际提交姿态,不重新拟合模型。 旧 confirmed_branch_v1/v2/v3 及 v4 三维候选约束记录仍按原语义读取,不能自动升级或恢复成新 v5 证据。 motion_evidence.py 的旧三维筛选入口保留供历史兼容和诊断。这里 v5 仅指姿态策略,最终标定 JSON 仍为 schema v3。

真实 O6 20260914_175815 的旧三维筛选只读回放中,yaw 与 pitch 分别存在唯一通过几何条件的家族; 但 pitch→IP 的四种父子组合全部超差,IP 转心 RMS 约 4.9–11.4 mm,完整拇指链仍为 unresolved。 这批图像来自旧零位冻结之后的单轮诊断扫描,只能用于反事实回归,不能代替新准备段证据、 覆盖旧零位或证明 JSON+URDF 精度。 可复算摘要见本次分支修复回放, 测试夹具同时保留“yaw/pitch 可分”和“IP 不得强行通过”两类断言。 后续 20260914_200044 的图像求解回放使用其真实归零准备段冻结模型,后续 838 帧可复算通过当前图像检查; 这仍是单轮诊断回放,不是完整四轮精度验收,也不会生成正式 JSON 或 URDF。

20260915_104446 正式实采中,拇指三个关节各冻结 10 帧零位并完成四轮, pitch/IP 的旋转及指令单表子项均通过;yaw 旋转通过,但指令单表 MAE 为 1.0454°,超过 1°。 两场独立会话均观察到稳定方向差,该批观测即使允许每个指令任取一个输出角,MAE 下界仍超过 1°。 这不是通过重新拟合单表能够消除的误差;方向分支的诊断通过也不能冒充单表通过。 详见拇指实测子项报告。 后续 20260915_111401 yaw 保持实测在 64/128/192 各双向保持 10 秒;末 3 秒的方向差 分别为 2.2965°/2.7885°/2.1248°,每个窗口均有 90 帧可靠观测,同一目标的 SDK 反馈相同。 0.5–1 秒至末段的角度变化最多约 0.038°,延长等待没有消除方向差。 本轮仅启动顶部机位,不能据此确定具体机械原因或证明整手精度;继续保留单值 JSON 与原验收门槛。 复现脚本、时间曲线与来源哈希见双向保持实测20260915_104446 会话在小指准备求解超时后停止,未完成整手空间/FK 验收、未发布 JSON/URDF。 小指同源准备段离线求解只需约 3.42 秒,但候选仍未达到统计区分要求;运行时超时来源尚待定位, 详见小指准备段核查。 后续会话另存 motion_solver_diagnostic.jsonl,记录请求来源哈希、版本、求解耗时和提交接受/拒绝, 以区分求解未返回与迟到结果被丢弃;该文件不参与测量授权,也不修改已冻结的原始记录。 此诊断接线在上述实采之后添加,不能补证该次超时原因。

固定基准另有明确的静止约束:锁定后始终使用首次冻结的相机局部姿态,将其投影到当前角点验证, 不因 IPPE 两支重投影误差排名反转而更换姿态或暂停。仍执行原有 1.5 px 重投影和 75° 倾角限制, 并保留当前 IPPE 候选与原有角点漂移监测。当前图像无法验证冻结姿态时丢弃依赖该基准的观测, 记录 fixed_reference_pose_unverified,不将几帧图像质量不合格判为不可恢复的姿态分支冲突。 恢复须默认至少 5 个合格帧且跨越至少 0.1 秒,始终核验同一冻结姿态,恢复完成前也禁止缓存兜底。 尚未使用该机位的动作可以继续;当前任务持续缺少有效数据时,仍按原有零位超时、稳态采样和最多一次同速重扫处理。 固定基准实际连续 10 帧漂移超过 5 px 仍使整场暂停,包括当前任务未使用的机位。 此约束仅用于 Profile 声明的 fixed_reference, 不用于随关节运动的 Tag,也不证明初始分支物理正确或当前外参已经实测验证。

方向结束才检查 ≥40 个有效同步样本、≥32 分箱、内部空白 ≤行程的 1/16、规定的有效行程覆盖, 以及 Profile 声明的端点/双视角观测。数据不足只同速重扫一次;第二次失败暂停。 拟合或最终文件验收失败不重新自动运动、不更新发布指针。 independent_rotation_holdout_failed 表示前三轮冻结旋转模型未通过第四轮检查,发生在空间求解前。 错误原因包含失败关节、机位和 MAE/P95/最大误差;fit_diagnostics.json.rotation_holdout 另存完整 SO(3)、轴向、偏轴统计及最坏图像身份。验收使用完整旋转误差,不能仅凭投影角度曲线重复判定通过。 新采样行的 pnp_observation_evidence 保存父子 Tag 的当前角点、尺寸、已选姿态和候选诊断, 相机矩阵引用同会话的 rectified_camera_model。缓存固定基准明确标注来源,不伪造当前帧的角点或候选。 可见且通过冻结姿态投影验证的基准标为 verified_fixed_reference,保存当前角点、IPPE 候选和实际投影误差; 完全未检测到时的缓存仍标为 cached_fixed_reference。候选回放保持同一冻结基准并复核前者的当前角点证据。 短程诊断报告以 fixed_reference_summary 单独统计基准的当前图像投影误差, 不会把固定输出姿态的零跳变误当作独立测量结果。 不合格姿态事件另外记录当时的动作阶段、指令与同步反馈,便于区分准备动作期间的异常与正式采样异常。 角点与候选记录用于诊断;其中分支状态、版本及实际选中姿态也用于核验冻结参考的一致性, 不使历史采集自动获得缺失的观测证据。O12 的轨迹几何评分仍只使用训练轮, 但正式收尾不得用评分更好的另一支替换已冻结的训练或第四轮姿态;超过同一公共等价界时明确拒绝发布。 历史记录仍可在离线诊断入口分析候选,不能借旧格式标记绕过正式产物的冻结分支检查。

所有型号显示相同进度:阶段、任务/轮次/方向、分机位 Tag ID、命令/反馈/单位/速度、 有效数据、基准、断点来源,以及中文原因、建议和原始诊断。 ROS 收尾拟合在独立进程执行:只读取采集结束时已同步的日志前缀,后续图像不进入拟合; 主进程继续接收反馈并检查固定基准,阶段事件按原顺序回传,最终发布仍由主进程持锁完成。 中止时终止计算子进程,不允许子进程更新发布指针。finalization_timing.json 保存实际耗时。 标题明确显示型号、左右手和序列号。开始前等待状态列出缺少或过期的相机内参、检测消息和 SDK 条件; 空 Tag 检测消息可以证明视觉链路工作,不要求开始前已经识别全部 Tag。 收到开始请求时重新检查设备状态;实时 CameraInfo 参数必须与受保护外参中的指纹一致。 开始前相机消息的新鲜度窗口为 2 秒,该窗口不用于扫描中的实时暂停。

唯一生产代码链

linkerhand_calibration/
├── config/profiles/                 唯一型号定义(受保护 YAML
├── config/*_product.yaml            设备、序列号、相机、路径与哈希
├── urdf/                           原始 CAD/mesh,不覆盖
└── linkerhand_calibration/
    ├── profiles/                   加载、静态验证、Tag/CAD 观测关系
    ├── core/domain/                Profile、样本、CalibrationResult、状态
    ├── core/geometry/              相机、变换、PnP 兼容导出
    │   └── tag_pose/               IPPE、连续跟踪、冻结前运动证据、刚性组和轨迹选择
    ├── core/fitting/               运动曲线、线性耦合、固定安装
    │   └── spatial_solver/         空间观测、基座、零位、统计与独立验收
    ├── core/urdf/                  授权修正、FK、范围与最终文件验收
    ├── runtime/runner.py           唯一产品启动器
    ├── runtime/coordinator.py      会话协调、采集提交、运动和收尾接线
    ├── runtime/parameters.py       已解析的运行参数;无 ROS 类型
    ├── runtime/inputs.py           相机/检测输入、时钟与发布接口
    ├── runtime/cameras.py          受保护相机身份与消息新鲜度
    ├── runtime/camera_preflight.py 启动前配置检查结果与内外参实测能力边界
    ├── runtime/session.py          唯一在线状态机
    ├── runtime/execution.py        Profile 任务与运动效果调度
    ├── runtime/motion_execution.py 平滑轨迹与位移到位判定
    ├── runtime/capture.py          多机位视觉/反馈采集
    ├── runtime/branch_initialization.py 最后归零准备段证据与一次性几何确认
    ├── runtime/branch_tracking.py  冻结约束选候选,原子提交同帧姿态与证据
    ├── runtime/motion_provenance.py 读取和收尾核验来源内容、零位与样本关联
    ├── runtime/scan_quality.py     唯一方向数据门
    ├── runtime/reference_lock.py   开始后固定基准
    ├── runtime/resume.py           整场断点证据检查
    ├── runtime/joint_resume.py     逐关节原零位验证后导入旧样本
    ├── runtime/safety.py           最小实时保护
    ├── runtime/status.py           统一状态与中文显示
    ├── runtime/snapshot.py         类型化状态快照与旧状态映射
    ├── runtime/adapters/           SDK 协议,不包含标定业务
    ├── runtime/ros/                ROS 参数加载、消息转换、订阅/服务/定时器
    ├── runtime/artifacts/          收尾控制、后台 finalizer、serializer、原子发布
    └── compat/                    历史格式、布局与离线诊断;不提供旧在线流程

calibrate_hand → runner → runtime/ros/entrypoint → UnifiedCalibrationNode → CalibrationCoordinator → SessionExecution 是所有型号的生产调用链。节点直接继承 ROS Node,组合 RosCalibrationIO 和协调器, 不通过父子类业务回调执行标定。SDK 绑定接收明确的反馈新鲜度、时钟、健康订阅和发布接口。

空间入口 core/fitting/spatial.py 保留显式兼容导出,计算顺序在 spatial_solver/solve.py:输入准备 → 仅训练求解 → 可观测性/轮间统计 → 冻结结果的独立验证 → 结果装配。 TrainingProblem 不包含第四轮观测,训练几何中的掌部姿态也按训练轮筛选。 基座策略由 Profile 的几何约束选择;曲线、零位坐标约定、权重、阈值和原始 URDF 授权修正规则不变。

PnP 的原导入路径继续可用。tag_pose/parameters.py 集中定义跟踪默认值, ROS 加载器将角度参数由 deg 转为 rad,离线采集直接使用同一套默认值。 公共分支容差仍为 0.03 px,重投影质量和倾角限制保持一致;首次确认、参考冻结与失联恢复 使用同一公共分支策略,ROS 和离线链路均不能绕过它。

CalibrationSession.phase 是流程状态来源。协调器在状态锁内开始、中止、推进和提交观测; 耗时姿态计算在锁外进行,提交时核对会话版本、采集对象、运动对象及稳态采样边界。 Start 更换预览采集对象并重新锁定基准,暂停/中止使尚未完成的旧观测失效。 收尾由 FinalizationController 组合 worker 和发布器,worker 只上报阶段事件、准备候选文件; 协调器核实全部验收阶段后才能提交。锁顺序固定为状态锁 → worker 锁, 拟合失败维持当前姿态,中止后禁止提交,成功产物只提交一次。

老的单相机拇指/CMC 在线节点和 launch 已退出安装入口;需要局部测量时应增加经过验证的 Profile, 不能重新启用旧状态机。老版本离线诊断不是新的标准 URDF 发布证据。

新增型号

必需文件:

config/profiles/new_hand.yaml
config/new_hand_product.yaml
urdf/new_hand/raw.urdf
urdf/new_hand/meshes/*

Profile 声明:

  1. Adapter、SDK 通道顺序、单位、正负方向、命令/反馈范围、基准值、独立速度槽布局。
  2. Tag ID/尺寸/机位/安装 link,以及每次运动的父子观测关系;不明确的 link 不能靠名称猜。
  3. 扫描任务、速度、准备/避让/收尾 waypoint;每关节首次零位任务、完整保持指令及固定到达姿态。
  4. 声明空间可观测约束、已知 baseline 几何或明确接受的 CAD 零位假设;每个运动关节须独立观测或显式指定对应实测来源。
  5. 每关节允许修正的 origin.rpylimit.lower/uppermimic.multiplier/offset

这仍需要正确的可观测性设计:单轴加一对未知安装角的 Tag 不能自动辨识绝对 CAD 零位。 不能为了减少配置,默认把 SDK 零点或最大指令当成 CAD 零位。可参考最简单的 L6/O6 Profile, 删除不适用关系后重新声明;不要盲目复制另一型号的空间约束。

复用 SDK 不增加 Python;新协议只增加 runtime/adapters/ 实现及协议工厂注册。 没有型号节点、runner、状态机、质量/断点/发布器;新数学只能增加通用拟合原语。 新型号使用 artifacts.output_schema_version: 3;产物为 format: unified_calibration_v3, schema_version: 3。详细双映射保留在独立标定报告中。 旧 generic v1、v4/v6/v7 只保留历史兼容工具,不把反馈表改名为指令表来伪造兼容。 v1/v2 按原语义读取,不会自动升级为独立实测;保留的 v2 数学入口用于历史回归,不能证明 v3 精度。 配置错字、错误绑定、未知修正字段在启动前失败;修改受保护文件后应审查并更新对应 SHA256。

产物与离线验证

会话目录保存原始样本、冻结 Tag 安装、拟合诊断、标准 URDF 验收、JSON、URDF。 有效发布由 release_manifest.json 及产品发布指针共同证明;单独的 PASS 摘要或存在 URDF 文件不等于已发布。 manifest 记录 JSON、URDF、标定报告三文件 SHA256、受保护输入和独立 holdout 证据。任一验收失败都不会替换原发布指针。 measurement_contract.json 提前列出 Tag/link、零位观测关系、CAD 保留/迁移与原始 mimic 限位冲突。 主 JSON 以原始 URDF 关节名为键,单值关节包含 sdk_channelangle_rad; O12 等 rad 输入还包含一一对应的 input_values(在有效节点间线性插值,不外推)。 字节型号每条曲线固定 256 项:SDK 指令 v 对应所选曲线的第 v 项,只能在训练指令实际覆盖 0–255 时导出。 输出均是修正 URDF 的 q_output,不再加零偏。顶层记录身份、单位、完整 baseline_command 和共同零位约定。 独立观测的主动和被动关节直接导出各自视觉曲线,同一驱动通道可绑定多个关节。 显式复制的关节使用来源关节的同一组节点与角度,不重新拟合、归零或由 mimic 推导;接收通道保持不变。 默认单值曲线在指定的稳态训练数据中约束 baseline=0,正反向诊断分支共用冻结参考、保留回差。 独立稳态验证分别检验两个方向;超差时禁止发布,不隐藏切换分支。

O6 侧摆 rh_thumb_cmc_yaw 通过 artifacts.directional_command_joints 显式启用双向指令曲线。 v3 JSON 顶层保存同名声明,所列关节额外包含 increasing_rad / decreasing_rad; 方向指原始 SDK 数值增加/减少,与关节角度正负无关。其他关节继续使用单值表。 两条曲线分别只用所声明的稳态训练数据拟合,共用已冻结零位、各自满足 baseline=0; 独立稳态验证按实采方向读取最终序列化曲线,仍要求 MAE≤1°、P95≤2°、最大≤3°。 报告分别保存运动与指令映射的训练/验证分区及来源图像,限位推导仍不得使用任一验证分区。 angle_rad 保留为均值参考;双向关节在非 baseline 输入且方向未知时禁止用均值代替。 读取器须从 baseline 初始化,停住时保持到达方向,允许端点换向;行程中途反转尚无小回环标定证据,会明确拒绝。 字节指令的方向判断与查表共用 floor(v+0.5) 量化规则;连续小数步进不能绕过方向切换检查。 两条主回环曲线不构成任意反转、负载或多轴工况的精度证明。旧读取器会拒绝新增关节字段,须同步更新。 修正 URDF 保留原被动关节 <mimic>,单独拖动父关节滑条时,末节按 q_child ≈ multiplier × q_parent + offset 联动。标准 URDF 不能将查找表表达为 mimic。 前三轮成对视觉角度用于拟合线性近似,优化变量为父关节两端对应的子关节角度; 两端都限制在子关节原有输出范围内,因此整个联动行程合法,无需放大限位或裁剪小负角。 偏移是近似直线在 baseline 的误差,不能当作新的物理零位;报告明确保存 baseline_error_radphysical_zero_correction: false 和独立留出误差。 JSON 指令/反馈曲线、几何零位不因近似拟合改变。需要实测非线性运动时使用完整 JSON, 消费程序显式设置全部关节角,并避免 URDF mimic 再次覆盖末节角度。

原始 CAD 限位默认仍是硬边界。Profile 可逐关节显式声明 calibrated_motion_envelope, 将该 CAD 限位视为本次要测定的运动范围;只有已授权的 limit.lower/upper 可据此修改。 O6 右手采用此策略,范围由前三轮运动反馈映射、独立稳态训练映射的实测包络及共同零位确定, 不增加安全余量,不用第四轮扩展范围,也不为容纳 mimic 越界而放大范围。 SDK 安全指令域不变,IP/DIP 的 CAD 零位不变。复制关节的范围证据仍属于来源关节。 报告和 manifest 的验收数据保存 motion_range_evidence,包含来源限位、零偏、训练轮次、 样本身份与新范围,并在收尾重新验证。O6 尚无独立机械安全角度界的实测证据, 该策略不代表对碰撞、过载或任意多轴姿态作出安全认证。

验收分别记录:最终 JSON 对独立稳态验证角度;完整 JSON+URDF FK 对独立验证 Tag 位姿; 仅使用 URDF mimic 的近似损失。前两项决定实测精度通过,第三项如实报告,不冒充非线性精度。 manifest 同时保存 urdf_joint_angle_source: calibration_jsonurdf_mimic_policy 和两种回放结果。 门限保持角度 MAE≤1°、P95≤2°、最大≤3°,Tag 位置 P95≤3 mm。 平行转轴的零位计算先消除轴上参考点的任意坐标,再比较相机平面中的轴间方向; 改变同一轴线的表示点不能改变标定零位。这是所有型号共用的几何规则。 独立精度结论只覆盖实际观测的关节和 Tag;复制关节另检查来源、查表一致性、通道与机械范围, 不把来源关节的第四轮误差称为接收关节精度。混合产物 manifest 使用 measured_and_transferred_json_urdf_v3,列出实测和复制覆盖。 FK 使用训练冻结的安装与基座;第四轮不拟合零位、曲线、Tag 安装或基座。 正式图像采集还要求核验第四轮及独立稳态验证的原始 Tag 四角,沿用冻结的安装、基座 和受哈希保护的相机参数。主观测的独立三维位姿必须解释同帧原始角点,四角重投影 RMS 不得超过原 1.5 px;最终落盘 JSON+修正 URDF 与独立位姿的差异仍按上述角度和位置门限检查。 最终 FK 的总像素误差单独报告,不能将包含允许运动误差的总量等同于图像测量噪声。 额外机位没有独立三维测量时,仅检查是否存在满足原物理误差范围、且重投影误差不超过 1.5 px 的局部刚性位姿。这个数值见证不是真实位姿测量,不写回安装、零位、曲线或相机; 各运动任务分别检查,不能用其他任务中的静止图像稀释误差。它不能替代必要的独立三维验收。 缺少独立测量或原始图像、相机身份不一致、测量像素或物理误差超限均不能发布。 失败时保留 final_image_diagnostics.json;通过后在 manifest 记录 final_file_image_holdout 及规则版本 independent_measurement_and_physical_consistency_v2,分别列出独立精度与额外视角相容性。 训练阶段若 Tag 与整条运动链不满足刚性一致性,tag_installation_diagnostics.json 一次列出全部 Tag 的角度/位置 P95、门限和通过状态,不只显示遇到的第一个失败 Tag。 该失败不能直接归因于硬件松动,也不能作为自动重做整场采集的依据。

calibration_report.json 保存完整的 command_to_rad / feedback_to_rad、输入域、方向分支、 实测关节的零位原始参考帧、完整指令/反馈/到达方向、来源和训练/holdout 指标。 复制关节记录 independently_measured: falsetransferred_from_joint 与来源参考链接, 不生成自己的伪零位样本或独立精度指标。绝对零位来源另由 zero_methodcad_zero_assumptionzero_transferred_from_joint 表达。报告中的 applicability 记录保持姿态; 未采集多姿态证据不能宣称任意多轴工况已验证。主 JSON 用于指令查表,不能误作反馈转换。 旧 unified v1 会话继续只读兼容,不自动覆盖或转换历史产物;新输出单表必须重新通过最终文件验收。

统一消费桥可直接读取通过验证的 manifest(纯消息转换,不会发送实机命令):

ros2 run linkerhand_calibration calibrated_joint_state_bridge --ros-args \
  -p calibration_file:=<session/release_manifest.json> -p input_kind:=command \
  -p input_topic:=<SDK原始指令话题> -p output_topic:=/calibration/verification/joint_states

需要反馈角度显示时显式选择 input_kind:=feedback,桥读取配套报告中的反馈映射。 桥检查三文件哈希、拒绝越域输入, v3 全部关节角由 JSON 显式计算,不加第二次零偏。完整 FK 使用 independent_mimic_angles=True,以 JSON 给出的末节角度覆盖线性近似。 标准 robot_state_publisher 和普通 URDF 网页查看器会执行 mimic,适合检查近似联动; 仅把完整 JointState 发给标准 RSP,不能让它停止覆盖被动角。 消费实测非线性曲线的仿真程序必须明确采用 JSON 角度,关闭重复的线性被动更新。 仿真输出不能与实机反馈话题同名。 MuJoCo 是用户后续使用的外部验收项目,本轮不改动它、不增加 MuJoCo 依赖,自动标定不要求打开 GUI。 外部对比须确认加载的模型来自这份 URDF,指令转换使用配套 JSON;不能用实机反馈直接驱动仿真来证明指令映射。 现有 MuJoCo 脚本尚不能视为支持 v3:后续须消费完整关节映射并停止冲突的线性被动更新。

# 同一算法离线回放新策略的完整采集;不启动硬件,默认不发布
ros2 run linkerhand_calibration calibrate_hand --config <product.yaml> \
  --offline-raw <raw_samples.jsonl> --offline-output <新的输出目录>

# 只比较两份 URDF,不修改,不控制机械手
ros2 run linkerhand_calibration compare_calibration_urdfs \
  --reference <参考.urdf> --candidate <本次.urdf> --output <新报告.json>

离线回放也检查原始会话策略、SDK 输入域、配置/相机证据、每方向完整数据及最终文件。 旧会话不能自动获得当前哈希或新版本认证。--publish-offline 必须显式给出,且不会绕过验证。 当前坐标规则、生产职责与验收范围见 v3 实施记录

早期需求审查见 2026-09-10 需求审查; 当前修改依据、实机结果与未完成的精度验收见 流程评估。 相机和反馈目前均采用下述测量时间;时间配对正确仍须经过独立空间验收。

实时调度边界

公共 ROS 节点顺序处理控制和反馈消息;耗时图像估计由每视角有界工作队列执行。 图像最多在原同步容差内等候真实反馈时间覆盖,队列不反向持锁查询会话。 增加执行器线程数不能提高共享状态的处理能力;反馈超时仍按真实测量判断。 暂停或验收失败的会话仅提供诊断/候选文件,不能当作已发布的标定结果。 调度对照依据与验证范围见 CALIBRATION_PIPELINE_REVIEW.md 第七轮评估。

相机与反馈时间

连续扫描将 CAN 实测反馈与相机曝光中点配对。MVS 取帧/图像发布时间不参与替代曝光时间; 设备时钟由独立锁存换算到 ROS 时钟,换算依据保存在会话的 camera_timing_<view>.jsonl。 运行中的时钟校验失败会暂停出图并重新执行独立锁存校验,通过后恢复采集;校验失败期间不会继续使用旧映射,也不会永久退出采集线程。恢复前后的图像时间戳仍须严格递增;若主机时钟向后跳变,早于已发布图像的帧继续丢弃。 四型号共用 unified_engine_v8_all_view_images,保留设备曝光时间策略;旧采集只能按其原策略诊断,不能混用。 报告中的 feedback_u8 表示来自字节通道的测量值:插值到曝光时间后可以是小数, 拟合、内存映射和报告读取均按声明的分段线性曲线计算,不再次取整。 command_u8 仍按实际发送的整数字节查表;旧版整数表保留原读取规则。 相机时钟修正不会将被动关节的非线性曲线转成 URDF mimic;仿真仍须读取 JSON 查找表。

同一运动中的所有机位原始角点

四轮扫描、必要稳态点和关节基准采样期间,所有机位均保存 image_observation_frame。 记录包含曝光时间、完整指令/反馈及方向、轮次与尝试编号、相机矩阵和可见 Tag 的二维角点。 它经过原检测质量与采集窗口检查,但不依赖 PnP 分支或任务的主机位。 缺少同步反馈时保留明确的空值,不能将该帧当成有效角度测量。

例如 O6 顶部 ID7 与正面 ID2 属于同一末节:弯曲和侧摆时两机位的可见角点都应保存, 以便检查整条运动链。其他机位的记录不会增加停点或运动次数,也不会替代主机位的采样覆盖要求。 同机位重复帧不重复入库;暂停、切换运动和结束采集时仍按同一会话身份撤销迟到数据。 最终 JSON/URDF 的图像验收使用通过采集检查的第四轮和独立稳态验证窗口内全部机位角点。 主观测必须能与原始图像记录逐项对应,同一图像不能重复计数;其他动作下的有效角点也直接 投影验收,不先拟合成三维位姿。缺少主观测的原始记录或二者 SDK 条件不一致时拒绝发布。 这不表示联合求解或外参自校准已经通过验证。