Files
linkerhand_retarget_ros2/src/linkerhand_calibration/TESTING.md
T

15 KiB

标定测试的执行范围

准备契约升级用 test_preparation_contract.py,覆盖新旧断点分离、父子/预算/动作 变化、历史记录保留、原会话边界、节点写入和发布入口禁止旧模型冒充新预算。 时钟自适应刷新用 test_camera_timing.py,正负平滑漂移在模拟 30 Hz 的 60 秒内 曝光时间误差保持小于原 1 ms 连续性预算,同时验证真跳变仍拒绝及启动锚点年龄。 辅助精度预算前置用 test_observer_pose.py 和 test_observer_reference.py。

此次增量验证分组记录在 software_review_stability_repair:初始针对组 79 项、8.84 秒; 证据/真实 PIP-DIP 回放/runner/合成完整产物组 91 项、58.99 秒;修正启动锚点刷新 时机后的相机组 15 项、0.47 秒。组间有重复用例,不将累计次数称为一次全量回归。 实际目录的断点选择只读验证约 17 ms,排除了旧准备契约;配置 validate-only 通过。 这些结果不替代新版本的连续实机验收。

相机时钟诊断变更使用 test_camera_timing.py:同时覆盖原始计时单位、跳变、陈旧帧、 重同步顺序及拒绝事务可重放。新增 CLOCK_MONOTONIC_RAW 仅记录诊断边界,不能 替换 ROS 时间或放宽同步门限。2026-09-20 针对组 12 项通过,0.33 秒,记录于 calibration_output/O30_RIGHT_001/software_review_stability_architecture/camera_timing_tests.json。 这不表示实机重复失同步已经修复;整体评估见 O30_STABILITY_ASSESSMENT_20260920.md。

日常修改运行受影响用例和必要契约用例,目标为 60 秒内完成。不要因为修改了一行代码就运行 整手拟合或全量 colcon test。公共产物契约变更需要覆盖相关型号,发布前再执行全量回归。

测试保留原文件位置,避免破坏现有共享夹具。test/conftest.py 集中维护分类;函数上的显式标记优先。

标记 内容 何时执行
quick 局部求解、调度、恢复、隔离和拒绝边界 日常选择受影响文件
replay 已记录的真实图像、准备失败和姿态歧义 修改对应观测或恢复逻辑
integration 完整拟合、产物回读、进程或 ROS 链路 修改公共契约或发布前
legacy 历史格式、旧型号算法及兼容入口 修改兼容契约或发布前

从工作区根目录准备环境:

source /opt/ros/jazzy/setup.bash
source install/setup.bash
export PYTHONPATH="$PWD/src/linkerhand_calibration:${PYTHONPATH:-}"
export PYTHONDONTWRITEBYTECODE=1

采集计划、训练接口日常验证(按改动选择文件,不必每次全部执行):

python3 -m pytest -q -m quick \
  src/linkerhand_calibration/test/test_capture_plan.py \
  src/linkerhand_calibration/test/test_adaptive_training.py \
  src/linkerhand_calibration/test/test_command_motion.py \
  --durations=8 --timings-json=/tmp/calibration-quick.json

涉及零位恢复时加 test_zero_recovery.py;涉及断点时加 test_prepared_resume.py、 test_passed_resume.py;涉及发布时加 test_artifact_publication.py。真实中指 DIP 歧义回归位于 test_measured_transfer.py::test_real_middle_dip_fits_but_remains_ambiguous,其正确结果仍为拒绝不充分证据。

test_prepared_resume.py::test_resume_tick_uses_prepared_data_and_still_checks_current_reference 同时覆盖 O30 分阶段恢复:历史证据索引须在启动准备时完成,实时回调不能再次整理整份历史; 参考变化仍拒绝复用。五个型号、参考保持/变化共 10 项约 2 秒。真实 20260920_141822 在该索引阶段发生控制回调阻塞,修复后的 20260920_142046 已实际复用 4 个扫描单元; 后续因小指零位 ID4 检测断续停止,不代表整手通过。

O30 回访反馈重复性修改,先运行 test_staged_training.py -k revisit:真实反馈 48→41、 10 的边界、超限、同批不稳定、图像/安装变化以及 v1/v2 回读共 8 项约 1.2 秒。 涉及复核记录格式时,追加 test_staged_artifacts.py 的 JSON→URDF 一致性和阶段拒绝组, 共享一次整手产物,5 项约 31 秒;无需为每个拒绝条件重复完整拟合。

分阶段调度、旁观证据和恢复的快速检查:

python3 -m pytest -q -m quick \
  src/linkerhand_calibration/test/test_staged_training.py \
  src/linkerhand_calibration/test/test_staged_runtime.py \
  src/linkerhand_calibration/test/test_observer_pose.py \
  src/linkerhand_calibration/test/test_observer_reference.py \
  --durations=8 --timings-json=/tmp/calibration-staged-quick.json

test_observer_reference.py 共享一次上下游图像求解,角点、来源、安装和取样篡改直接检验 证据回读边界。test_staged_runtime.py 的进程复用用例及整手训练用例标为 integration。 只有训练/冻结/发布契约变化时,才运行下面的分阶段整手组;本次初次完整组 19 项约 45 秒, 后增加的回访时序拒绝用例随产物组单独复查,未重复其余已通过的完整拟合:

python3 -m pytest -q \
  src/linkerhand_calibration/test/test_staged_training.py \
  src/linkerhand_calibration/test/test_staged_runtime.py \
  src/linkerhand_calibration/test/test_staged_artifacts.py \
  --durations=8 --timings-json=/tmp/calibration-staged-integration.json

该组共享整手输入和产物,包含实际混合轮数统计、冻结后不再训练、一次集中补采、失败终止、 断点及 JSON→URDF 链路。下面的旧策略整手组仅在旧策略相关逻辑也受影响时追加。

共享 Tag 的进入姿态联合准备修改,运行真实 MCP 的专门回放组:

python3 -m pytest -q \
  src/linkerhand_calibration/test/test_preparation_transition.py \
  --durations=8 --timings-json=/tmp/calibration-preparation-transition.json

该组共享一次真实图像求解,检查正式运行时继续、原始证据回读、缺失辅助数据、留出隔离、 保持通道/参考变化、篡改拒绝及取消清理,通常约 10 秒。只有候选选择公共逻辑受影响时追加 test_image_model_selection.py 的相关用例;已有通过的完整拟合不为文档或断言修正重复执行。 本次验证记录位于 calibration_output/O30_RIGHT_001/software_review_preparation_transition/, 保留失败与针对性复查关系,不把软件回放写成实机通过。

测量几何候选的精度资格与图像评估分离,回归位于 test_measured_transfer.py 的 test_real_ip_compares_uncertain_alternative_before_authorizing、 test_uncertain_alternative_cannot_be_frozen 和 test_uncertain_alternative_still_vetoes_when_images_do_not_distinguish。 三项共享 20260920_140424 的 129 帧真实 IP 准备及父几何证据;精度不足的候选仍参与 留出区分,不能冻结;无法区分仍拒绝。相关旧中指 DIP、候选选择及回读边界共 17 项 耗时 5.39 秒,后补否决边界 1 项耗时 2.16 秒。记录位于 calibration_output/O30_RIGHT_001/software_review_ip_alternative/,不代表整手实机通过。

完整 O30 产物链路按需单独执行:

python3 -m pytest -q \
  src/linkerhand_calibration/test/test_o30_artifacts.py::test_o30_nonlinear_twenty_channel_finalization \
  --durations=8 --timings-json=/tmp/calibration-o30-integration.json

该用例从正式采集证据入口进入,检查采样完成、训练决策回放、运动授权、受保护相机文件、 原始角点、JSON→URDF 重建和回读。输入来自独立 FK 合成场景,使用兼容的几何运动授权; 不模拟在线图像模型选择,不替代实机连续采集及精度验收。末节 CAD 零位假设仍须保留原语义。

整手输入由会话级夹具共享,只读使用;需要篡改的用例先复制。反馈缺失、反馈卡住已改为局部 映射契约测试,不再各生成一套整手 JSON/URDF。篡改、取消和失败指针检查优先复用验收边界。

--timings-json 记录各用例 setup/call/teardown 耗时、结果、层级和 Python/YAML 源码指纹。 同一版本通过的测试不无理由复跑;修复失败后只重跑失败及受新改动影响的用例。裸 pytest 仍会运行全量,标记不会悄悄关闭测试。分层收集可用 --collect-only -m replay 等命令检查。

上一版逐任务自适应方案的软件验证记录保存在工作区 calibration_output/O30_RIGHT_001/software_review_adaptive_training/software_validation.json, 同目录保留各组耗时记录和历史数据对照。O30 正式产物链路与混合轮数统计约 44 秒, 恢复、断点及真实 DIP 歧义回归约 19 秒,在线训练评估与取消约 6 秒。记录保留开发期间 的失败及修复后复查关系,不将不同代码版本的增量检查称为一次最终全量回归。

本次分阶段方案记录在 calibration_output/O30_RIGHT_001/software_review_staged_training/software_validation.json。 同目录保留各次运行的源码指纹、耗时及开发失败,报告标明修复关系。新旧策略、旁观图像、 原真实 DIP 拒绝和文件链路均分别记录;不重复旧的 2.88 GB 日志性能测量,不宣称实机已通过。

遮挡感知准备策略的日常检查使用 test_preparation_witness.py:真实无名指停点保留为 fixtures/o30_ring_mcp_ambiguous.json.gz,合成独立侧摆源明确标注,模块共享一次原始 拟合和一次新增证据。覆盖逐指避让后单通道往返、整数 SDK 目标、回访不重采、 训练/留出隔离、姿态改变、完整窗口、篡改、强制证据缺失及正式读回。只读策略为 --preparation-policy occlusion_aware_witness_v1 --validate-only,不启动硬件。

最终遮挡版:该文件加 Profile 加载、采集计划、原图像采集契约,共 58 项约 14.12 秒。 源码迭代与现场遮挡澄清导致的增量复查分别记录,不把开发过程累计次数称为一次回归。 记录目录为 calibration_output/O30_RIGHT_001/software_review_preparation_witness/。 新策略仍需完整实机对照;原 O30 JSON→URDF 及取消发布两项共享整手夹具约 32.12 秒, 没有逐个拒绝条件重新拟合整手,也没有运行全量 pytest/colcon。

准备优化器统一恢复检查位于 test_image_bundle_optimizer.py。快速用例检查计算次数、 内存上限、不收敛/异常/非有限值/误差增大时继续拒绝;真实回放标记为 replay,共享 一次 20260920_154722 拇指 IP 求解。夹具保留真实父参考、上游准备记录及其原始角点, 通过正式准备、来源几何和父参考验收;不能仅用孤立合成输出代替这些入口。

该次真实候选原先耗尽 150 次稀疏求值,使用同一训练证据暖启动直接解法后,再 6 次 收敛,随后被原 1.5 px 门限拒绝,合格候选得以通过。未收敛候选仍否决,不参与绕过 门限的比较;留出数据不决定计算恢复。主回归 24 项约 32.20 秒,覆盖旧 IP 精度不足 候选、真实中指/无名指歧义拒绝、转接/共享姿态回读、小指历史预算、训练/留出隔离和 工作进程取消。新增用例开发失败及针对性复查分别保存于 calibration_output/O30_RIGHT_001/software_review_solver_recovery/。 未重跑整手拟合和 JSON→URDF 产物组;本次改变的是准备求解及其证据绑定,产物生成、 最终角度/像素门限没有修改。软件回放通过不代表整手实机已经通过。

准备采样有效性与辅助适用性使用 test_preparation_sync.py:夹具来自真实 20260920_160630 小指完整准备窗口,保留两帧原始缺指令值。检查完整原始窗口回读、 排除记录绑定、缺值行上的保持关节变化、持续缺样/端点/身份变化拒绝,以及拟合前 辅助不适用时主模型独立验收,拟合后不改路线。与 test_preparation_witness.py 的 原歧义和篡改检查合计 41 项、10.58 秒;历史 v1 和拟合失败拒绝另 3 项、3.97 秒。 后补缺候选不可视为转角不足的边界单独执行;没有无理由重跑前述已通过组。 验证记录位于 calibration_output/O30_RIGHT_001/software_review_preparation_admission/。

双端共享姿态交接使用 test_shared_pose_handoff.py,夹具来自 20260920_163002, 包括原 MCP 模型/零位/像素来源、上游回基准原始窗口和 PIP 准备完整证据。 共享一次 PIP 拟合,检查原失败复现、新双端验证、原门限、正式回读、数据篡改、验证轮 隔离、源图像先于全部新准备数据、断点依赖导入/去重、恢复代次和四指模型归属。 DIP 用真实像素模拟后续访问,经 ObservationCapture、父参考确认和文件回读, 下游模型外壳明确为合成数据,不能宣称整指实机通过。

本轮父参考、有限恢复、旧断点及旧版共享姿态 62 项通过,20.95 秒;最终针对组 25 项通过,4.11 秒(含 1 项已受修改影响的旧父参考回读)。随后新增时序边界与导入 去重只执行相应用例。现有 O30 正式采集证据入口 JSON→URDF 集成单独运行一次, 31.24 秒。开发中的夹具缺来源、策略字段绑定和 ROS 环境收集失败保留原记录, 不计为通过;没有全量 pytest 或 colcon。完整记录见 calibration_output/O30_RIGHT_001/software_review_shared_pose/。

准备观测预算的实机像素回归使用 test_preparation_density.py,夹具为 20260920_171710 的完整小指 PIP/DIP 准备窗口、上游模型和双端交接图像。新 PIP 模型重新投影原 DIP 像素后,验证更完整的训练/留出分组、源模型精度传递、候选区分 及模型回读;冻结旧父模型仍拒绝,四指多关节求解不扩容。此回放带有明确的离线姿态 准入标记,不声称它是一次可发布的实机采集。正式产物另用已有 staged 产物组检验。

test_checkpoint_startup.py 检查断点加载心跳、独立 900 秒上限、恢复后设备/服务 时限、异常清理及无断点不发布加载状态。相关启动/共享姿态/真实密集回放 98 项 14.79 秒;产物回读、阶段隔离和取消发布 8 项 24.11 秒。前一批 85 项通过、1 项 失败是历史测试将最大像素误差写死为 0.75;新增留出图像为 0.752,实际契约为 1.5 px。现已断言正式契约,并复查完整共享姿态回读。环境收集失败、反事实回放 拒绝分类断言修正和后续通过记录均保留,不混算为一次完整实机回归。

扩展到准备采集/辅助证据入口后,发现在线密集预算未传入离线重拟合,已补齐 image_sampling_policy 的版本、报告与角色哈希绑定、重放参数及帧数上限校验。 后续来源/辅助/真实回放组 121 项通过、2 项为测试断言失败;原断言分别固定了错误 文本和旧采样子集模型。修正为准确拒绝码及相同预算的离线对照后,仅复查失败用例、 受影响的启动与产物边界,16 项通过,32.54 秒。发布线程清理再检查 7 项通过。 记录完整保存在 software_review_stability_density,没有将多版本增量测试宣称为 一次全量回归。实机尝试 20260920_174317 在发起标定运动前被现有 ROS 控制节点占用 阻止,未产生可交付 JSON/URDF。