From 7a04780b52f1d21387a1d3d880db6680695ecc6d Mon Sep 17 00:00:00 2001 From: lxp <2770281812@qq.com> Date: Wed, 16 Sep 2026 14:05:08 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84O6=E9=AA=8C=E8=AF=81=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 7 + AGENTS.md | 3 +- .../hikrobot_DB2163739.yaml | 55 + .../hikrobot_DB2163742.yaml | 55 + .../hikrobot_DB2163749.yaml | 55 + ...ree_camera_extrinsics_20260915_192517.yaml | 60 + .../core/can/linker_hand_g20_can.py | 10 + .../LinkerHand/core/can/linker_hand_l6_can.py | 6 + .../LinkerHand/core/can/linker_hand_o6_can.py | 6 + .../LinkerHand/linker_hand_api.py | 8 + .../LinkerHand/utils/feedback.py | 65 + .../linker_hand_ros2_sdk/linker_hand.py | 39 +- .../test/test_feedback_timestamps.py | 73 + .../test/test_l6_channel_order.py | 7 +- .../CALIBRATION_PIPELINE_REVIEW.md | 937 +++++ .../CALIBRATION_V3_IMPLEMENTATION.md | 103 +- src/linkerhand_calibration/README.md | 485 ++- .../config/g20_right_product.yaml | 2 +- .../config/l6_right_product.yaml | 2 +- .../config/o12_right_product.yaml | 4 +- .../config/o12_sdk.yaml | 45 + .../config/o6_right_8_tags.yaml | 12 +- .../config/o6_right_product.yaml | 16 +- .../config/o6_three_camera_calibration.yaml | 4 +- .../config/profiles/g20_right_19.yaml | 4 +- .../config/profiles/l6_right_8.yaml | 4 +- .../config/profiles/o12_right_16.yaml | 10 +- .../config/profiles/o6_right_8.yaml | 62 +- .../launch/three_camera_calibration.launch.py | 36 +- .../linkerhand_calibration/acquisition.py | 24 + .../linkerhand_calibration/camera_timing.py | 165 + .../models/o6/left_transfer.py | 3 + .../linkerhand_calibration/core/__init__.py | 2 + .../core/artifacts/storage.py | 16 +- .../core/domain/__init__.py | 2 + .../core/domain/profile.py | 65 +- .../core/domain/reference.py | 94 +- .../core/domain/result.py | 4 +- .../core/domain/sampling.py | 9 + .../core/domain/status.py | 1 + .../core/fitting/command_mapping.py | 35 +- .../core/fitting/command_sampling.py | 141 + .../core/fitting/coupling.py | 43 +- .../core/fitting/mimic_approximation.py | 76 +- .../core/fitting/motion_fit.py | 9 +- .../core/fitting/motion_transfer.py | 4 +- .../core/fitting/observed_motion.py | 10 +- .../core/fitting/rotation_diagnostics.py | 78 + .../core/fitting/session.py | 13 +- .../core/fitting/spatial_solver/residuals.py | 17 +- .../core/fitting/spatial_solver/types.py | 5 - .../core/fitting/tag_installation.py | 22 +- .../core/geometry/candidate_selection.py | 125 +- .../core/geometry/image_motion_replay.py | 167 + .../core/geometry/pnp.py | 1 + .../core/geometry/tag_border.py | 44 + .../geometry/tag_pose/branch_confirmation.py | 299 ++ .../core/geometry/tag_pose/cad_hinge.py | 111 + .../core/geometry/tag_pose/cad_image_model.py | 44 + .../core/geometry/tag_pose/fixed_reference.py | 11 + .../tag_pose/image_model_selection.py | 90 + .../geometry/tag_pose/image_motion_commit.py | 157 + .../geometry/tag_pose/image_motion_model.py | 136 + .../tag_pose/image_motion_projection.py | 131 + .../tag_pose/image_projection_worker.py | 38 + .../core/geometry/tag_pose/ippe.py | 27 + .../core/geometry/tag_pose/motion_evidence.py | 413 +++ .../tag_pose/motion_image_diagnostics.py | 223 ++ .../geometry/tag_pose/motion_image_solver.py | 174 + .../geometry/tag_pose/motion_image_types.py | 121 + .../core/geometry/tag_pose/parameters.py | 14 + .../tag_pose/production_image_motion.py | 230 ++ .../core/geometry/tag_pose/tracking.py | 464 ++- .../core/urdf/acceptance.py | 105 +- .../core/urdf/image_acceptance.py | 165 + .../core/urdf/image_consistency.py | 109 + .../core/urdf/joint_control.py | 78 + .../core/urdf/limits.py | 102 + .../core/urdf/measured_acceptance.py | 13 +- .../linkerhand_calibration/core/urdf/patch.py | 33 +- .../linkerhand_calibration/core/urdf/plan.py | 73 +- .../core/urdf/result_plan.py | 18 +- .../core/urdf/source_mimic.py | 120 + .../core/urdf/tag_acceptance.py | 20 +- .../core/urdf/validate.py | 7 +- .../linkerhand_calibration/hikrobot_camera.py | 20 +- .../profiles/diagnostics.py | 31 + .../linkerhand_calibration/profiles/export.py | 3 + .../linkerhand_calibration/profiles/loader.py | 13 +- .../profiles/observations.py | 35 + .../profiles/validator.py | 19 +- .../runtime/adapters/base.py | 43 +- .../runtime/artifacts/controller.py | 20 +- .../runtime/artifacts/direction.py | 38 + .../runtime/artifacts/evidence.py | 49 +- .../runtime/artifacts/finalization.py | 141 +- .../runtime/artifacts/image_evidence.py | 153 + .../runtime/artifacts/process_finalizer.py | 102 + .../runtime/artifacts/publisher.py | 70 +- .../runtime/artifacts/reader.py | 37 +- .../runtime/artifacts/replay.py | 9 +- .../artifacts/serializers/compact_v2.py | 1 + .../artifacts/serializers/measured_v3.py | 43 +- .../runtime/artifacts/urdf_from_json.py | 150 + .../runtime/branch_diagnostics.py | 32 + .../runtime/branch_initialization.py | 301 ++ .../runtime/branch_tracking.py | 94 + .../runtime/branch_worker.py | 73 + .../linkerhand_calibration/runtime/capture.py | 449 ++- .../runtime/coordinator.py | 709 +++- .../runtime/detection_dispatch.py | 98 + .../runtime/diagnostic_analysis.py | 298 ++ .../runtime/diagnostic_capture.py | 112 + .../runtime/diagnostic_quality.py | 84 + .../runtime/diagnostic_reference.py | 100 + .../runtime/diagnostic_report.py | 181 + .../linkerhand_calibration/runtime/engine.py | 55 +- .../runtime/execution.py | 114 +- .../runtime/feedback.py | 19 + .../runtime/image_capture.py | 33 + .../runtime/image_pairing.py | 27 + .../linkerhand_calibration/runtime/inputs.py | 1 + .../runtime/joint_resume.py | 151 +- .../runtime/motion_execution.py | 29 +- .../runtime/motion_provenance.py | 272 ++ .../runtime/observation_scope.py | 36 + .../runtime/observation_timing.py | 113 + .../runtime/parameters.py | 12 +- .../runtime/pose_evidence.py | 128 + .../runtime/reporting/reasons_zh.py | 12 + .../linkerhand_calibration/runtime/resume.py | 9 +- .../runtime/ros/calibration_node.py | 81 +- .../linkerhand_calibration/runtime/ros/io.py | 25 +- .../runtime/ros/parameters.py | 16 + .../runtime/ros/tag_border_filter.py | 71 + .../linkerhand_calibration/runtime/runner.py | 101 +- .../runtime/runner_support.py | 4 +- .../runtime/scan_quality.py | 32 +- .../linkerhand_calibration/runtime/session.py | 45 +- .../runtime/snapshot.py | 48 +- .../linkerhand_calibration/runtime/status.py | 38 +- .../linkerhand_calibration/runtime/steady.py | 10 +- .../runtime/zero_recovery.py | 29 + src/linkerhand_calibration/setup.py | 3 + .../test/engine_capture_fixture.py | 12 +- .../fixtures/camera_clock_receipts.json.gz | Bin 0 -> 12185 bytes .../o6_final_image_sources_160040.json.gz | Bin 0 -> 2419 bytes ...6_front_20260914_175815_raw_replay.json.gz | Bin 0 -> 345861 bytes ...6_front_20260914_192637_raw_replay.json.gz | Bin 0 -> 112690 bytes ...t_base_20260914_161416_quality_events.json | 474 +++ .../o6_front_base_20260914_branch_events.json | 459 +++ .../test/fixtures/o6_front_border_121857.json | 60 + .../test/fixtures/o6_front_border_121857.npz | Bin 0 -> 216787 bytes .../o6_image_live_flow_200044.json.gz | Bin 0 -> 227118 bytes .../fixtures/o6_image_motion_200044.json.gz | Bin 0 -> 140307 bytes .../o6_pinky_preparation_144514.json.gz | Bin 0 -> 70851 bytes .../o6_pinky_preparation_160040.json.gz | Bin 0 -> 56080 bytes .../test/fixtures/o6_raw_replay_sources.md | 22 + .../o6_side_base_20260914_190619.json | 182 + .../o6_side_preparation_132102.json.gz | Bin 0 -> 177258 bytes .../o6_side_preparation_193254.json.gz | Bin 0 -> 102421 bytes .../test/fixtures/o6_spatial_125641.json.gz | Bin 0 -> 63966 bytes .../fixtures/o6_spatial_125641.metadata.json | 7 + ...thumb_20260914_175815_motion_evidence.json | 1 + .../o6_thumb_20260914_branch_events.json | 3246 +++++++++++++++++ .../o6_yaw_directional_114156.json.gz | Bin 0 -> 121042 bytes .../test/measured_capture_fixture.py | 16 +- .../test/pose_branch_fixture.py | 110 + .../test/runtime_host_fixture.py | 7 +- .../test/test_adaptive_command_sampling.py | 94 + .../test/test_affine_mimic_export.py | 103 + .../test/test_all_model_artifact_pipeline.py | 99 + .../test/test_all_view_image_acceptance.py | 103 + .../test/test_all_view_image_capture.py | 71 + .../test/test_axis_line_gauge.py | 59 + .../test/test_bounded_mimic_approximation.py | 44 + .../test/test_branch_initialization.py | 225 ++ .../test/test_branch_worker.py | 49 + .../test/test_cad_image_geometry.py | 163 + .../test/test_calibrated_motion_limits.py | 161 + .../test/test_calibration_coordinator.py | 81 +- .../test/test_camera_timing.py | 93 + .../test/test_capture_journal.py | 85 + .../test/test_capture_metadata_concurrency.py | 111 + .../test/test_capture_provenance.py | 32 +- .../test/test_detection_dispatch.py | 109 + .../test/test_diagnostic_analysis.py | 210 ++ .../test/test_diagnostic_coordinator.py | 383 ++ .../test/test_diagnostic_observation_scope.py | 215 ++ .../test/test_diagnostic_provenance.py | 98 + .../test/test_diagnostic_quality.py | 111 + .../test/test_diagnostic_reference_flow.py | 218 ++ .../test/test_diagnostic_report.py | 185 + .../test/test_diagnostic_session.py | 203 ++ .../test/test_diagnostic_thumb_cli.py | 246 ++ .../test_diagnostic_yaw_hold_coordinator.py | 139 + .../test/test_diagnostic_yaw_hold_plan.py | 59 + .../test/test_directional_command_mapping.py | 156 + .../test/test_dual_mapping.py | 3 + .../test/test_feedback_capture_timing.py | 37 + .../test/test_final_image_holdout.py | 85 + .../test/test_final_image_sources.py | 55 + .../test/test_fixed_reference_pose.py | 234 ++ .../test/test_frozen_candidate_selection.py | 228 ++ .../test/test_frozen_tag_replay.py | 5 +- .../test/test_held_joint_replay.py | 86 + .../test/test_image_error_budget.py | 124 + .../test/test_image_model_selection.py | 126 + .../test_image_motion_capture_evidence.py | 195 + .../test/test_image_motion_live_flow.py | 75 + .../test/test_image_motion_provenance.py | 265 ++ .../test/test_image_motion_tracker_commit.py | 150 + .../test/test_image_projection_worker.py | 53 + .../test/test_independent_urdf_export.py | 206 ++ .../test/test_joint_zero_runtime.py | 99 +- .../test/test_joint_zero_timing.py | 102 + .../test/test_json_urdf_rebuild.py | 92 + .../test/test_mapping_input_contract.py | 74 + .../test/test_measured_feedback_time.py | 68 + .../test/test_measured_finalization.py | 9 +- .../test_measured_transfer_finalization.py | 56 +- .../test/test_motion_branch_evidence.py | 257 ++ .../test/test_motion_capture_tracking.py | 221 ++ .../test/test_motion_capture_transitions.py | 45 + .../test/test_motion_confirmed_tracking.py | 273 ++ .../test/test_motion_image_diagnostics.py | 169 + .../test/test_motion_reference_provenance.py | 284 ++ .../test/test_motion_transfer.py | 47 +- .../test/test_o12_full_hand_zero.py | 6 +- .../test/test_o12_observation_resolution.py | 6 +- .../test/test_o12_right_profile.py | 21 +- .../test/test_o6_tag_installation.py | 25 + .../test/test_observation_capture.py | 13 +- .../test/test_observation_strategy.py | 9 +- .../test/test_observation_timing.py | 134 + .../test/test_paused_snapshot.py | 106 + src/linkerhand_calibration/test/test_pnp.py | 32 +- .../test/test_pnp_branch_confirmation.py | 309 ++ .../test/test_pnp_metadata_concurrency.py | 93 + .../test/test_pnp_parameter_wiring.py | 50 + .../test/test_pnp_runtime_policy.py | 14 +- .../test/test_pose_capture_evidence.py | 125 + .../test/test_pose_tracking_resume_policy.py | 112 + .../test/test_prepared_resume.py | 125 + .../test/test_process_finalizer.py | 138 + .../test/test_production_image_motion.py | 194 + .../test/test_profile_finalization.py | 15 +- .../test/test_profile_loader.py | 2 +- .../test/test_recorded_diagnostic_replay.py | 147 + .../test/test_recorded_side_preparation.py | 48 + .../test/test_reference_waypoints.py | 3 +- .../test/test_rotation_holdout_diagnostics.py | 143 + .../test/test_runtime_pose_branch_guard.py | 295 ++ .../test/test_runtime_readiness.py | 6 +- .../test/test_separate_mapping_capture.py | 139 + .../test/test_session_execution.py | 85 +- .../test/test_single_pass_capture.py | 151 + .../test/test_source_mimic_export.py | 200 + .../test/test_spatial_solve_order.py | 33 + .../test/test_steady_feedback_window.py | 143 + .../test/test_storage.py | 34 + .../test/test_tag_border.py | 110 + .../test/test_transfer_artifact_contract.py | 2 + .../test/test_unified_engine.py | 8 +- .../test/test_unified_launch.py | 111 +- .../test/test_unified_ros_host.py | 43 +- .../test/test_unified_runner.py | 5 +- .../test/test_zero_recovery.py | 72 + 268 files changed, 26640 insertions(+), 789 deletions(-) create mode 100644 config/o6_camera_intrinsics_20260915/hikrobot_DB2163739.yaml create mode 100644 config/o6_camera_intrinsics_20260915/hikrobot_DB2163742.yaml create mode 100644 config/o6_camera_intrinsics_20260915/hikrobot_DB2163749.yaml create mode 100644 config/o6_three_camera_extrinsics_20260915_192517.yaml create mode 100644 src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/utils/feedback.py create mode 100644 src/linker_hand_ros2_sdk/test/test_feedback_timestamps.py create mode 100644 src/linkerhand_calibration/CALIBRATION_PIPELINE_REVIEW.md create mode 100644 src/linkerhand_calibration/config/o12_sdk.yaml create mode 100644 src/linkerhand_calibration/linkerhand_calibration/camera_timing.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/fitting/command_sampling.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/fitting/rotation_diagnostics.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/image_motion_replay.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_border.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/branch_confirmation.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/cad_hinge.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/cad_image_model.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/fixed_reference.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_model_selection.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_motion_commit.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_motion_model.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_motion_projection.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_projection_worker.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_evidence.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_image_diagnostics.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_image_solver.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_image_types.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/production_image_motion.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/urdf/image_acceptance.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/urdf/image_consistency.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/urdf/joint_control.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/urdf/limits.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/core/urdf/source_mimic.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/profiles/diagnostics.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/direction.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/image_evidence.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/process_finalizer.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/urdf_from_json.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/branch_diagnostics.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/branch_initialization.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/branch_tracking.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/branch_worker.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/detection_dispatch.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_analysis.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_capture.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_quality.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_reference.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_report.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/feedback.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/image_capture.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/image_pairing.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/motion_provenance.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/observation_scope.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/observation_timing.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/pose_evidence.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/ros/tag_border_filter.py create mode 100644 src/linkerhand_calibration/linkerhand_calibration/runtime/zero_recovery.py create mode 100644 src/linkerhand_calibration/test/fixtures/camera_clock_receipts.json.gz create mode 100644 src/linkerhand_calibration/test/fixtures/o6_final_image_sources_160040.json.gz create mode 100644 src/linkerhand_calibration/test/fixtures/o6_front_20260914_175815_raw_replay.json.gz create mode 100644 src/linkerhand_calibration/test/fixtures/o6_front_20260914_192637_raw_replay.json.gz create mode 100644 src/linkerhand_calibration/test/fixtures/o6_front_base_20260914_161416_quality_events.json create mode 100644 src/linkerhand_calibration/test/fixtures/o6_front_base_20260914_branch_events.json create mode 100644 src/linkerhand_calibration/test/fixtures/o6_front_border_121857.json create mode 100644 src/linkerhand_calibration/test/fixtures/o6_front_border_121857.npz create mode 100644 src/linkerhand_calibration/test/fixtures/o6_image_live_flow_200044.json.gz create mode 100644 src/linkerhand_calibration/test/fixtures/o6_image_motion_200044.json.gz create mode 100644 src/linkerhand_calibration/test/fixtures/o6_pinky_preparation_144514.json.gz create mode 100644 src/linkerhand_calibration/test/fixtures/o6_pinky_preparation_160040.json.gz create mode 100644 src/linkerhand_calibration/test/fixtures/o6_raw_replay_sources.md create mode 100644 src/linkerhand_calibration/test/fixtures/o6_side_base_20260914_190619.json create mode 100644 src/linkerhand_calibration/test/fixtures/o6_side_preparation_132102.json.gz create mode 100644 src/linkerhand_calibration/test/fixtures/o6_side_preparation_193254.json.gz create mode 100644 src/linkerhand_calibration/test/fixtures/o6_spatial_125641.json.gz create mode 100644 src/linkerhand_calibration/test/fixtures/o6_spatial_125641.metadata.json create mode 100644 src/linkerhand_calibration/test/fixtures/o6_thumb_20260914_175815_motion_evidence.json create mode 100644 src/linkerhand_calibration/test/fixtures/o6_thumb_20260914_branch_events.json create mode 100644 src/linkerhand_calibration/test/fixtures/o6_yaw_directional_114156.json.gz create mode 100644 src/linkerhand_calibration/test/pose_branch_fixture.py create mode 100644 src/linkerhand_calibration/test/test_adaptive_command_sampling.py create mode 100644 src/linkerhand_calibration/test/test_affine_mimic_export.py create mode 100644 src/linkerhand_calibration/test/test_all_model_artifact_pipeline.py create mode 100644 src/linkerhand_calibration/test/test_all_view_image_acceptance.py create mode 100644 src/linkerhand_calibration/test/test_all_view_image_capture.py create mode 100644 src/linkerhand_calibration/test/test_axis_line_gauge.py create mode 100644 src/linkerhand_calibration/test/test_bounded_mimic_approximation.py create mode 100644 src/linkerhand_calibration/test/test_branch_initialization.py create mode 100644 src/linkerhand_calibration/test/test_branch_worker.py create mode 100644 src/linkerhand_calibration/test/test_cad_image_geometry.py create mode 100644 src/linkerhand_calibration/test/test_calibrated_motion_limits.py create mode 100644 src/linkerhand_calibration/test/test_camera_timing.py create mode 100644 src/linkerhand_calibration/test/test_capture_journal.py create mode 100644 src/linkerhand_calibration/test/test_capture_metadata_concurrency.py create mode 100644 src/linkerhand_calibration/test/test_detection_dispatch.py create mode 100644 src/linkerhand_calibration/test/test_diagnostic_analysis.py create mode 100644 src/linkerhand_calibration/test/test_diagnostic_coordinator.py create mode 100644 src/linkerhand_calibration/test/test_diagnostic_observation_scope.py create mode 100644 src/linkerhand_calibration/test/test_diagnostic_provenance.py create mode 100644 src/linkerhand_calibration/test/test_diagnostic_quality.py create mode 100644 src/linkerhand_calibration/test/test_diagnostic_reference_flow.py create mode 100644 src/linkerhand_calibration/test/test_diagnostic_report.py create mode 100644 src/linkerhand_calibration/test/test_diagnostic_session.py create mode 100644 src/linkerhand_calibration/test/test_diagnostic_thumb_cli.py create mode 100644 src/linkerhand_calibration/test/test_diagnostic_yaw_hold_coordinator.py create mode 100644 src/linkerhand_calibration/test/test_diagnostic_yaw_hold_plan.py create mode 100644 src/linkerhand_calibration/test/test_directional_command_mapping.py create mode 100644 src/linkerhand_calibration/test/test_feedback_capture_timing.py create mode 100644 src/linkerhand_calibration/test/test_final_image_holdout.py create mode 100644 src/linkerhand_calibration/test/test_final_image_sources.py create mode 100644 src/linkerhand_calibration/test/test_fixed_reference_pose.py create mode 100644 src/linkerhand_calibration/test/test_frozen_candidate_selection.py create mode 100644 src/linkerhand_calibration/test/test_held_joint_replay.py create mode 100644 src/linkerhand_calibration/test/test_image_error_budget.py create mode 100644 src/linkerhand_calibration/test/test_image_model_selection.py create mode 100644 src/linkerhand_calibration/test/test_image_motion_capture_evidence.py create mode 100644 src/linkerhand_calibration/test/test_image_motion_live_flow.py create mode 100644 src/linkerhand_calibration/test/test_image_motion_provenance.py create mode 100644 src/linkerhand_calibration/test/test_image_motion_tracker_commit.py create mode 100644 src/linkerhand_calibration/test/test_image_projection_worker.py create mode 100644 src/linkerhand_calibration/test/test_independent_urdf_export.py create mode 100644 src/linkerhand_calibration/test/test_joint_zero_timing.py create mode 100644 src/linkerhand_calibration/test/test_json_urdf_rebuild.py create mode 100644 src/linkerhand_calibration/test/test_mapping_input_contract.py create mode 100644 src/linkerhand_calibration/test/test_measured_feedback_time.py create mode 100644 src/linkerhand_calibration/test/test_motion_branch_evidence.py create mode 100644 src/linkerhand_calibration/test/test_motion_capture_tracking.py create mode 100644 src/linkerhand_calibration/test/test_motion_capture_transitions.py create mode 100644 src/linkerhand_calibration/test/test_motion_confirmed_tracking.py create mode 100644 src/linkerhand_calibration/test/test_motion_image_diagnostics.py create mode 100644 src/linkerhand_calibration/test/test_motion_reference_provenance.py create mode 100644 src/linkerhand_calibration/test/test_o6_tag_installation.py create mode 100644 src/linkerhand_calibration/test/test_observation_timing.py create mode 100644 src/linkerhand_calibration/test/test_paused_snapshot.py create mode 100644 src/linkerhand_calibration/test/test_pnp_branch_confirmation.py create mode 100644 src/linkerhand_calibration/test/test_pnp_metadata_concurrency.py create mode 100644 src/linkerhand_calibration/test/test_pnp_parameter_wiring.py create mode 100644 src/linkerhand_calibration/test/test_pose_capture_evidence.py create mode 100644 src/linkerhand_calibration/test/test_pose_tracking_resume_policy.py create mode 100644 src/linkerhand_calibration/test/test_prepared_resume.py create mode 100644 src/linkerhand_calibration/test/test_process_finalizer.py create mode 100644 src/linkerhand_calibration/test/test_production_image_motion.py create mode 100644 src/linkerhand_calibration/test/test_recorded_diagnostic_replay.py create mode 100644 src/linkerhand_calibration/test/test_recorded_side_preparation.py create mode 100644 src/linkerhand_calibration/test/test_rotation_holdout_diagnostics.py create mode 100644 src/linkerhand_calibration/test/test_runtime_pose_branch_guard.py create mode 100644 src/linkerhand_calibration/test/test_separate_mapping_capture.py create mode 100644 src/linkerhand_calibration/test/test_single_pass_capture.py create mode 100644 src/linkerhand_calibration/test/test_source_mimic_export.py create mode 100644 src/linkerhand_calibration/test/test_spatial_solve_order.py create mode 100644 src/linkerhand_calibration/test/test_steady_feedback_window.py create mode 100644 src/linkerhand_calibration/test/test_tag_border.py create mode 100644 src/linkerhand_calibration/test/test_zero_recovery.py diff --git a/.gitignore b/.gitignore index b3d8edd..ec9bd89 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,11 @@ Thumbs.db /profiles/ /calibration_output/ /config/*_three_camera_extrinsics.yaml +# Superseded local O6 camera calibrations. Keep the active extrinsics and +# intrinsics referenced by o6_right_product.yaml available for version control. +/config/o6_three_camera_extrinsics_20260915.yaml +/config/o6_three_camera_extrinsics_intrinsics_20260915.yaml +/config/o6_three_camera_extrinsics_recalibrated.yaml *.wear_check.json *.checkpoint.json *.verification.json @@ -73,6 +78,8 @@ Thumbs.db /src/linkerhand_calibration/urdf/*/*_calibrated_*.urdf /src/linkerhand_calibration/urdf/*/*_zero_calibrated*.urdf /src/linkerhand_calibration/urdf/*/*_transferred_from_*.urdf +# Unreferenced local URDF experiment; never use as a protected CAD input. +/src/linkerhand_calibration/urdf/o6_right/linkerhand_o6_right111.urdf /src/linkerhand_retarget/linkerhand_retarget/assets/robots/hands/linker_hand/g20_left/linkerhand_g20_left_cmc_pitch_*.urdf /src/linkerhand_retarget/linkerhand_retarget/assets/robots/hands/linker_hand/g20_left/linkerhand_g20_left_calibrated_*.urdf /src/linkerhand_retarget/linkerhand_retarget/assets/robots/hands/linker_hand/g20_left/linkerhand_g20_left_zero_calibrated_*.urdf diff --git a/AGENTS.md b/AGENTS.md index a8e003d..0589e3e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1 +1,2 @@ -避免产生屎山代码,要求代码结构清晰可读性好 \ No newline at end of file +1.不要补丁式修复问题,避免产生屎山代码,要求代码结构清晰可读性好。 +2.永远用中文回复。 \ No newline at end of file diff --git a/config/o6_camera_intrinsics_20260915/hikrobot_DB2163739.yaml b/config/o6_camera_intrinsics_20260915/hikrobot_DB2163739.yaml new file mode 100644 index 0000000..8556005 --- /dev/null +++ b/config/o6_camera_intrinsics_20260915/hikrobot_DB2163739.yaml @@ -0,0 +1,55 @@ +image_width: 1624 +image_height: 1240 +camera_name: hikrobot_top_DB2163739 +camera_matrix: + rows: 3 + cols: 3 + data: + - 3608.7915725635876 + - 0.0 + - 814.529414176388 + - 0.0 + - 3602.9474866035775 + - 596.7866652774017 + - 0.0 + - 0.0 + - 1.0 +distortion_model: plumb_bob +distortion_coefficients: + rows: 1 + cols: 5 + data: + - -0.05774549441026762 + - -0.5653814259405426 + - -0.0034831870836035525 + - -0.0007572304719307008 + - 0.0 +rectification_matrix: + rows: 3 + cols: 3 + data: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + - 1.0 + - 0.0 + - 0.0 + - 0.0 + - 1.0 +projection_matrix: + rows: 3 + cols: 4 + data: + - 3590.9833984375 + - 0.0 + - 813.6314052451635 + - 0.0 + - 0.0 + - 3591.4892578125 + - 595.0547132430074 + - 0.0 + - 0.0 + - 0.0 + - 1.0 + - 0.0 diff --git a/config/o6_camera_intrinsics_20260915/hikrobot_DB2163742.yaml b/config/o6_camera_intrinsics_20260915/hikrobot_DB2163742.yaml new file mode 100644 index 0000000..a01efb8 --- /dev/null +++ b/config/o6_camera_intrinsics_20260915/hikrobot_DB2163742.yaml @@ -0,0 +1,55 @@ +image_width: 1624 +image_height: 1240 +camera_name: hikrobot_front_DB2163742 +camera_matrix: + rows: 3 + cols: 3 + data: + - 3520.83963745858 + - 0.0 + - 971.8024440668337 + - 0.0 + - 3503.4665942352735 + - 636.2092891200001 + - 0.0 + - 0.0 + - 1.0 +distortion_model: plumb_bob +distortion_coefficients: + rows: 1 + cols: 5 + data: + - -0.06456607194260039 + - -0.3209716179987898 + - 0.0016533290649511723 + - 0.014394085090833234 + - 0.0 +rectification_matrix: + rows: 3 + cols: 3 + data: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + - 1.0 + - 0.0 + - 0.0 + - 0.0 + - 1.0 +projection_matrix: + rows: 3 + cols: 4 + data: + - 3485.41357421875 + - 0.0 + - 980.9585478080553 + - 0.0 + - 0.0 + - 3500.062255859375 + - 636.4474700149367 + - 0.0 + - 0.0 + - 0.0 + - 1.0 + - 0.0 diff --git a/config/o6_camera_intrinsics_20260915/hikrobot_DB2163749.yaml b/config/o6_camera_intrinsics_20260915/hikrobot_DB2163749.yaml new file mode 100644 index 0000000..79dc878 --- /dev/null +++ b/config/o6_camera_intrinsics_20260915/hikrobot_DB2163749.yaml @@ -0,0 +1,55 @@ +image_width: 1624 +image_height: 1240 +camera_name: hikrobot_side_DB2163749 +camera_matrix: + rows: 3 + cols: 3 + data: + - 3564.4557723541934 + - 0.0 + - 633.5422135270659 + - 0.0 + - 3549.8257661608695 + - 614.1194434338138 + - 0.0 + - 0.0 + - 1.0 +distortion_model: plumb_bob +distortion_coefficients: + rows: 1 + cols: 5 + data: + - -0.023159717917942222 + - -0.6611401590594669 + - 0.0010272016326466154 + - -0.014523561130335804 + - 0.0 +rectification_matrix: + rows: 3 + cols: 3 + data: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + - 1.0 + - 0.0 + - 0.0 + - 0.0 + - 1.0 +projection_matrix: + rows: 3 + cols: 4 + data: + - 3531.000244140625 + - 0.0 + - 623.4124604711214 + - 0.0 + - 0.0 + - 3551.248291015625 + - 614.0312247691472 + - 0.0 + - 0.0 + - 0.0 + - 1.0 + - 0.0 diff --git a/config/o6_three_camera_extrinsics_20260915_192517.yaml b/config/o6_three_camera_extrinsics_20260915_192517.yaml new file mode 100644 index 0000000..7652e91 --- /dev/null +++ b/config/o6_three_camera_extrinsics_20260915_192517.yaml @@ -0,0 +1,60 @@ +schema_version: 1 +reference_view: front +cameras: + front: + serial_number: DB2163742 + width: 1624 + height: 1240 + intrinsics_sha256: 3cdecb8fcede41561019882a48a342d7265a44855cf0282ec96ec7f69a9b1ee5 + side: + serial_number: DB2163749 + width: 1624 + height: 1240 + intrinsics_sha256: dbe0cdc73460dc1e51c0e3967395d14b4415a71cee952597e0717ad0babd3449 + top: + serial_number: DB2163739 + width: 1624 + height: 1240 + intrinsics_sha256: 315d5cd6f28964ad57ff67088222fff3d9679cce076acc3f05a22b76038e5352 +front_from_view: + front: + translation_xyz_m: + - 0.0 + - 0.0 + - 0.0 + quaternion_xyzw: + - 0.0 + - 0.0 + - 0.0 + - 1.0 + side: + translation_xyz_m: + - -0.8459732881345877 + - -0.000555404488719817 + - 0.9729938257334647 + quaternion_xyzw: + - -0.04404064417946292 + - 0.6703485002266404 + - -0.01659275028535042 + - 0.7405524900654377 + top: + translation_xyz_m: + - -0.10806018719194868 + - -0.5842672050520756 + - 1.0419844264616385 + quaternion_xyzw: + - 0.7284460604085318 + - -0.016210595429770967 + - 0.019511507802845565 + - -0.6846333724953536 +quality: + passed: true + reprojection_rms_px: 0.46496404895142424 + maximum_rotation_repeatability_deg: 0.019287272875226844 + maximum_translation_repeatability_m: 0.0002246640979668814 + front_side_captures: 15 + front_top_captures: 15 + front_side_candidates: 15 + front_top_candidates: 15 + front_side_rejected: 0 + front_top_rejected: 0 diff --git a/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/core/can/linker_hand_g20_can.py b/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/core/can/linker_hand_g20_can.py index 6e7538d..ab81d74 100644 --- a/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/core/can/linker_hand_g20_can.py +++ b/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/core/can/linker_hand_g20_can.py @@ -7,6 +7,7 @@ from enum import Enum from utils.open_can import OpenCan from can.exceptions import CanError from utils.color_msg import ColorMsg +from utils.feedback import ReceivedPositionFrames current_dir = os.path.dirname(os.path.abspath(__file__)) target_dir = os.path.abspath(os.path.join(current_dir, "..")) sys.path.append(target_dir) @@ -178,6 +179,10 @@ class LinkerHandG20Can: # 串联控制数据存储 self.x41, self.x42, self.x43, self.x44, self.x45 = [], [], [], [], [] + frame_layout = [[(frame, index) for index in range(6)] for frame in range(0x41, 0x46)] + layout = self.joint_state_to_cmd_state(frame_layout) + self.position_feedback = ReceivedPositionFrames( + {frame: 6 for frame in range(0x41, 0x46)}, [item if item != 0 else None for item in layout]) self.x49, self.x4A, self.x4B, self.x4C, self.x4D = [0] * 6, [0] * 6, [0] * 6, [0] * 6, [0] * 6 self.x51, self.x52, self.x53, self.x54, self.x55 = [], [], [], [], [] self.x59, self.x5A, self.x5B, self.x5C, self.x5D = [], [], [], [], [] @@ -354,6 +359,8 @@ class LinkerHandG20Can: response_data = msg.data[1:] if len(list(response_data)) == 0: return + if frame_type in range(0x41, 0x46): + self.position_feedback.accept(frame_type, response_data, msg.timestamp) # 并联控制指令响应 if frame_type == 0x01: self.x01 = list(response_data) elif frame_type == 0x02: self.x02 = list(response_data) @@ -1040,6 +1047,9 @@ class LinkerHandG20Can: return None return self.joint_state_to_cmd_state(state=state) + def get_feedback_snapshot(self): + return self.position_feedback.snapshot() + def get_current_pub_status(self): """API接口:获取手指当前状态""" self.get_current_status() diff --git a/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/core/can/linker_hand_l6_can.py b/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/core/can/linker_hand_l6_can.py index de94aed..8a3054a 100644 --- a/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/core/can/linker_hand_l6_can.py +++ b/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/core/can/linker_hand_l6_can.py @@ -6,6 +6,7 @@ import threading import numpy as np from utils.open_can import OpenCan from utils.color_msg import ColorMsg +from utils.feedback import ReceivedPositionFrames from can.exceptions import CanError @@ -17,6 +18,7 @@ class LinkerHandL6Can: self.open_can = OpenCan(load_yaml=yaml) self.x01 = [0] * 6 # 关节位置 + self.position_feedback = ReceivedPositionFrames({0x01: 6}, [(0x01, i) for i in range(6)]) self.x02 = [-1] * 6 # 转矩限制 self.x05 = [0] * 6 # 速度 self.x07 = [-1] * 6 # 加速度 @@ -233,6 +235,7 @@ class LinkerHandL6Can: break if not is_position_echo: self.x01 = list(response) + self.position_feedback.accept(frame_type, response, msg.timestamp) elif frame_type == 0x02: # 0x02 self.x02 = list(response_data) elif frame_type == 0x05: # Set speed @@ -326,6 +329,9 @@ class LinkerHandL6Can: def get_current_pub_status(self): return self.x01 + def get_feedback_snapshot(self): + return self.position_feedback.snapshot() + def get_speed(self): #self.send_frame(0x05, [],sleep=0.003) #print("L6暂不支持读取实时速度") diff --git a/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/core/can/linker_hand_o6_can.py b/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/core/can/linker_hand_o6_can.py index 5c95ab8..4a1a256 100644 --- a/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/core/can/linker_hand_o6_can.py +++ b/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/core/can/linker_hand_o6_can.py @@ -4,6 +4,7 @@ import threading import numpy as np from utils.open_can import OpenCan from utils.color_msg import ColorMsg +from utils.feedback import ReceivedPositionFrames from can.exceptions import CanError @@ -15,6 +16,7 @@ class LinkerHandO6Can: self.open_can = OpenCan(load_yaml=yaml) self.x01 = [0] * 6 # 关节位置 + self.position_feedback = ReceivedPositionFrames({0x01: 6}, [(0x01, i) for i in range(6)]) self.x02 = [-1] * 6 # 转矩限制 self.x05 = [0] * 6 # 速度 self.x07 = [-1] * 6 # 加速度 @@ -224,6 +226,7 @@ class LinkerHandO6Can: return if frame_type == 0x01: # 0x01 self.x01 = list(response_data) + self.position_feedback.accept(frame_type, response_data, msg.timestamp) elif frame_type == 0x02: # 0x02 self.x02 = list(response_data) elif frame_type == 0x05: # Set speed @@ -317,6 +320,9 @@ class LinkerHandO6Can: def get_current_pub_status(self): return self.x01 + def get_feedback_snapshot(self): + return self.position_feedback.snapshot() + def get_speed(self): self.send_frame(0x05, [],sleep=0.002) #print("L6暂不支持读取实时速度") diff --git a/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/linker_hand_api.py b/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/linker_hand_api.py index 82fc7e7..3074b82 100644 --- a/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/linker_hand_api.py +++ b/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/linker_hand_api.py @@ -207,6 +207,14 @@ class LinkerHandApi: getter = getattr(self.hand, "get_cached_current_status", None) return getter() if getter is not None else None + @property + def supports_feedback_timestamps(self): + return callable(getattr(self.hand, "get_feedback_snapshot", None)) + + def get_feedback_snapshot(self): + getter = getattr(self.hand, "get_feedback_snapshot", None) + return getter() if getter is not None else None + def get_state_for_pub(self): return self.hand.get_current_pub_status() diff --git a/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/utils/feedback.py b/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/utils/feedback.py new file mode 100644 index 0000000..18ce8f0 --- /dev/null +++ b/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/LinkerHand/utils/feedback.py @@ -0,0 +1,65 @@ +"""Atomic CAN position snapshots with the original receive timestamps.""" + +from dataclasses import dataclass +import math +from threading import Lock + + +@dataclass(frozen=True) +class FeedbackSnapshot: + positions: tuple + channel_stamps_ns: tuple[int, ...] + + @property + def stamp_ns(self): + return max(self.channel_stamps_ns) + + def as_dict(self, names): + return {"schema": "can_feedback_v1", "name": list(names), + "position": list(self.positions), "channel_stamps_ns": list(self.channel_stamps_ns), + "stamp_ns": self.stamp_ns} + + +class ReceivedPositionFrames: + """Keep receipt identity even if stationary feedback has identical values. + + ``layout`` lists the frame and value index for each public SDK channel. + None represents a protocol-reserved constant-zero slot, whose freshness + is bounded by the oldest actual frame. No polling/publication clock is + allowed to update a snapshot's timestamps. + """ + + def __init__(self, sizes, layout): + self.sizes, self.layout = dict(sizes), tuple(layout) + self._frames = {} + self._lock = Lock() + + def accept(self, frame, values, timestamp): + if frame not in self.sizes or len(values) != self.sizes[frame]: + return False + if not isinstance(timestamp, (int, float)) or not math.isfinite(timestamp) or timestamp <= 0: + return False + stamp = int(round(timestamp * 1_000_000_000)) + with self._lock: + previous = self._frames.get(frame) + if previous is not None and stamp <= previous[0]: + return False + self._frames[frame] = (stamp, tuple(values)) + return True + + def snapshot(self): + with self._lock: + if self._frames.keys() != self.sizes.keys(): + return None + oldest = min(value[0] for value in self._frames.values()) + positions, stamps = [], [] + for item in self.layout: + if item is None: + positions.append(0) + stamps.append(oldest) + else: + frame, index = item + stamp, values = self._frames[frame] + positions.append(values[index]) + stamps.append(stamp) + return FeedbackSnapshot(tuple(positions), tuple(stamps)) diff --git a/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/linker_hand.py b/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/linker_hand.py index b08da9d..502130d 100644 --- a/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/linker_hand.py +++ b/src/linker_hand_ros2_sdk/linker_hand_ros2_sdk/linker_hand.py @@ -168,6 +168,8 @@ class LinkerHand(Node): self.last_position_command_time = None self.last_hand_state = [-1] * 10 + self.last_hand_state_stamp_ns = None + self._last_timed_feedback_stamps = None self.last_hand_vel = [-1] * 10 self.force = [[-1] * 5] * 4 self.matrix_dic = { @@ -245,6 +247,7 @@ class LinkerHand(Node): COMMAND_QOS, ) self.hand_state_pub = self.create_publisher(JointState, self.topic(f'/cb_{self.hand_type}_hand_state'),10) + self.timed_state_pub = self.create_publisher(String, self.topic(f'/cb_{self.hand_type}_hand_state_timed'), 30) self.hand_info_pub = self.create_publisher(String, self.topic(f'/cb_{self.hand_type}_hand_info'), 10) if self.is_touch == True: if self.modbus != "None": @@ -391,7 +394,7 @@ class LinkerHand(Node): self.last_hand_vel_cmd = None def _poll_state_if_due(self): - if self.hand_state_pub.get_subscription_count() < 1: + if self.hand_state_pub.get_subscription_count() < 1 and self.timed_state_pub.get_subscription_count() < 1: return now = time.monotonic() if state_reads_deferred( @@ -400,8 +403,7 @@ class LinkerHand(Node): self.command_quiet_period, self.defer_state_reads_while_commanding, ): - # pub_state continues to publish the last completed state as a - # heartbeat. A fresh blocking read is made after motion settles. + # A repeated heartbeat retains its original measurement time. return if not state_poll_due( self.last_state_poll_time, now, self.state_poll_period @@ -411,6 +413,7 @@ class LinkerHand(Node): # another read on the following timer callback. self.last_state_poll_time = now self.last_hand_state = self.api.get_state() + self.last_hand_state_stamp_ns = self.get_clock().now().nanoseconds time.sleep(0.003) if state_poll_due( self.last_velocity_poll_time, now, self.velocity_poll_period @@ -478,9 +481,7 @@ class LinkerHand(Node): def pub_state(self): while True: - if self.hand_state_pub.get_subscription_count() > 0: - msg = self.joint_state_msg(self.last_hand_state, self.last_hand_vel) - self.hand_state_pub.publish(msg) + self._publish_feedback() if self.is_touch == True and self.touch_type == 1 and self.modbus == "None" and self.touch_pub.get_subscription_count() > 0: msg = Float32MultiArray() msg.data = [float(val) for sublist in self.force for val in sublist] @@ -498,6 +499,26 @@ class LinkerHand(Node): self.hand_info_pub.publish(msg) time.sleep(self.hz) + def _publish_feedback(self): + """Publish one atomic measured state; never give cached values a new time.""" + if self.api.supports_feedback_timestamps: + snapshot = self.api.get_feedback_snapshot() + if snapshot is None: + return + positions, stamp = snapshot.positions, snapshot.stamp_ns + if (snapshot.channel_stamps_ns != self._last_timed_feedback_stamps + and self.timed_state_pub.get_subscription_count() > 0): + message = String() + message.data = json.dumps(snapshot.as_dict(self.api.get_finger_order())) + self.timed_state_pub.publish(message) + self._last_timed_feedback_stamps = snapshot.channel_stamps_ns + else: + positions, stamp = self.last_hand_state, self.last_hand_state_stamp_ns + if stamp is None: + return + if self.hand_state_pub.get_subscription_count() > 0: + self.hand_state_pub.publish(self.joint_state_msg(positions, self.last_hand_vel, stamp_ns=stamp)) + def pub_matrix_mass(self, dic): """发布矩阵数据合值 单位g 克 JSON格式""" msg = String() @@ -556,10 +577,12 @@ class LinkerHand(Node): msg.data = json.dumps(self.matrix_dic) self.matrix_touch_pub.publish(msg) - def joint_state_msg(self, pose,vel=[]): + def joint_state_msg(self, pose, vel=(), *, stamp_ns=None): joint_state = JointState() joint_state.header = Header() - joint_state.header.stamp = self.get_clock().now().to_msg() + if stamp_ns is None: + stamp_ns = self.get_clock().now().nanoseconds + joint_state.header.stamp.sec, joint_state.header.stamp.nanosec = divmod(int(stamp_ns), 1_000_000_000) joint_state.name = self.api.get_finger_order() joint_state.position = [float(x) for x in pose] if len(vel) > 1: diff --git a/src/linker_hand_ros2_sdk/test/test_feedback_timestamps.py b/src/linker_hand_ros2_sdk/test/test_feedback_timestamps.py new file mode 100644 index 0000000..c9315c5 --- /dev/null +++ b/src/linker_hand_ros2_sdk/test/test_feedback_timestamps.py @@ -0,0 +1,73 @@ +"""A CAN receipt, rather than a heartbeat, defines position sample identity.""" + +import json +from types import SimpleNamespace + +from linker_hand_ros2_sdk.LinkerHand.utils.feedback import ReceivedPositionFrames +from linker_hand_ros2_sdk.linker_hand import LinkerHand + + +def test_cached_unchanged_and_out_of_order_frames_keep_measurement_identity(): + frames = ReceivedPositionFrames({1: 2}, [(1, 0), (1, 1)]) + assert frames.snapshot() is None + assert not frames.accept(1, [4], 1.) + assert not frames.accept(1, [4, 5], 0.) + assert frames.accept(1, [4, 5], 1.) + first = frames.snapshot() + assert first == frames.snapshot() + assert not frames.accept(1, [8, 9], .9) + assert not frames.accept(1, [8, 9], 1.) + assert frames.snapshot() == first + assert frames.accept(1, [4, 5], 2.) # A new stationary observation is real. + assert frames.snapshot().positions == first.positions + assert frames.snapshot().stamp_ns == 2_000_000_000 + assert first.stamp_ns == 1_000_000_000 + + +def test_split_finger_snapshot_does_not_retimestamp_held_channels(): + frames = ReceivedPositionFrames({1: 2, 2: 2}, [(2, 1), (1, 0), None]) + frames.accept(1, [10, 11], 1.) + assert frames.snapshot() is None + frames.accept(2, [20, 21], 1.02) + first = frames.snapshot() + assert first.positions == (21, 10, 0) + assert first.channel_stamps_ns == (1_020_000_000, 1_000_000_000, 1_000_000_000) + frames.accept(2, [20, 22], 1.04) + assert frames.snapshot().channel_stamps_ns == (1_040_000_000, 1_000_000_000, 1_000_000_000) + + +def test_sdk_republication_cannot_refresh_cached_state_timestamps(): + frames = ReceivedPositionFrames({1: 2}, [(1, 0), (1, 1)]) + joint_messages, timed_messages = [], [] + publisher = lambda messages: SimpleNamespace(get_subscription_count=lambda: 1, publish=messages.append) + host = SimpleNamespace(api=SimpleNamespace(supports_feedback_timestamps=True, + get_feedback_snapshot=frames.snapshot, get_finger_order=lambda: ['a', 'b']), + _last_timed_feedback_stamps=None, last_hand_vel=[], hand_state_pub=publisher(joint_messages), + timed_state_pub=publisher(timed_messages)) + host.joint_state_msg = lambda *args, **kwargs: LinkerHand.joint_state_msg(host, *args, **kwargs) + LinkerHand._publish_feedback(host) + assert not joint_messages and not timed_messages + frames.accept(1, [10, 20], 100.) + LinkerHand._publish_feedback(host) + LinkerHand._publish_feedback(host) + assert len(joint_messages) == 2 and len(timed_messages) == 1 + assert all(message.header.stamp.sec == 100 for message in joint_messages) + assert json.loads(timed_messages[0].data)['channel_stamps_ns'] == [100_000_000_000]*2 + frames.accept(1, [10, 20], 101.) + LinkerHand._publish_feedback(host) + assert len(timed_messages) == 2 and joint_messages[-1].header.stamp.sec == 101 + + +def test_g20_timed_layout_matches_the_public_sdk_order(): + from linker_hand_ros2_sdk.LinkerHand.core.can.linker_hand_g20_can import LinkerHandG20Can + hand = LinkerHandG20Can.__new__(LinkerHandG20Can) + layout = hand.joint_state_to_cmd_state([[(frame, index) for index in range(6)] for frame in range(0x41, 0x46)]) + frames = ReceivedPositionFrames({frame: 6 for frame in range(0x41, 0x46)}, [item or None for item in layout]) + raw = [] + for finger, frame in enumerate(range(0x41, 0x46)): + raw.append([finger*10+i for i in range(6)]) + frames.accept(frame, raw[-1], 100.+finger*.01) + snapshot = frames.snapshot() + assert snapshot.positions == tuple(hand.joint_state_to_cmd_state(raw)) + assert snapshot.channel_stamps_ns[0] == 100_000_000_000 + assert snapshot.channel_stamps_ns[4] == 100_040_000_000 diff --git a/src/linker_hand_ros2_sdk/test/test_l6_channel_order.py b/src/linker_hand_ros2_sdk/test/test_l6_channel_order.py index 15c8cd8..626290c 100644 --- a/src/linker_hand_ros2_sdk/test/test_l6_channel_order.py +++ b/src/linker_hand_ros2_sdk/test/test_l6_channel_order.py @@ -46,12 +46,14 @@ def test_l6_can_position_echo_does_not_replace_measured_feedback() -> None: sys.path.insert(0, str(linker_hand_root)) try: from core.can.linker_hand_l6_can import LinkerHandL6Can + from utils.feedback import ReceivedPositionFrames finally: sys.path.remove(str(linker_hand_root)) hand = LinkerHandL6Can.__new__(LinkerHandL6Can) hand.can_id = 0x27 hand.x01 = [10, 20, 30, 40, 50, 60] + hand.position_feedback = ReceivedPositionFrames({0x01: 6}, [(0x01, i) for i in range(6)]) hand._position_echo_lock = threading.Lock() command = (255, 2, 253, 253, 253, 253) hand._pending_position_echoes = deque( @@ -60,13 +62,14 @@ def test_l6_can_position_echo_does_not_replace_measured_feedback() -> None: hand._position_echo_timeout_seconds = 0.02 hand.process_response( - SimpleNamespace(arbitration_id=0x27, data=bytes((0x01, *command))) + SimpleNamespace(arbitration_id=0x27, data=bytes((0x01, *command)), timestamp=100.) ) assert hand.x01 == [10, 20, 30, 40, 50, 60] assert not hand._pending_position_echoes measured = (250, 3, 252, 252, 252, 252) hand.process_response( - SimpleNamespace(arbitration_id=0x27, data=bytes((0x01, *measured))) + SimpleNamespace(arbitration_id=0x27, data=bytes((0x01, *measured)), timestamp=101.) ) assert hand.x01 == list(measured) + assert hand.get_feedback_snapshot().channel_stamps_ns == (101_000_000_000,) * 6 diff --git a/src/linkerhand_calibration/CALIBRATION_PIPELINE_REVIEW.md b/src/linkerhand_calibration/CALIBRATION_PIPELINE_REVIEW.md new file mode 100644 index 0000000..ea78d21 --- /dev/null +++ b/src/linkerhand_calibration/CALIBRATION_PIPELINE_REVIEW.md @@ -0,0 +1,937 @@ +# 四轮采集与图像拟合的修改前评估 + +日期:2026-09-16。目标:稳定完成采集,从可信标定 JSON 重建修正 URDF。 + +## 当前结论(2026-09-16) + +当前 O6 按重新实测的 16.5 mm Tag 完成新采集,完整数据经正式离线流程验收通过, +已生成合格 JSON、修正 URDF 和发布清单。本次在线收尾发生系统内存耗尽,不能把离线 +成功表述为在线全过程无中断。已修正收尾读取原始日志时的整文件副本开销,独立验证见后文。 +没有增加 O6 专用容错分支或降低物理精度门限,也不据此宣称其他型号实机均已通过。 + +| 环节 | 当前证据与结论 | +| --- | --- | +| 四轮连续采集与必要稳态点 | 新会话 `20260916_105528` 完成 36/36,1 个小指方向自动重扫后通过;采集约 6 分 47 秒。随后收尾时发生 OOM,反馈超时暂停;完整数据已用于正式离线验收。此前两次 16 mm 会话也完成采集,但空间验收失败。 | +| 反馈时间 | 使用真实 CAN 接收时间;相同缓存不得伪装成新测量。逐通道时间保留至图像插值。 | +| 相机时间 | 三台实际设备的曝光事件与帧计数器吻合;设备锁存换算曝光中点,替换约晚 16.5 ms 的取帧时间。 | +| 调度 | ROS 控制/反馈顺序处理,图像独立有界队列;实机对照消除了共享锁造成的反馈排队。 | +| 映射输入 | 当前报告的分段线性反馈曲线保留曝光时刻的小数输入;实际字节命令与旧整数表保持量化。 | +| 图像证据 | 同一运动保存全部机位的原始角点,不依赖任务主机位或 PnP;最新会话保存 16,822 个图像记录。没有增加运动或停点。 | +| 图像验收 | 独立三维测量对原始角点仍须 ≤1.5 px;最终文件按原角度/位置门限检查,额外视角只提供物理容差内的图像相容性证据。已知真值反例和反向拒绝测试通过。 | +| 平行转轴零位 | 已修正参考点沿轴移动导致零位变化的公共公式;四型号源 URDF 的已知真值及任意轴上参考点检查通过。数学规则不再由型号开关选择。 | +| JSON → URDF | `20260916_105528/offline_165mm` 已生成通过验收的文件与 manifest;另外调用重建 CLI,仅从保存 JSON 和源 URDF 得到的文件与发布 URDF 逐字节相同。 | +| 整链空间精度 | 新 16.5 mm 会话已通过原门限。主观测运动/稳态独立验收中,各角色最差角度 P95 为 1.873°,位置 P95 为 2.122 mm;最终文件空间与图像检查通过。精度适用范围是观测关节、扫描方向与固定的其他关节姿态;未独立观测的关节仍按配置迁移。 | +| 尺度输入核验 | 用户实测更正为 **16.5×16.5 mm** Tag、**27×27 mm** 棋盘,与独立图像检查基本吻合。当前 O6 的 Profile、检测器和标定节点尺寸已同步并记录新指纹;未修改相机参数,未将旧 16 mm 数据冒充新尺寸采集。 | + +先前的分机位空间不一致已在修正实测尺寸后的新数据上通过验收,未靠增加拟合参数 +或放宽门限达成。当前稳定性问题集中在已确认的收尾内存峰值;保留完整采集数据直接 +验证收尾修复,不为重复计算重新运动机械手。其余型号仍需对应硬件实测,O6 当前 Tag +布局中的其他三指曲线迁移不等于各关节独立标定。 + +上述两次未验收候选的三个主动零位差均小于 0.03°,说明当前分段求解可重复, +不能据此证明其绝对空间精度。正式发布仍必须通过整链与最终文件验收。 + +以下按发生顺序保留评估依据;早期 CAN 未连接等现场状态已由上述新实采结果更新。 + +## 已核实的问题 + +1. 当前生产采集要求准备运动先确定唯一 PnP/铰链姿态;不唯一时整场暂停,后续三轮训练数据尚未获得。 +2. 准备阶段的自由铰链几何与最终保留骨长、轴向的 URDF 使用不同参数空间。 +3. 后续姿态已受准备模型约束,不能把其重复性直接解释为原始图像上的整链正确性。 +4. JSON 保存后读取并重建 URDF 已有独立字节一致性验证,应保留这条文件链。 + +## 修改前的离线实验 + +没有改动生产算法、现场硬件、原始采样或原始 URDF。以 `20260915_150015` 保存的原始 +角点进行诊断:前三轮抽取 480 个 Tag 观测,第四轮 160 个观测仅用于冻结后的检查。 +直接按源 URDF 的运动链拟合一个共同基座、三个允许修改的零位和每个 Tag 的固定安装; +另一个实验允许带固定基准的双向曲线修正。两个实验均使用记录时的相机参数。 + +- 固定曲线:39 个参数,32 次迭代收敛,约 0.60 秒。 +- 联合曲线:119 个参数,23 次迭代收敛,约 1.90 秒,曲线最大修正约 1.38°。 +- 带 0.05 px 噪声的已知真值实验,三个初值均收敛,三个零位最大误差约 0.023°。 +- 实采第四轮仍有不合格残差;小指末节最大约 4.15 px。上述实验不是实机通过证据。 +- 使用后来核验的相机参数进行的私有反事实诊断也没有消除全部残差,不能将旧数据改挂新参数发布。 + +脚本、参数、雅可比和报告位于 +`calibration_output/O6_RIGHT_001/20260915_150015/review/architecture_assessment/`。 +原型只证明统一参数空间可以计算、在所测合成条件下可恢复;没有证明全型号实机精度。 + +## 采用的边界 + +- 采集保存原始角点、相机身份、SDK 指令/反馈、方向、轮次与基准图像;固定基准漂移、 + SDK 故障、时间同步和必要采样覆盖检查仍然有效。 +- 稳定基准图像先冻结,三维解释后求解;未求解的图像不得标成已测准的角度或零位。 +- 训练只使用前三轮;第四轮与独立稳态验证不参与几何、安装、曲线或候选的选择。 +- 图像拟合使用与输出 URDF 一致的拓扑和可修改参数;不自动改变骨长、转轴、相机参数, + 不新增 CAD 零位假设。显式复制关节继续保留来源声明。 +- 像素残差小不是角度/位置精度合格的充分条件。原物理门限、参数可确定性和最终文件读取验证需保留。 +- 模型尚未通过回归与实测验收前,不凭局部收敛结果替换正式发布逻辑,不降低门限以生成 PASS。 +- 采集完成后的拟合失败应保留完整数据并支持离线重算,区别于运动中暂停。 + +## 第二轮评估:不采用未经验证的整体替换 + +对同一会话进一步测试直接从原始角点初始化、共同拟合 33 节点双向单调曲线的版本。 +共 359 个参数、6160 个训练残差,150 次迭代、43.39 秒后仍未收敛;拇指末节 +第四轮最大误差约 45.18 px,小指末节约 7.77 px。这个结果说明,前一个实验依赖 +既有拟合初值的局部收敛,不足以证明新流程具备独立初始化和实机鲁棒性。 + +因此该原型**未接入生产**,相关采集/基准/拟合草稿已移出程序源码,连同源代码快照保存到 +上述评估目录的 `unadopted_prototype/` 和 `prototype_source_snapshot.tar.gz`。 +`monotonic_urdf_image_replay.json` 保留失败数据。下一步不能通过增加迭代次数、减小残差权重 +或放宽门限将其直接上线。 + +经过四型号 FK 对照测试的投影基础 `core/fitting/urdf_image.py` 和对应测试也归档到 +`unadopted_prototype/`,便于复查。生产源码不保留未接入的备用算法。 + +## 本次实际接入的流程修改 + +1. 四轮及稳态采集完成、数据同步落盘后,显式关闭图像采集,撤销尚未返回的图像回调。 + 拟合使用已经保存的图像与相机参数;此后相机消息/Tag 位置变化不再改变这份输入或触发 + 采集中暂停。运动采集期间的固定基准漂移检查保持生效。 +2. 反馈接收、SDK 故障保护和用户中止在后台拟合期间持续生效。 +3. 数值拟合、文件验收和后台阶段协议错误统一结束为 `FAILED`,保存失败阶段、原始数据路径、 + 完成/计划采集单元数和异常堆栈;不再写成采集中 `PAUSED`。 +4. 状态输出始终提供原始数据路径,拟合失败提供诊断路径,提示先离线复算。 + 失败结果不会填写已通过的 JSON/URDF 路径,也不会被迟到观测或清理用中止覆盖。 +5. 修复反馈曲线整链回放中丢失非活动关节到达方向的问题。比如下游关节正在弯曲, + 上游关节保持不动时,上游仍应使用其到达方向对应的回差曲线,不能自动改用双向均值。 + 反馈回放和稳态指令回放现在使用同一个方向解析函数;记录了不完整或非法方向时明确拒绝。 + 独立两轴合成数据证明:旧处理会使固定 Tag 看起来有超过 4° 的安装误差,修复后 + 训练和第四轮最终 URDF 回放恢复真值。当前旧 O6 数据的对应影响小于 0.03°,不是 + 该次实采大误差的主要原因。数值见 `held_joint_direction_effect.json`。 +6. 用户确认 O6 顶部 ID7 与正面 ID2 固定在同一末节后,明确设置其安装连杆为 + `rh_thumb_distal`,不再从侧摆任务名称推断为侧摆根部连杆。Profile 哈希同步更新; + 旧会话的配置身份保持原样。独立合成场景中的 O6 安装真值同步修正,其他型号不变。 + 单独侧摆且弯曲角固定时,错误连杆可以被固定安装矩阵吸收,因此不能声称该错误 + 一定解释先前全部残差;必须在正确绑定及完整关节状态下继续验证。 + +这组流程修改解决采集完成后继续受实时视觉影响和失败状态误导的问题;准备段的几何 +不一致另按下面的评估处理。保持原文件生成与发布验收链路。 + +## 第三轮评估:使用输出 URDF 已保留的几何约束 + +源 URDF 中相邻平行转轴的平行关系及垂直轴距,在任意关节零位修正下均不变。 +旧准备模型却允许这些量自由变化,求出的几何无法由最终保留骨长和轴向的 URDF 表达。 +因此只对真实 Tag 安装连杆、观测拓扑和源 URDF 均满足条件的相邻平行轴,消去两个 +自由轴倾角和一个自由轴距参数。相机、Tag 安装与逐帧角度仍由图像估计;不新增 +CAD 零位约定、不使用 SDK 角度作为拟合先验、不引入额外惩罚权重。 + +适用性由共同编译器判断,没有 O6 专用分支;不满足条件时保留原图像模型。 +冻结模型记录新的策略版本、约束及源 URDF 哈希,最终处理前从源文件重新推导验证。 +旧模型的序列化字节结构保持不变,避免破坏旧证据哈希。内存记录与磁盘记录均保留会话头。 +断点策略升级为 `confirmed_branch_v6_source_geometry`;旧自由几何会话仍可离线读取, +不能作为新约束流程的断点混入新采集。 + +对 `20260915_160040` 保存的小指准备段 104 帧原始角点: + +- 原自由模型返回 `image_motion_families_not_distinguishable`。 +- 使用源平行关系与 36.9995936 mm 轴距后,四个候选均在原 150 次迭代上限内收敛。 +- 最优两个候选收敛到相同完整 Tag 姿态,其他候选仍用原统计检验排除。 +- 最优候选留出帧最大误差约 0.696 px,低于原 1.5 px 门限;正式求解器约 4.69 秒返回成功。 +- 原像素、角度/轴心不确定性、候选等价性及显著性门限均未放宽。 + +独立合成图像验证同向及反向平行轴的真值恢复;四型号源几何验证约束在独立零位变化下 +仍成立。这证明该准备段可以使用与输出一致的模型解算,**尚不等于新四轮整手实机通过**。 +结果保存在 `20260915_160040/review/cad_geometry_resolution.json` 及相邻诊断文件。 + +随后经实际 `BranchInitialization` 入口回放该会话三个准备段的原始图像,拇指侧摆 +98 帧、拇指弯曲 88 帧、小指 104 帧均通过,耗时约 0.45 / 3.78 / 4.94 秒。 +该回放使用用户已确认的当前 Tag 绑定,仅保存诊断;没有改写旧会话或发布新标定。 +见 `all_preparations_source_geometry.json` 和同目录可复现脚本。 + +## 验证顺序 + +1. 受约束图像模型的已知真值、多初值、旧证据兼容和错误几何反例。 +2. 四型号共同采集逻辑与数据身份;反馈/固定基准保护及中止持续生效。 +3. 前三轮训练身份与第四轮隔离;完整性、重复图像、篡改与旧会话身份检查。 +4. JSON 落盘后独立重建 URDF、标准 ROS 加载及最终文件验收。 +5. O6 当前相机配置下重新实采;只有通过实际验收才更新发布结果。 + +## 本轮验证结果及实机阻塞 + +- 最终几何、真实图像入口、采集边界、来源身份、JSON 独立重建回归:87 项通过(39.81 秒)。 +- 后台进程、诊断采集、四型号产物与 launch 相关集成检查:80 项通过(157.08 秒)。 +- 新旧断点策略、恢复与原始图像凭据检查:92 项通过(4.85 秒)。 + 各组有交叉,不将数量相加作为独立测试总数。 +- 已知真值覆盖两/三关节链和正/反向平行轴;故意把 37 mm 轴距改为 80 mm 时, + 求解器因原重投影门限不合格而拒绝,不能仅凭源几何声明强行通过。 +- `colcon build --packages-select linkerhand_calibration --symlink-install` 通过。 +- 用户确认现场为 O6 后,清理了仍订阅同一控制话题的 O30 残留 SDK/GUI。 + 实际执行新的 `calibrate_hand --no-resume` 命令时,启动检查报告 `CAN interface does not exist: can0`。 + 系统仅有 lo/eno1/wlo1;采集未开始、未发送电机指令、没有发布本轮新标定。 + 需恢复 O6 原通信适配器后继续四轮实测;当前不能宣称整手实机精度或全部型号实机通过。 + 启动报告保存在上述 review 目录的 `o6_cad_geometry_startup.json` 和日志中。 + +## 第四轮评估:有限恢复、精简停点与最终原始角点验收 + +本次优化以保留四轮连续扫描、独立验证集和原精度门限为前提。没有采用直接用运动中 +角度代替静态指令映射的办法,因为反馈滞后和方向回差会使两种角度不同。 + +### 准备段恢复 + +`runtime/zero_recovery.py` 为每组尚未冻结零位的关节提供一次局部恢复额度。 +图像数量不足或候选暂时不确定时,复用该组原归零路径补采;几何已确认而零位图像 +不足时,仅保持当前姿态重新打开图像窗口。恢复撤销旧观测回调,保留真实日志; +不替换已冻结几何/零位、不改变保持条件,不将设备故障当成视觉缺样处理。 +两种恢复共享一次额度,持续失败仍暂停,不无限重做整场四轮运动。 + +### 训练停点精简 + +`core/fitting/command_sampling.py` 只读取前三轮连续数据,在原静态训练网格上求最少 +保留节点,使每关节、每方向在原网格节点的插值变化不超过 0.25°。端点、baseline、 +中间支撑点和显式非线性补充节点必须保留;训练不足或单调修正过大时保留完整网格。 +该准则决定采样位置,不证明实际静态误差已经达标。每个保留点仍需采集新稳态图像; +原独立稳态验证点全部保留,只有这些验证与最终产物验收通过才可发布。 + +计划记录训练来源及自身哈希,采集、恢复、离线回放和拟合共同消费同一声明,禁止 +使用第四轮或稳态验证点反过来选择训练节点。断点策略升级为 +`confirmed_branch_v7_final_image_sampling`,旧数据不会混入新流程冒充新证据。 + +另一项优化只省去重复准备等待:上一段已经完成到位检查、下一目标的完整指令向量 +相同、一秒内反馈未变化时复用该结果;真实位移、反馈变化、记录过期或归零准备段 +仍执行原运动检查。原运动速度、避让和采样稳定性门限保持不变。 + +对 O6 `20260915_150015` 的已保存数据离线比较,稳态训练点由每方向 9/9/10 个变为 +7/6/10 个;独立验证仍为 10/10/11 个,双向总停点由 118 次降至 108 次。 +小指因训练曲线噪声保留原网格,不以强行减点换取精度风险。 +原方案和精简方案均通过原独立稳态角度验证,全部关节最大误差仍小于 1.69°。 +这是旧数据的训练点选择对照,不能当作新四轮实机或最终空间精度通过。 +原始文件哈希保持不变,脚本及结果为该会话的 +`review/assess_static_sampling.py`、`review/static_sampling_assessment.json`。 + +### 最终文件到原始图像 + +`runtime/artifacts/image_evidence.py` 从原始记录绑定图像身份、四角、Tag 尺寸以及 +受哈希保护的相机外参和 CameraInfo。`core/urdf/image_acceptance.py` 读取最终文件, +使用冻结的安装和基座,把 JSON+URDF 的 FK 直接投影到第四轮及独立稳态验证四角。 +这里不调用 PnP、优化器或重新配准;同一最终产物还须通过原来的 3D 和角度门限。 +每帧四角重投影 RMS 沿用 1.5 px 门限,超限保存 `final_image_diagnostics.json`。 +原始证据缺失、相机身份变化、训练/验证重叠及重复图像都会被拒绝。 + +新软件的自动测试与离线数据检查不代表四型号已经逐一实测通过。 +O6 仍需恢复 `can0` 后运行新的完整四轮,并通过上述最终文件验收。 + +本次新增链路的验证结果: + +- 四型号精简采样策略及实际 JSON→URDF→角点验收共 11 项通过(96.41 秒), + 包含静态验证异常仍被拒绝、第四轮不影响训练点选择等反例。 +- 局部恢复、真实定时器流程和重复等待边界共 24 项通过(21.62 秒)。 + 持续缺少第十张零位图像在一次补采后仍停止,不能少采冒充合格。 +- 最终角点、真实相机来源、混合实测/复制产物及后台进程共 23 项通过(25.13 秒)。 +- `colcon build --packages-select linkerhand_calibration --symlink-install` 通过(1.81 秒)。 +- 构建后再次执行 O6 正式 `calibrate_hand --no-resume`,仍在配置检查阶段报 + `CAN interface does not exist: can0`,没有进入运动采集;日志保存为 + `20260915_160040/review/o6_optimized_flow_startup.log`。 + +这些组存在覆盖交叉,不相加作为独立总数;四型号整链使用独立构造的合成观测, +真实来源绑定另用 O6 已保存角点检查,两者均不能冒充新实机结果。 + +全包回归中同步修正两处旧测试约定:零位不足的用例须观察到唯一一次恢复结束, +而非要求第一次超时立即暂停;历史侧面准备图像须调用带源 URDF 约束的当前生产入口, +不能继续要求旧自由几何模型必然可解。针对这两处的 7 项复测通过(11.49 秒)。 +同一份侧面历史数据 121 帧使用当前入口通过,训练/验证仍为互不重叠的 61/60 帧, +候选统计门限与绝对像素门限保持不变;没有为了通过测试修改原始数据或生产门限。 + +全包首次运行共 1497 项:1494 项通过、上述 2 项旧测试断言失败,另有 1 项因未提供 +O12 本地实采与独立参考文件而跳过(1175.15 秒)。两处失败修正后的 7 项复测通过, +没有剩余已知失败。完整日志、专项回归和构建日志保存在 +`20260915_160040/review/common_flow_validation/`;不将跳过的 O12 实采检查视为通过。 + +## 第五轮评估:训练选择与准备验证的边界 + +2026-09-15 19:32 使用新相机外参进行正式 O6 采集,拇指两任务完整采完, +小指准备阶段两次均报 `image_motion_families_not_distinguishable`。原始数据与修改前 +评估保存在 `20260915_193254/review/solver_assessment/`;先完成诊断实验,再修改生产代码。 + +整体链路保留:准备几何 → 冻结零位 → 四轮连续扫描 → 必要稳态映射 → JSON → 回读生成 +URDF → 独立图像验收。本次问题位于准备模型的选择和验证,不涉及末端非线性存储格式。 +独立稠密 Jacobian 求解证实增加迭代无效;四个初值有三个收敛到同一解,剩余解与其 +相差约 19°、9 mm,不能把真实空间差异当作浮点误差。 + +审查发现原实现按验证误差选择优胜模型,再用同一验证集证明它较好;拟合使用平方误差, +统计比较却使用 RMS 差;逐个立即返回还保留了最保守的固定多重比较惩罚。 +现统一为训练选择、验证否决,候选所用的训练资格不受验证结果或验证异常影响; +验证失败不得改选。公共 `image_model_selection.py` 按同一平方损失比较,完成全部候选 +比较后使用 Holm 校正,原始与校正后的概率均写入诊断。 + +1.5 px、0.03 px、1°/1 mm 等价界、原角度和空间验收门限均保留,名义族错误水平仍为 0.01。 +同帧等价检查按角色名对齐,并拒绝缺失或重复角色;不再依靠 `zip` 恰好有相同顺序。 +姿态策略升级为 `confirmed_branch_v8_training_model_selection`,旧会话仅供诊断,不能 +自动续接为新策略采集。新增策略适用于公共流程,不增加 O6 专用回退或停点。 + +修改前的候选策略诊断中,两次失败数据均可区分(概率约 0.00373、0.00576),拇指保持通过, +真正歧义的合成数据保持拒绝;这些仅是修复依据,不代表整手新实机验收完成。 +统计假设和条件性边界参考 [SciPy Wilcoxon 文档](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.wilcoxon.html), +多重比较方法参考 [Holm 方法文档](https://www.statsmodels.org/stable/generated/statsmodels.stats.multitest.multipletests.html)。 + +新策略实机 `20260915_195619` 完整采集 36/36 个方向单元,0 次暂停、0 次局部补采; +小指准备一次通过。146 项准备求解与产物链专项检查通过。最终空间验收仍失败, +候选 JSON/URDF 未发布;这与采集是否完整是两个不同结论。 + +## 第六轮评估:先修复测量时间,再判断空间求解 + +该会话的原始角点、生产重放和参数对照保存在 `20260915_195619/review/spatial_assessment/`。 +轴线投影开关没有改善整体误差;共同 URDF 的 39 参数图像求解虽然收敛,第四轮仍有 +4.124 px 误差,因此没有采用这一替换。相同反馈和相同方向的第四轮图像还能构成 +与任何确定性查找表不相容的反例:两帧像素距离 3.753 px,最大误差必有一帧不低于 +1.877 px,超过现有 1.5 px 门限。该反例不依赖拟合算法,也不证明硬件损坏。 + +SDK 代码确认了一个时间来源错误:发布线程给低频轮询缓存反复填上当前时间。 +相机与发布消息的时间差很小,并不能证明相机与真实反馈同步。修复位于共同数据边界: + +- CAN 接收端原子保存位置和接收时间,O6/L6 单帧、G20 按各指分帧保留。 +- SDK 的标准状态保留真实接收时间;标定专用接口携带每通道时间,仅发布新增测量快照。 +- 标定按每通道实际时间插值。重复、乱序、过期或同一接收身份对应不同值的记录不能刷新 + 稳态样本或反馈新鲜度;原始反馈快照落盘,便于复核。 +- 共用采集策略升级为 `unified_engine_v6_measured_feedback_time`,四型号 Profile 同步声明。 + 源 URDF、相机和精度门限保持原样;旧采集不能改挂新时间策略发布。 + +时间契约、缓存、分帧、回归与稳态检查 150 项通过;四型号配置、ROS 接入、断点与产物链 +集成检查 69 项通过;SDK 与标定两包构建通过。不运动的实机接口核验中,486 条标定反馈 +全部匹配独立旁路收到的原始 CAN 帧,0 条无来源、0 条重复身份。真实测量频率约 25 Hz, +同一时段标准接口发布 1147 条消息,说明之前约 58 Hz 的发布频率不能作为测量频率。 +该核验启动 SDK,SDK 仍执行其初始化速度/力矩设置;没有发送位置运动命令。 +完整实机产物仍须用新的测量时间重新采集后验收。 + + +## 第七轮评估:控制消息顺序处理,图像任务独立执行 + +真实时间采集 `20260915_202203` 暴露了图像缺样:一段约 0.30 秒的有效图像空白 +内仍有 15 条真实 CAN 测量,不应扩大同步窗口来掩盖。最初加入每视角有界图像工作线程, +但 ROS 端仍保留多线程执行器;`20260915_203104` 恢复导入期间仍因反馈超过一秒暂停。 +这项中间实现没有完成目标,不能算作修复成功。 + +进一步评估保存在 `20260915_203104/review/dispatch_profiling/`:真实 64 条记录编码 +约 6 ms,不支持直接减小导入条数。实机锁计时和线程栈显示,多个 ROS 回调争用同一把 +会话锁,图像队列还在持有自己的锁时反向查询会话。仅在诊断入口替换为单线程 ROS +执行器后,反馈进入回调的延迟由初段中位约 113 ms、P95 247 ms,降到约 9 ms、17 ms; +同一精度、采样和时间策略下已成功复用 8 个合格单元。 + +据此整理公共 ROS 接入,而不更换拟合器或增加型号特例: + +- 控制、反馈、相机消息解析和状态更新由一个 ROS 执行器顺序处理,删除冗余回调组。 +- 每个视角只保留一项执行中的图像任务和一项待处理任务,过载时保留最新待处理图像。 +- 只有 coordinator 接受的新测量才能推进图像队列的时间水位;多帧协议使用最旧通道时间。 + 队列持锁时不调用 coordinator,开始新会话时清空旧水位和未执行图像。 +- 原 50 ms 同步界限继续有效;图像估计仍在锁外,提交结果仍检查会话和运动版本。 +- 图像线程异常传回控制入口,不将线程停止当成正常无图;检查点落盘规则保持原样。 + +调度、ROS 接入、真实测量时间、coordinator 和断点恢复共 51 项针对性检查通过; +图像元数据并发、过期结果提交和隔离计算共 28 项检查通过;标定包构建通过。 +单线程对照运行及后续正式运行的终态以各会话记录为准;这里的延迟改善不能代替 +四轮完整性、独立角点和 JSON+URDF 的最终空间验收。 + + +## 第八轮评估:相机曝光时间与取帧时间分离 + +单线程对照会话 `20260915_203750` 完成 36/36 单元(复用 8 个已验证单元)、0 次暂停, +生成候选 JSON 和由该 JSON 重建的 URDF,但顶部 ID7 的训练安装位置 P95 为 18.285 mm, +超过 3 mm,产物未发布。其余四个实测 Tag 角色通过该项空间检查。共同 URDF 的 39 参数 +原始角点拟合虽然收敛,第四轮仍达 4.779 px,因而没有替换生产拟合器。 + +随后检查图像时间来源,发现相机驱动在 `MV_CC_GetImageBuffer` 返回后使用当前 ROS +时间,不能表示曝光时刻。实测三台相机的设备时钟、40 次独立时钟锁存,以及曝光开始/ +结束事件,确认: + +- 三台取帧时间分别比设备帧时间晚约 16.44 / 16.52 / 16.49 ms;不应靠拟合器吸收该延迟。 +- 帧计数时间与独立曝光开始事件相差约 150 ns;固定曝光时可使用曝光中点时间配对。 +- 当前固件的 `DeviceTimestampIncrement` 返回 100,000,000,实测约 10 ns/tick。 + 设备 XML 的单位说明与此不一致,因此程序启动必须用独立经过时间核验计数单位。 + +`camera_timing.py` 单独负责设备/主机时钟对应和曝光时间换算。每 0.5 秒重新锁存, +选择往返区间最短的有效测量;偶发缓慢事务丢弃后有界重试,不能刷新原两秒有效期。 +错误单位、时钟跳变、计数器重置、重复帧和过期对应均不能伪装成新图像。 +相机驱动在图像及 CameraInfo 中使用同一曝光中点时间,保留每次锁存和每帧换算记录 +`camera_timing_.jsonl`。固定外参和内参不因软件时间修正而改变。 + +共用采集策略升级为 `unified_engine_v7_camera_device_time`,四型号 Profile 及其受保护 +哈希同步更新;旧会话保留诊断用途,不能直接续接到新时间规则。改动没有增加停点、 +调整非线性查找表语义或放宽最终精度门限。 + +时钟、真实三相机记录、ROS 接入、配置、启动图和断点/队列共 57 项检查通过;构建通过。 +随后实际驱动对三台相机各连续发布 242 帧、执行 16 次校时,无时序或时钟错误。 +O6 运动期间另旁路监听 CAN:370 条位置命令的响应均不是原命令回显,仍可作为真实状态 +观测;没有把 L6 的回显过滤规则未经核实套到 O6。 + +完整新策略实采会话为 `20260915_205704`,不复用断点、不使用调度诊断包装。 +共完成 36/36 个单元,0 次人工暂停;拇指弯曲第四轮增加方向和小指第三轮增加方向 +各自动重扫一次。候选 JSON/URDF 已生成,Tag 安装刚性训练检查失败,未发布 manifest。 +正式失败时位置 P95:小指末节 3.108 mm、拇指末节 6.476 mm、拇指弯曲 3.875 mm、 +顶部拇指 10.086 mm。会话中 `review/spatial_assessment/live_run_summary.json` 保留统计。 + +## 第九轮评估:反馈插值契约与空间模型边界 + +反馈拟合使用曝光时刻的连续插值值,报告声明 `piecewise_linear`;原 `JointMapping` +和 `SerializedJointMapping` 却对所有 `_u8` 输入再次取整,使训练和读取语义不同。 +统一规则为:现代反馈曲线连续插值,实际 `command_u8` 量化,历史整数表保持原兼容行为。 +序列化读取在量化前检查输入支持域,不能将越界小数取整后当成合法测量。 +此修改不改变非线性曲线本身、不重拟合零位、不调整采集版本或原始记录。 + +先用相同 39 参数整链诊断做对照:保留原 URDF 骨长、轴向和已拟合曲线,前三轮拟合 +共同基座、允许的零位及固定安装;独立第四轮不参与优化。取消反馈取整后,小指末节 +最大像素误差由 3.390 降至 2.449 px,但仍超过原 1.5 px,因此该诊断模型未接入生产。 +另加有界双向曲线修正的 119 参数实验,第四轮小指末节最大仍为 3.136 px,且出现 +非单调局部曲线;增加参数并没有提供足以替换现有生产求解器的证据。 + +正式公共回放也验证:反馈取整修复不能消除空间不一致。位置 P95 为小指末节 3.080 mm、 +拇指末节 6.489 mm、拇指弯曲 3.877 mm、顶部拇指 10.088 mm;保持失败且未发布。 +原始会话与在线候选文件未覆盖;新回放位于 `20260915_205704/review/spatial_assessment/` +下的 `continuous_production_replay/`。同目录保留诊断脚本、参数、Jacobian 和测试日志。 + +补充整链可行性对照仍只使用前三轮、相同 39 个参数:将训练目标换成按原 2°/3 mm +尺度归一化的三维位姿残差,局部收敛后拇指弯曲零位触及原 20° 边界;第四轮顶部 +位置 P95 仍为 5.148 mm,原始图像最大误差增至 28.718 px。此结果没有证明全局无解, +但否定了将这一局部三维优化结果直接用于替换现有求解器的做法。 +原始保存位姿经受保护的相机变换投回同帧角点,各角色最大误差均小于 0.64 px; +没有发现这条原始公共坐标变换的方向或图像身份错配。检查见 +`continuous_spatial.json`、`common_pose_projection_check.json`。 + +反馈输入、双映射、被动非线性与读取兼容检查 57 项通过;补充真实时间插值、四型号 +合成产物链、JSON 重建、独立角点和标准 URDF 检查 49 项通过,两组有交叉。 +SDK 和标定包最终构建通过,`git diff --check` 通过。 +合成验证不等于其他型号实机通过;本轮 O6 的最终空间精度问题仍未解决。 + +## 第十轮评估:保留跨运动的原始图像证据 + +此前原始角点随任务主机位的姿态采集写入,缺少顶部 ID7 在弯曲中、正面 ID2 在侧摆中 +的完整原始图像。这两块 Tag 已确认属于同一末节;只保存各自任务下的图像不足以独立 +核查共享运动链。公共采集器现独立保存所有机位的有效原始角点,保留原检测质量、 +窗口与会话身份,不依赖 PnP 是否成功。主采样门限、运动计划和稳态点数量均不变。 +同机位图像时间去重只在成功提交时推进;迟到或窗口已撤销的图像不能推进水位。 + +采集策略升级为 `unified_engine_v8_all_view_images`,四型号 Profile 及产品哈希同步更新。 +旧会话不修改哈希、不补造新策略记录。新增采集与公共并发检查 43 项通过,补充采集、 +四型号配置与断点身份检查 58 项通过,两组有交叉;标定包构建通过。 + +新会话 `20260915_213337` 完成 36/36,0 次人工暂停;小指第二轮反向扫描自动重试一次。 +三机位共保存 16,822 个原始图像记录。正式算法仍在 Tag 安装空间一致性处失败, +未发布 manifest。主动零位与上一会话差均小于 0.03°,不能以重复性替代正确性。 + +保持当前源 URDF、相机参数和 Tag 尺寸,前三轮拟合共享基座、零位和每块 Tag 的固定安装, +第四轮检查的新全视角对照仍未通过。39 参数固定曲线模型的最大像素误差约 3.52 px; +359 参数双向单调曲线模型最大约 4.32 px。后者按 SDK 输入均匀选择训练图像, +不再按停留时间重复加权;非负曲线增量和参考输入零约束仍不足以消除残差。 + +静止姿态也出现约 1%~3% 的投影边长偏差,因此另做一次受限的统一骨长尺度诊断。 +其训练最优尺度约 1.0327,第四轮最大误差仍约 4.37 px;没有采用该尺度,没有修改 +源 URDF 或相机文件。原始公共位姿可重投回其源角点,暂未发现转换方向错误。 +上述原型和数值结果均仅保存在 `20260915_213337/review/whole_chain/`,未接入生产求解器。 + +仅用稳态共同视野核查 ID2/ID7 刚体关系时,训练 6 个姿态、留出 7 个姿态全为侧摆, +不能独立确定完整外参。连续扫描保存了两种动作的共同视野,可在原曝光时间上对齐后 +进行独立于 URDF 和曲线的外参一致性诊断;诊断结果不能自动覆盖已记录的相机参数。 + +## 第十一轮评估:尺度一致性与全机位最终图像验收 + +对 ID2/ID7 同末节约束做独立检查,不使用 URDF 尺寸、轴向或 SDK 查找表。 +连续图像按曝光时间对齐,线性/局部三次及留一插值的一致性误差不超过 0.1 px; +训练前三轮 96 个姿态,第四轮 32 个姿态仅检查冻结的参数。每帧刚体位姿作为局部未知量, +两个 Tag 之间始终只有一个固定安装变换。多初值只按训练平方误差选择。 + +| 诊断假设 | 第四轮 front/top 最大误差 | 结论 | +| --- | --- | --- | +| 记录的外参与两个 16 mm Tag | 1.28 / 1.69 px | 与原始图像仍有不一致。 | +| 自由估计 front/top 外参,Tag 均 16 mm | 0.44 / 0.48 px | 外参改变量约 2.43°/23.72 mm;条件三倍标准差约 1.96°/22.24 mm,不能作为精确外参替换。 | +| 记录外参,单独估计 ID7 尺度 | 0.84 / 0.49 px | 固定 ID2 为 16 mm 时 ID7 约 16.52 mm;不是两块 Tag 实测尺寸差的独立结论。 | +| 记录外参,同时估计两个 Tag 尺度 | 0.42 / 0.52 px | 有效边长 ID2 约 16.38 mm、ID7 约 16.52 mm;依赖现有相机模型和刚体/正方形假设,尚不能替代物理尺寸。 | + +自由外参不能解释此前保存的棋盘格图像:其最大误差约 12.93 px,原外参约 0.56 px。 +因此不能因为手指图像误差变小就认定相机移动或自动重写外参。只读采集当前静止画面后, +正面背景匹配显示约 3 px 变化,侧面约 1 px 内;顶部旧图被棋盘格遮满,没有足够共同背景, +不能判断顶部机位是否变化。该检查没有启动 SDK 或驱动手指。 + +另将整链中的所有相机基线或所有 Tag 尺寸分别只增加一个公共尺度参数:前三轮最优值 +约为 0.97226 和 1.03073。两者都明显降低拇指正面误差,但第四轮小指末节最大仍约 +4.29/4.27 px、顶部最大仍约 2.66/2.44 px;没有采用任何尺度修正。 +条件协方差未包含相机、打印尺寸和模型的系统误差,不能用其很小的数值宣称物理尺寸测准。 +已请求分别精测 ID2/ID7 黑框宽高及棋盘格跨五格长度,现有整数尺寸记录不足以区分尺度来源。 +结果在 `review/whole_chain/rigid_*_assessment.json`、`board_camera_comparison.json`、 +`static_background_comparison.json` 和 `all_view_*_scale.json`。 + +程序结构上,只有任务主机位参与最终图像验收会漏掉同一 Tag 在其他动作下的不一致。 +现将相机身份核验和原始图像索引集中在 `ImageEvidence`,反馈和稳态指令各自使用独立窗口。 +前三轮、未通过尝试和无同步反馈的原始图像不能成为最终验收测量;主观测必须在新原始记录 +中找到完全一致的角点、SDK 值和方向。其他动作中的原始角点也直接进入冻结 JSON/URDF 投影, +不伪造 PnP 位姿,不参与训练,不增加运动或停点。历史主图像读取接口保留原证据语义。 + +实际 O6 输入核对:反馈原有 1,818 个主观测全部一致,增加 3,270 个其他观测; +稳态指令原有 312 个主观测全部一致,增加 677 个观测。此结果仅证明证据绑定正确, +不代表最终空间精度通过。专项和现有产物流程 36 项通过,补充最终检查、四型号合成产物、 +JSON 重建及非有限输入反例 34 项通过;两组有交叉,不相加为独立测试总数。 +标定包构建通过;实际数据证据核对和测试日志保存在 +`review/whole_chain/all_view_final_source_verification.json` 与 `final_image_validation/`。 + +### 剩余运动残差的解释边界 + +固定上述未启用的候选相机尺度与整链几何,仅逐帧调整活动关节角度进行定位。 +小指两关节最大改变量约 0.63°/0.49°,侧摆约 0.38°;图像最大残差分别可降至 +0.43/0.94 px 和 0.47 px。最大原始残差集中在 SDK 约 244 的运动区间; +同一 Tag 静止、其他手指运动时的误差显著更小。 + +这项实验在每个被检查图像上估计局部角度,明确**不是独立第四轮验收**,也不证明 +其条件几何正确。它提示剩余像素误差中包含运动映射误差,不能据此继续修改骨长或零位。 +前三轮相同输入位置的角度改变量有约 0.1° 的轮间差,增加曲线节点也未必能消除。 +后续需分别评估反馈分辨率、允许的运动映射误差与图像测量误差;不能直接把 PnP 的 +像素测量门限等同于整条运动链的角度/位置精度要求。本轮没有修改任何验收门限。 +完整逐帧结果见 `review/whole_chain/local_angle_residuals.json`。 + +## 第十二轮评估:分离图像测量误差与运动精度 + +先用完全独立的已知真值验证规则:单关节运动存在恒定 0.5°、0.873 mm 偏差, +满足原角度和位置要求。仅将相机焦距从 1000 改为 3500 px,最终 FK 的最大投影误差 +就从 1.10 变成 3.84 px;原规则因此对相同物理精度作出不同结论。测量位姿对原始角点 +的误差均近于零。这证明 PnP 的 1.5 px 图像测量门限被误用于含运动映射误差的总投影量。 + +公共验收现在分为以下职责: + +1. 发布器原有的独立三维反馈、指令验收仍先执行,角度 MAE≤1°、P95≤2°、最大≤3°, + 位置 P95≤3 mm。非线性被动关节仍读取完整 JSON,不以 mimic 近似替代。 +2. `image_acceptance.py` 将独立位姿与原始角点、SDK 输入、方向和图像身份直接绑定。 + 测量位姿重投影 RMS≤1.5 px;最终 FK 的总投影误差保留为单独统计,不据此改变物理精度要求。 +3. 额外视角无独立三维测量时,`image_consistency.py` 查找一个能解释原始角点的局部刚性位姿, + 直接核验其像素残差及相对最终 FK 的物理位移。数值见证仅说明图像与容差相容, + **不是真实位姿测量,不证明最优,不替代独立三维精度**。求解器返回成功也不能单独使其通过。 + 不回写或重新估计共享的相机、安装、基座、零位和曲线;按运动任务分别统计,防止静止帧稀释误差。 +4. manifest 明确记录 `independent_measurement_and_physical_consistency_v2`, + 独立精度与额外机位相容性分开报告。缺失独立位姿不能由数值见证补齐。 + +解析投影 Jacobian 已用独立有限差分检查。已知真值、不同焦距、物理超差、角点/相机篡改、 +证据身份和静止图像稀释等 42 项专项检查通过;四型号合成产物、非线性、JSON 重建及原物理 +验收共 36 项通过;最后的规则版本/任务身份补充检查 15 项通过。测试集合有交叉,不相加。 +标定包构建通过,`git diff --check` 通过。没有增加实机轮次或停点。 + +最新真实 O6 的 1,818 个第四轮及 312 个稳态主观测均核验了真实保存位姿与原始角点, +最大误差分别为 0.613/0.616 px。此检查没有拟合任何参数,属于测量证据核验,不是产物 PASS。 +复算原训练安装检查仍得到拇指位置 P95 为 5.371/3.591/11.352 mm,仍失败,没有 manifest。 +尺度/相机输入尚待现场精测区分,未采用之前任何诊断尺度,也未直接替换整链求解器。 +已知真值脚本使用保存的旧规则源码复现反例,当前规则由生产测试验证;全部记录位于 +`20260915_213337/review/whole_chain/image_error_budget/`。 + +## 第十三轮评估:轴线参考点不能决定零位 + +一条转轴可用轴上任意一点表示;沿轴移动该点不会改变物理几何。当前 O6 的旧零位公式 +先把两轴参考点的差直接投到图像平面,没有消除沿轴任意坐标。相机斜看转轴时,这个 +任意坐标被误解释成弯曲零位。旧代码已为 O12 增加可选开关,但 O6/L6/G20 仍使用原公式。 + +在真实 O6 的相同轴线输入上,仅将被动轴参考点沿轴移动 ±20 mm:拇指弯曲零位从 +−9.8736° 变成 +1.0529° 或 −16.1650°,三个结果竟都通过原轴线角度检查。整个实验没有 +改变相机、Tag 尺寸、运动图像或真实轴线,足以证明程序中存在几何表示相关的错误。 + +公共 `ObservationGeometry.phase_error` 现始终先取两轴的垂直间距向量,再投影到相机平面。 +移除了 `project_axis_gauge_before_image` 型号开关及其编译参数;O12 删除已无必要的 YAML +声明并同步产品哈希,其数学行为保持原先已启用的正确规则。O6 的采集配置与受保护输入 +不变,可以用原四轮数据离线复算,无须增加运动。 + +独立测试使用四型号的原始 URDF、自行生成已知 0.07 rad 零位,改变相机视角、共同坐标系, +并任意移动预测和观测的轴上参考点;所有平行轴对均恢复相同真值。源码专项、四型号完整 +合成产物链、配置与 O12 安装/机位变化共 24 项检查通过;源代码的轴线/求解顺序 9 项通过。 +构建后另核对实际加载的安装包与源码字节一致,安装包的轴线、图像与物理门限检查共 +51 项通过,避免用旧安装文件检验新修改。各测试集合有交叉,不相加。 + +正式回放保留原始输入身份检查,输出到 `review/whole_chain/axis_gauge_production_replay/`。 +拇指弯曲零位约 −0.5384°、小指约 −1.8044°,不再依赖轴上参考点;侧摆仍约 14.5179°。 +JSON 已先保存并被读取生成候选 URDF;空间安装检查仍失败,位置 P95 如页首所列, +未生成 manifest。这项数学修正没有消除后续空间不一致,也不能替代尚待核实的尺度输入。 + +对最近两次已保存的轴线输入使用同一修正公式,侧摆、拇指弯曲、小指零位差分别为 +0.02955°、0.01044°、0.02942°。这是条件零位的重复性对照,不是绝对精度通过; +没有改写旧 v7 会话的采集策略或将其伪装成 v8 发布。结果见 `corrected_zero_repeatability.json`。 + +旧公式及配置已备份到 `axis_gauge_before/`,`assess_axis_line_gauge.py` 从备份源码复现 +历史反例;不将旧公式或型号条件保留在生产求解器中。数值对照见 `axis_line_gauge_assessment.json`, +源码、安装包测试及构建日志均在同一 `review/whole_chain/` 目录。 + +## 第十四轮评估:独立双目尺度检查 + +先使用已冻结的 ID2/ID7 刚体关系,在第四轮 32 个共同观测姿态上仅估计逐帧刚体位姿, +不使用 URDF 或 SDK 曲线。保持原相机与两个 16 mm Tag 时,front/top 图像最大误差仍为 +1.280/1.693 px;与原主机位三维测量的位置 P95 分别相差 26.84/19.09 mm。 +此前“调整外参”或“调整 Tag 尺寸”的条件模型均能把最大图像误差降至约 0.5 px, +但得到不同的三维位置,且部分与原主测量相差约 20~33 mm。因此不能将其中任意一个 +条件重建当成独立真值,亦不能用小图像残差证明最终空间精度。共享参数均未在第四轮更新。 +对照见 `stereo_primary_measurement_comparison.json`。 + +只读保存的静止画面还发现:侧面相机可以同时看见正面 ID1、ID2。用两机位对同一个 Tag +的四个对应角点直接三角测量,不预设 Tag 尺寸、刚体安装、URDF 或 LUT。两帧曝光相差 +6.55 ms,保存时没有 SDK 控制进程、机械手静止。尺度仅来自记录的 front/side 外参基线。 + +- ID1 两组对边平均长度为 **16.496 / 16.503 mm**;两机位重投影 RMS 为 **0.536 / 0.443 px**。 + 这项独立对照进一步提示当前尺度与“16 mm”的记录不一致。 +- ID2 两组对边约 **16.593 / 16.713 mm**,但重投影 RMS 为 **1.583 / 1.412 px**, + 最大单角误差约 2.28 px,不能将该尺寸估计作为已通过质量检查的测量。 + +此检查仍依赖棋盘格确定的相机基线尺度,不能独立区分打印 Tag 偏大与棋盘格尺度偏差。 +没有把 ID1 的尺寸复制给其他 Tag,没有更改原相机或尺寸配置,没有增加整手运动。 +原始 PNG、相机元数据、逐角三维坐标及误差均可复查,见 `current_camera_images/`、 +`same_tag_stereo_assessment.json` 与相应同名诊断脚本。 + +下一项必要现场证据仍是已请求的精确黑框尺寸与棋盘格跨五格长度。现有图像能发现 +尺度不一致,但不足以无假设地决定应修改哪一项物理输入。尺寸证据到位后,应先核实输入、 +评估与原始数据的兼容性,再复算整链;不能把某个局部拟合最优值直接写入生产配置。 + +## 第十五轮评估:固定已确认尺寸,排除图像坐标处理与局部拟合误导 + +2026-09-16 用户再次明确:全部 Tag 黑色码区为 16×16 mm,棋盘格为 27×27 mm。 +这解除上一轮的尺寸信息等待;本轮按用户确认值作为固定物理输入,不把双目条件估计 +当作新的 Tag 尺寸。以下诊断记录在 `20260915_213337/review/whole_chain/confirmed_dimensions_audit/`。 + +### 1. 原始图像与去畸变图像的数值核验 + +仅启动相机,机械手不运动;保存同一曝光时间戳的原始和去畸变 PNG。按 CameraInfo 的 +K、D、R、P 独立执行 OpenCV remap,front/side/top 三机位与 image_proc 输出的最大、平均 +灰度差均为 **0**。程序以 P 解释 image_rect、棋盘物点间距为 0.027 m;没有发现单位错误、 +缩放裁剪、重复去畸变或原始 K 与去畸变图像混用。该证据只核验软件坐标契约,不证明 +K/D 本身就是准确物理内参。结果见 `raw_rect_consistency.json`、`raw_rect_capture/`。 + +新静止图像中 ID1 的两组对边平均为 **16.5057/16.5078 mm**,两机位重投影 RMS 为 +**0.830/0.687 px**。ID2 的重投影 RMS 为 1.836/1.635 px,继续不作为合格尺度测量。 +因此上一轮发现的矛盾可再次观测,并非一次检测结果;见 `fresh_same_tag_stereo.json`。 + +### 2. 棋盘低像素误差不能单独决定新相机参数 + +使用已有原始角点,保持 27 mm 间距,对正面/侧面及正面/顶部训练对分别联合估计内参。 +两组的留出误差均下降,但同一 front 的主点横坐标分别约 866/1078 px,明显不同。 +又将单机位训练集分为互不重叠子集:side 的部分子集可给出明显不同焦距/主点,同时 +在同一留出图像上仍保持约 0.45~0.48 px 的 RMS。不能凭这个像素指标选择物理参数。 +结果见 `joint_intrinsics.json`、`intrinsic_stability.json`;未修改相机配置。 + +进一步以 OpenCV object-releasing 仅在训练图像中估计一个固定棋盘角点形状,保持 +第一行两端 7×27 mm 间距。三个机位独立估计的形状均存在亚毫米偏差;其中 front、side +估出的形状相近。冻结形状后,未参与拟合的单机位图像 RMS 分别从 +**0.276/0.465/0.317 px** 降为 **0.133/0.168/0.138 px**。 +这提示理想平面棋盘假设可能影响内参,但仍是条件模型证据,不等于已测准棋盘变形。 + +为避免“各相机自由拟合都能变好”的误判,另只采用 front 训练图像估出的同一形状, +重新估计 side/top 内参,再用 19:25 棋盘原角点训练新的条件外参(每四组留一组验证)。 +side 的留出双目 RMS 最大约 0.395/0.383 px;随后检查从未参加相机拟合的新静止 Tag, +ID1 两组对边仍为 **16.5038/16.5058 mm**。棋盘误差改善没有解决目标尺度矛盾,故明确 +拒绝启用这组候选参数。见 `board_shape.json`、`shape_camera_transfer.json`。 + +### 当前下一步 + +已请求保持手和相机不动,将棋盘置于手旁,使 front/side 同时看到完整棋盘及 ID1, +采集同一时段的静止画面。这可直接核查已确认的两种尺度,减少先标棋盘、后拍手之间 +场景变化的影响。另询问棋盘载体是否刚性平整,作为几何假设的现场证据。 +本轮不增加整手运动、不启用候选相机参数、不改变 Tag 尺寸或精度门限;原数据和受保护 +配置保留。整手发布条件仍未达成,无新的合格 manifest。 + +### 当日现场反馈与贴平后检查 + +用户补充“棋盘没有完全弯曲变形,tag贴纸有弯曲不平整”,随后确认“已贴平固定”。 +因此不能把之前的平面 Tag 位姿当作已证实准确的物理测量,也不能把棋盘条件拟合 +直接解释成全部误差的原因。正式复标需使用处理后的新图像,而非沿用旧 Tag 安装结果。 + +10:09 的贴平后静止对照,front/side 曝光差为 1.763 ms。ID2 的双目重投影 RMS 为 +1.349/1.201 px,较处理前改善;ID1 为 2.254/1.872 px,仍不满足图像质量门限。 +两者的条件尺度仍约 16.5 mm,不能据此宣布测量链修复完成。数据保存在 +`confirmed_dimensions_audit/flattened_tags_20260916_100921/`。 + +用户表示棋盘已摆好后进行了实际拍摄,并持续开启无机械手控制的双目预览;至 10:10 +两路画面实际均未出现棋盘。已提供正面/侧面画面帮助摆放,没有将空缺的棋盘观测标成 +成功采集。新的同帧棋盘/Tag 核验脚本已用已知真值验证:正确相机下恢复 16 mm 各边, +误差小于 1e−5 mm;该合成检查不是现场相机验证。下一步仍需让棋盘实际进入两个机位, +目前未重新启动整手运动或输出新的合格产物。诊断汇总与受保护输入哈希见 +`confirmed_dimensions_audit/audit_summary.json`。 + +## 第十六轮评估:分时核验棋盘与 Tag,取消不必要的同框限制 + +固定相机外参时,棋盘负责估计相机之间的刚体变换;Tag 的位置和姿态在检查时独立 +估计。因此棋盘与机械手不必同时出现在画面中,手在两次拍摄间也不必保持原位置。 +此前把同框设为必要条件,给现场带来了不必要的摆放限制,现已取消。只需相机及镜头 +设置在两段采集中保持固定。这一修改属于诊断采集流程,不改变整手正式运动流程。 + +10:21~10:22 保存四组正面/侧面完整棋盘原图: +`board_placement_preview_20260916_102131`、`102213`、`102215`、`102217`。 +原始双目曝光差 10.79~10.99 ms;每组以已确认的 27 mm 棋盘估计条件相机变换, +棋盘重投影 RMS 为 front 0.281~0.364 px、side 0.438~0.571 px。 +四次相对首帧的变化最大为 0.0272°、0.458 mm。姿态相近的四组只说明条件重复性, +不能充当完整多姿态外参标定,也没有写入活动相机配置。 + +临时预览的自动保存器还曾设置 20 ms 配对条件,而一次启动后的相机帧相位差约 +24.66 ms,造成没有进入检测。这是诊断脚本的问题,已采用原相机核验的 50 ms 配对 +范围,并保留真实曝光时间;正式图像与空间精度门限不变。另一个临时触发条件要求 +全部棋盘角点连续 2 秒变化均小于 0.4 px,未触发自动保存。上述四组是另行直接保存的 +原始画面,经离线图像核验后使用;不能把预览中的 `saved_count=0` 改记成自动触发成功。 +后续共面检查直接保存可见原图,在原图上评估几何质量,不先用这种触发条件丢弃证据。 + +用户移开棋盘、放回 O6 后,10:24 保存新的 Tag 图像,曝光差 11.75 ms,位于 +`joint_board_tag_20260916_102403/`。分别使用上述四组棋盘估计的相机变换,得到: + +- ID1 两组对边平均约 **16.487~16.500 mm**;自由角点双目重投影 RMS 为 + front 1.040~1.107 px、side 0.878~0.935 px。 +- 强制 ID1 为 16 mm 平面方形时,front RMS 仍为 **1.629~1.677 px**,超过 1.5 px。 +- ID2 两组对边约 **16.499~16.585 mm**;自由角点的双目 RMS 约 0.41~0.51 px。 + +新旧手位姿分别估计,不参与棋盘相机参数的拟合。使用已知真值的另一个位置/姿态 +Tag 验证过这一分时检查方法,两种来源均恢复 16 mm,误差小于 1e−5 mm。 +结果记录在各棋盘目录的 `board_tag_consistency_joint_board_tag_20260916_102403.json`。 +这仍不足以单独判定应改 Tag 尺寸、棋盘尺寸还是相机模型;没有因此运行新一轮整手运动。 + +### 共面长度比例的独立检查 + +用户提供同批备用 Tag,并把 ID1 固定在棋盘同一块板上。10:27 的三个原始正面图像 +位于 `coplanar_capture_20260916_102710/frame_00` 至 `frame_02`。图中左侧的备用 ID1 +与棋盘共面;右侧手掌 ID0 不共面,不将其按棋盘平面换算的数值解释为尺寸。 + +使用棋盘格交错的 20 个角点拟合平面单应变换,另 20 个角点检查投影误差,不使用 +相机外参、URDF、SDK 或关节曲线。原始像素直接计算的 ID1 两组对边约 +**16.474~16.478 / 16.595~16.602 mm**,留出棋盘角点 RMS 约 0.186~0.191 px。 +用当前内参去畸变后约 **16.680~16.685 / 16.593~16.600 mm**;旧内参、未采用的 +棋盘形状内参仅作预先规定的敏感性对照,结果也记录在同一报告中,不按 Tag 尺寸选模型。 + +该 ID1 接近原图左边缘,畸变模型之间仍有约 0.2 mm 的差异,所以未据此改配置。 +随后请用户将备用 ID1 移到棋盘上方中间,再核验边缘畸变的影响。 + +### 中央位置复核与实测尺寸的矛盾 + +用户将备用 ID1 贴到棋盘中间上方后,10:43 保存三个新的原始正面图像,位于 +`coplanar_capture_20260916_104349/frame_00` 至 `frame_02`。图中 ID1 位于同一底板上方, +纸张棋盘完整可见;手掌 ID0 仍不与棋盘共面,不解释其尺寸换算值。 + +沿用预先规定的棋盘交错训练/留出划分,三个图像的结果为: + +- 原始像素直接计算的 ID1 两组对边为 **16.511~16.513 / 16.594~16.602 mm**; + 留出棋盘角点 RMS 为 **0.188~0.197 px**。 +- 当前内参去畸变后为 **16.521~16.523 / 16.636~16.645 mm**,相较原始像素仅变化约 + 0.01 / 0.04 mm,不能解释相对 16 mm 的约 0.5 mm 差异。 +- 另用传统棋盘角点检测加亚像素细化,并分别使用全部角点、靠近 Tag 的三行或两行 + 拟合平面变换;不同检测方法和网格子集的边长变化小于 0.1 mm,比例差异仍存在。 + 这是同一图像上的方法敏感性检查,不是新增独立实物样本,也不是新的合格标定。 + +结果见各帧的 `coplanar_scale_assessment.json` 及汇总目录中的 +`central_coplanar_sensitivity.json`。上述长度全部以 27 mm 棋盘间距、平面棋盘及 +Tag 与棋盘共面为条件;图像本身不能无条件证明 Tag 的绝对毫米尺寸或两者物理共面。 +这项检查未使用外参、URDF 或关节拟合,不能通过增加关节拟合参数解决其比例矛盾。 + +用户随后确认 16 mm 和 27 mm 均为打印后实物测量值。保留这两个生产输入,不能将 +图像条件换算直接盖过实测值并断定打印错误。已请求测量工具、Tag 黑框宽高及棋盘连续 +五格的未取整总长度,以区分测量精度与几何假设的问题。此次仅进行静止图像与离线 +检查,没有新增整手运动,没有修改相机、尺寸或验收门限,没有生成合格发布 manifest。 + +## 第十七轮评估:依据实物复测修正 Tag 尺寸 + +用户再次量取后明确:“tag 是 16.5×16.5 mm,棋盘还是 27×27 mm”。这提供了独立的 +实物输入修正依据,与先前双目约 16.49 mm、中央共面约 16.51×16.60 mm 基本一致。 +不是从 URDF 拟合残差反推并选择一个易于通过的尺寸。 + +原先按 16 mm 计算 16.5 mm 的方形 Tag,会在同一单目姿态解下将平移距离缩小到 +16/16.5,约低估 3.03%;跨相机变换中的基线平移来自 27 mm 棋盘,并不会随之缩放。 +这构成明确的输入尺度不一致,不能通过增加运动曲线参数来合理消除。 + +修改范围仅为当前 O6 的以下配置及指纹: + +- `profiles/o6_right_8.yaml`:8 个 Tag 均显式声明 `size_m: 0.0165`。 +- `o6_right_8_tags.yaml`:检测器默认尺寸和每个 ID 的尺寸均为 0.0165 m。 +- `o6_three_camera_calibration.yaml`:标定节点尺寸及全部 ID 覆盖值同步为 0.0165 m。 +- `o6_right_product.yaml`:更新以上三个受保护文件的 SHA-256。 + +程序已有 Profile、检测器和标定节点的尺寸一致性检查,本次无需增加型号特判或 +修改拟合算法。其他型号/打印批次没有新的实测依据,不自动替换其配置。棋盘尺寸未变, +没有因此修改相机内外参、源 URDF、旧采集文件或验收门限。非线性关系仍由完整 JSON +和修正 URDF 配合表达。改前配置与改后指纹见 +`20260915_213337/review/whole_chain/tag_size_correction_20260916/`。 + +28 项配置加载、实际 launch 参数和 PnP 参数测试通过;已重新构建安装,核实四个 +配置文件与源码逐字节一致,`--validate-only` 通过。用户移开棋盘及备用 ID1 并确认 +O6 可运动后,启动新会话 `20260916_105528`,显式 `--no-resume` 完整采集。 +最终是否合格须以该会话的独立验收及发布文件为准,不能仅凭尺寸改正确就声明成功。 + +### 新会话结果与收尾内存问题 + +36 个采集单元全部通过,小指第三轮 increasing 方向第一次存在反馈分箱空白 42, +按原最大空白 16 的门限自动重扫一次后通过。完整采集约 6 分 47 秒;保存 15,167 个 +原始图像记录、6,828 个关节样本及 15,629 个 SDK 反馈样本。11:02:20 完成最后采集单元, +随后开始安全回位与收尾。11:05:28 系统内核明确记录 OOM、4 GiB swap 耗尽,终止了 +一个 VS Code 进程;三相机同时出现长时间时钟读取中断,SDK 反馈超时,终端随后发生 +BrokenPipeError。不能把该现象归因于机械手硬件,也不能将此会话记为在线无暂停完成。 + +原始日志完整保留,正式 `--offline-raw` 流程验证了配置指纹、全部采集单元、运动来源、 +独立第四轮与稳态验证数据,生成 `offline_165mm/` 下的 JSON、URDF、报告和 +`release_manifest.json`,退出码 0。JSON/URDF 的 SHA-256 与清单相同;已从保存 JSON +再独立调用重建 CLI,得到逐字节相同的 URDF。原来的角度 MAE/P95/max 1°/2°/3°、 +位置 P95 3 mm 及对应图像门限没有修改。 + +同时发现 `read_journal_prefix` 先整文件读取、再 `splitlines()`,在解码对象之外还保留 +两份原始文本。这份日志为 318,772,511 字节,额外副本会显著增加相机仍运行时的收尾 +峰值。已改为按冻结字节边界逐行解析,保留全部记录、顺序和不可越过边界的校验; +不通过丢数据或修改安全超时规避问题。新增 16 MiB 日志的临时缓冲内存上限回归检查, +并验证短文件与非对象记录拒绝;独立进程、取消、真实 finalizer 等共 9 项测试通过。 +已重新构建安装,进一步用同一实采数据检查独立收尾进程与已验收文件的一致性。 + +修改后的已安装独立进程以 `require_motion_evidence=True` 处理同一份完整原始数据, +约 **55.21 秒**完成,子进程峰值 RSS 为 **1,344,164 KiB(约 1.28 GiB)**; +JSON、URDF 两个文件的 SHA-256 均与正式离线验收版本相同。该检查没有启动相机或 SDK, +也没有重新运动机械手,不能单凭它宣称修复后在线全过程已经完成第二次实机验证。 +报告为 `20260916_105528/isolated_finalization_verification.json`。 + +通过已验收文件的字节/哈希与 JSON→URDF 对应关系复核后,使用现有发布器将相同文件 +发布到产品根目录的 `20260916_105528_verified_165mm/`,并更新 `latest_partial_passed`。 +原始中断会话、离线验收目录与独立进程检查目录全部保留;没有把原始 PAUSED 状态改为 +COMPLETE。正式目录中的 `release_origin.json` 记录已验收来源;完整结论和实际采集范围 +见 `20260916_105528/verification_summary.json`。当前布局直接测量拇指和小指共 5 个关节, +食指、中指、无名指共 6 个关节的运动曲线按原配置从小指迁移,不宣称这些关节独立实测。 + +## 第十八轮评估:被动关节由 JSON 提供角度,消除线性导出冲突 + +用户指出拇指 IP 在修正 URDF 中明显弯得更小。复核确认,原 URDF 的线性倍率为 1.86, +旧修正产物的倍率为 1.16556243135602,而实测 JSON 的 IP/pitch 比例随行程约从 1.71 +变化到 1.94。父子下限分别为 -0.00172483145889436、-0.0020103987491093 rad, +旧“共同零点+整段不越界”约束把这两个小负端点的比值变成全行程倍率上限。 +这不是非线性 JSON 的拟合失败,而是将独立实测曲线再次约束为一条直线的导出设计问题。 +旧完整 JSON+自定义 FK 验收虽通过,标准 ROS mimic 仍会覆盖被动角,因此显示与验收不一致。 + +评估后采用统一的 JSON 角度来源规则,不修补 O6 的某个倍率: + +- 新 v3 修正 URDF 移除原被动关节的 ``,全部关节角由 JSON 明确给出。 + 父子链、转轴、连杆、零位与限位保持原有受检语义;原被动关节没有新增实机驱动通道。 +- `urdf_correction` 升为版本 2,保存 `joint_angle_source: calibration_json`、 + `passive_joint_sources` 和明确的删除操作。只有完整覆盖运动关节、来源与原 URDF 一致、 + 被动关节与来源关节共享 SDK 通道时,才能授权移除;其他数值编辑仍使用 Profile 原授权。 + 导出版本与采集配置分开,未修改 Profile 指纹或原始记录来绕过保护。 +- JSON 先保存,重新读取后重建 URDF;普通 FK、角度/位置验证及原始图像验收使用该最终文件。 + 标准 `robot_state_publisher` 可直接接收全部关节状态,不需要忽略 mimic 的定制算法。 +- 线性拟合仅保留报告诊断,标记 `diagnostic_only_not_exported`,不再按限位限制拟合倍率, + 不参与导出和运行角度。旧 correction v1 的精确重建和旧读取方式继续兼容。 + +专项回归覆盖微小负限位、非线性行程、普通 FK、JSON 重建字节一致性、错通道、缺失曲线、 +非法来源、未经授权删除与拓扑改动拒绝。真实 ROS TF 测试在独立命名空间验证三个弯曲点, +未连接 SDK;末节角度不再被覆盖。O6/G20/L6/O12 的合成完整收尾、独立图像验收、迁移来源、 +双向映射及旧格式检查通过;这不是其他三型号的新增实机精度证明。 + +已构建安装,并用 `20260916_105528/raw_samples.jsonl` 的完整 16.5 mm Tag 实采数据 +执行正式离线回放与发布,退出码 0。新目录为 +`calibration_output/O6_RIGHT_001/20260916_123347_json_driven_165mm/`, +`latest_partial_passed` 已更新。原来的角度、位置和原始图像门限全部通过,没有放宽。 + +对新旧发布文件逐项比较:11 个关节的全部指令表、反馈映射、零位和限位不变; +URDF 的结构差异仅为移除 5 个线性 mimic。重新读取新 JSON 重建得到逐字节相同的 URDF。 +相同输入的拇指结果如下(单位:度): + +| SDK 指令 | CMC pitch | 旧 URDF 线性 IP | 新 JSON+URDF IP | +| --- | ---: | ---: | ---: | +| 192 | 9.3781 | 10.9307 | 16.0523 | +| 128 | 18.8914 | 22.0191 | 33.0907 | +| 64 | 27.2887 | 31.8067 | 49.6742 | +| 0 | 34.2391 | 39.9078 | 66.3515 | + +复核脚本与结果保存在原会话的 `verify_json_driven_export.py`、 +`json_driven_export_verification.json`,正式回放日志为 `json_driven_replay.log`。 +旧发布三文件的哈希未变。本次没有重新运动机械手,沿用原 5 个独立观测关节、6 个迁移关节 +及 CAD 零位假设的精度边界。使用新产物时须通过 JSON 同时驱动全部关节;单独拖动 URDF +父关节滑条不再自动联动末节,外部仿真也须移除与 JSON 冲突的旧线性更新逻辑。 + +## 第十九轮评估:按用户要求保留标准 mimic 近似联动 + +用户明确使用 `https://viewer.robotsfan.com/` 直接预览 URDF,并进一步说明: +非线性关节也必须保留 `` 自动联动,单独 URDF 尽量接近实机; +需要实测非线性结果时再由标定 JSON 查找表控制。上一轮删除 mimic 的正式导出方向 +不符合这一使用要求。已核对网站当前构建及官方 URDFAdapter:滑条直接设置 URDF 关节, +没有本项目标定 JSON 的读取逻辑,不能期望它自动获得 JSON 中的非线性曲线。 + +### 方案评估与实现 + +标准 mimic 使用 `q_child = a*q_parent+b`。将 `b` 强制为 0,再约束父子全行程, +使拇指的两个微小负端点决定全局倍率上限,这是旧版 1.16556 倍率失真的原因。 +仅恢复 ``、手填倍率或扩大限位都不能解决模型与约束之间的矛盾。 + +现在使用两端子关节角度作为拟合变量: + +- 父关节输出范围 `[p0,p1]` 保持不变,拟合 `c0=q_child(p0)`、`c1=q_child(p1)`。 +- 两个变量分别受子关节原输出限位约束;区间内的线性插值自然不会越界。 +- 由 `a=(c1-c0)/(p1-p0)`、`b=c0-a*p0` 导出标准 mimic 倍率和偏移。 +- 沿用前三轮成对视觉样本与稳健损失拟合,第四轮只验证;不改变 SDK→角度表、 + 坐标零位、Tag 安装、相机参数或训练支持的限位。 +- `b` 是线性近似在零点的误差,不是新测出的机械零位。报告同时保存 + `baseline_error_rad`、`physical_zero_correction: false` 和实际残差。 + +生产 `urdf_correction` 使用版本 3,并声明 +`mimic_policy: range_bounded_affine_approximation`、`joint_angle_source: calibration_json`。 +仍先保存 JSON,再读取生成 URDF。所有原 mimic 来源与 XML 拓扑保留; +数值修改继续使用已有 Profile 授权,没有更改采集指纹。版本 1、2 产物保留重建兼容, +不原地覆盖。完整 JSON 回放显式使用非线性被动角,URDF 单独回放使用 mimic; +两者分别报告精度,线性近似不能冒充 JSON 的实测精度。 + +专项检查覆盖微小负限位不压低全程倍率、正负运动方向、全行程限位、留出样本禁止参与拟合、 +重建字节一致性及缺失/降级导出规则拒绝。真实标准 ROS TF 检查只给父关节角度, +验证被动关节按导出 mimic 联动;历史无 mimic 版本的独立关节测试也继续通过。 + +### O6 实采数据复验与发布结果 + +相关 121 项检查通过,覆盖四型号合成产物、迁移来源、双向表、历史格式及标准 ROS 联动。 +构建安装后,以同一份完整 `20260916_105528/raw_samples.jsonl` 正式离线回放,退出码 0, +生成并发布 `20260916_124718_mimic_json_165mm/`,更新 `latest_partial_passed`。 +JSON+URDF 的角度、位置及独立原始图像验收通过,原精度门限不变。 + +新旧三版的全部 11 关节指令/反馈曲线、几何零位、限位逐项一致;5 组 mimic 来源与 +原始 CAD 相同。拇指 IP 倍率为 **1.86604745134131**,偏移为 +**0.00120821859875384 rad(0.06923°)**。小指及三个迁移末节的倍率为 +**0.870364582419037**,偏移为 **0.0348981257618717 rad(1.99952°)**。 +这些偏移是近似关系在零点的残差,完整 JSON 的 baseline 仍为 0。 + +| SDK 指令 | CMC pitch | 旧 URDF IP | 新 mimic IP | JSON IP | +| --- | ---: | ---: | ---: | ---: | +| 255 | 0.0000° | 0.0000° | 0.0692° | 0.0000° | +| 192 | 9.3781° | 10.9307° | 17.5691° | 16.0523° | +| 128 | 18.8914° | 22.0191° | 35.3214° | 33.0907° | +| 64 | 27.2887° | 31.8067° | 50.9912° | 49.6742° | +| 0 | 34.2391° | 39.9078° | 63.9611° | 66.3515° | + +相对 JSON 的 256 项指令网格,拇指线性近似最大差约 **2.3905°**,小指及对应迁移末节 +最大差约 **4.1965°**。这是两个表示之间的差异,不是新增的实机独立精度测量; +独立留出结果分别保存在 manifest 的 `full_json_urdf_holdout` 和 `urdf_mimic_approximation`。 +因此网站中的联动预览仍是近似,需要实测非线性结果时必须使用 JSON。 + +另行读取新 JSON 重建得到逐字节相同的 URDF。所有旧发布文件哈希不变, +没有重新运动机械手,也没有改写旧采集结果。复核脚本、结果及正式回放日志分别为 +原始会话下的 `verify_affine_mimic_export.py`、`affine_mimic_export_verification.json`、 +`affine_mimic_replay.log`。本次仍只独立观测拇指、小指 5 个关节,其他 6 个关节沿用原迁移关系。 + +## 第二十轮评估:全型号保留原始 mimic,实测非线性由 JSON 提供 + +用户确认统一采用原始 mimic 作为基础联动,暂时取消正式标定中的 mimic 参数优化。 +该规则替代第十九轮新产物的拟合策略;历史产物保持原样及原有读取语义。 + +### 全型号依据与边界 + +检查 L6、O6、O12、G20 的左右手原始 URDF,以及当前四套右手产品配置。 +L6/O6 部分曲线从小指迁移,O12 包含两级 mimic 链和无名指曲线迁移,G20 配置覆盖全部实测关节。 +左右手系数不能互相套用:O6 拇指左右分别为 2.29/1.86,G20 为 1.02/1.03。 +原始文件也不等于限位自洽:按原始右手父关节全行程,L6 拇指约超出子限位 3.6795°, +O12 无名指中节约 0.7162°,G20 四个 DIP 约 0.4297°。这些是文件内冲突,不是硬件越限结论。 +O6 的已声明实测范围策略不能自动授予其他型号放宽 CAD 硬限位的权利。 + +### 统一实现 + +- 新 correction schema v4:`mimic_policy=preserve_source_mimic`,`joint_angle_source=calibration_json`。 +- measured 正式拟合及收尾不再调用 mimic 参数优化,原 mimic 来源、倍率、偏移及省略属性全部保留。 +- `source_mimic.py` 统一计算负倍率、非零偏移及串联关系的可达范围,不含型号分支。 +- `mimic_envelopes` 单独记录基础范围、联动可达范围和最小导出包络;不会改写实测范围证据。 +- 包络扩展只适用于已有实测范围策略;默认 CAD 硬限位和独立安全限位继续生效,冲突明确报告关节及范围。 +- 零位仍修正 origin。原始 mimic 只作为修正坐标中的预览近似,精确 FK 使用 JSON 提供的各关节角。 +- 新 JSON 禁止请求 mimic 参数修改或删除;最终验收对照受保护原文件核对 mimic 元素和可信范围证据。 +- correction v1/v2/v3 继续兼容重建与读取,既有原始记录和配置指纹不变。 + +### 验证与 O6 发布 + +两组相关检查分别 **68 项、72 项通过,共 140 项**。覆盖八份左右手源文件、负倍率、零倍率、 +非零偏移、多级链、独立安全界限、非法 JSON 修正、历史版本重建及标准 ROS 的父关节联动。 +四型号合成完整流水线同时验证:禁止调用 mimic 优化函数,仍能完成 JSON→URDF→独立角点验收→发布→读取。 +这些合成检查不代表其他型号已经完成实机标定。 + +构建后以完整 `20260916_105528/raw_samples.jsonl` 正式离线回放,退出码 0,发布: +`20260916_134154_source_mimic_165mm/`,`latest_partial_passed` 指向该目录。 +5 个 mimic 元素与原始 O6 URDF 一致,拇指 1.86、其余四个末节 0.89,offset 全为 0。 +所有 11 关节指令/反馈查找表、几何零位、实测范围证据与前三版逐项一致,旧产物哈希不变。 +相对上一版 URDF,除 mimic 参数外仅 IP 下限改变: +`−0.0020103987491093 → −0.00320818651354352 rad`,约增加 **0.068628°** 的联动兼容范围。 + +SDK 指令 0 时,拇指 pitch 为 34.2391°,原始 mimic 预览 IP 为 63.6848°,JSON IP 为 66.3515°。 +完整 JSON+URDF 的拇指 IP 独立角度 P95 为 0.43466°、位置 P95 为 2.11692 mm; +小指 DIP 分别为 1.87341°、1.94512 mm,原精度门限未改。 +独立图像验收的门限、样本数量和通过结论保持不变;重复数值优化的末位浮点值及诊断项可能变化。 +读取保存的 JSON 重建得到逐字节相同 URDF;本次没有重新运动机械手。 + +复核材料位于原始会话目录:`verify_source_mimic_export.py`、 +`source_mimic_export_verification.json`、`source_mimic_replay.log`。 +当前 8 Tag 布局仍只独立测量 5 个关节,其余 6 个按既有声明迁移;在线采集曾有 OOM 的边界保持原记录。 diff --git a/src/linkerhand_calibration/CALIBRATION_V3_IMPLEMENTATION.md b/src/linkerhand_calibration/CALIBRATION_V3_IMPLEMENTATION.md index 5183571..6f41fa6 100644 --- a/src/linkerhand_calibration/CALIBRATION_V3_IMPLEMENTATION.md +++ b/src/linkerhand_calibration/CALIBRATION_V3_IMPLEMENTATION.md @@ -1,6 +1,80 @@ # 按关节零位与实测/复制 JSON v3 实施记录 -日期:2026-09-11。范围:`linkerhand_calibration`。 +更新: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` 汇总了验证范围。 ## 实现与坐标约定 @@ -38,16 +112,23 @@ Profile 必须声明完整零位指令和非零的最终到达动作;扫描起 JSON 关节来自自己的视觉样本,或 Profile 明确声明的对应实测来源;同一 SDK 通道允许绑定多个关节。 接收关节保留自己的 SDK 通道,复制来源的指令/反馈节点及角度,不再拟合。 接收关节没有独立零位帧和第四轮指标,报告标记 `independently_measured=false` 并引用来源证据。 -正式单条指令曲线只用前三轮,约束 baseline=0;两条方向诊断曲线保留回差,共用同一参考。 -第四轮分别检验两方向,不能用于重新拟合曲线、零位、Tag 安装或基座。 +运动曲线只用前三轮,第四轮分别验证两方向;独立稳态分区用于指令映射训练与验证,约束 baseline=0。 +O6 侧摆按已确认策略输出正反向指令曲线;其他关节沿用单值指令曲线。两方向共用同一参考。 +验证数据不能用于重新拟合曲线、零位、Tag 安装或基座。 序列化只导出冻结结果;u8 必须实测支持 0–255 才能生成 256 项,rad 只在 `input_values` 范围内插值。 -URDF mimic 使用前三轮成对实测角度拟合经过零点的直线,输出 offset 精确为 0。 -非线性 JSON 与 mimic 近似分别保存。近似误差不否决完整映射;机械范围、拓扑、授权字段及标准加载仍必须合法。 +修正 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 的被动 PIP 不在该假设范围。 +仅明确列出的关节取 δ=0,保持其自身 `origin`;O12 的三个额外关节假设见上方当前结果。 ## 验收与消费 @@ -55,7 +136,7 @@ URDF mimic 使用前三轮成对实测角度拟合经过零点的直线,输出 1. 最终 JSON 对第四轮原始视觉角度;逐实测关节、逐方向检查;复制表另核验来源与一致性。 2. 最终 JSON+修正 URDF 的完整 FK 对第四轮 Tag 位姿;显式使用所有被动角。 -3. 相同主动映射配合标准 mimic 的损失;使用同一份训练冻结安装关系,只报告近似误差。 +3. 相同主动映射配合标准 mimic 的独立近似损失,单独报告。 前两项精度门限仍为角度 MAE ≤1°、P95 ≤2°、最大 ≤3°,Tag 位置 P95 ≤3 mm。 三文件哈希、保护输入、观测覆盖和通道绑定一并检查;拟合失败不自动运动,取消后不能提交。 @@ -63,7 +144,8 @@ URDF mimic 使用前三轮成对实测角度拟合经过零点的直线,输出 明确记录观测关节、复制关节和 CAD 零位假设。未观测接收关节不具有独立精度结论。 公共读取器和 `calibrated_joint_state_bridge` 支持 v3 的完整关节映射及历史 v1/v2 原语义。 -完整 FK 使用 `independent_mimic_angles=True`。标准 mimic 加载器用于结构兼容检查,不能覆盖完整非线性关节状态。 +完整 JSON 回放使用 `independent_mimic_angles=True`,防止线性近似覆盖实测被动角。 +标准 `robot_state_publisher` 和普通 URDF 网页查看器提供线性联动预览,不据此宣称实测非线性精度。 本轮未修改 MuJoCo、相机时间同步和接触动力学;现有 MuJoCo 脚本尚不能视为支持 v3。 ## 当前型号的发布边界 @@ -73,12 +155,13 @@ URDF mimic 使用前三轮成对实测角度拟合经过零点的直线,输出 | 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 零位仍须另行确定 | +| O12 | 16 / 19 | 无名指 MCP/PIP/DIP ← 小指对应关节 | pinky_pip、ring_pip、thumb_mcp 及末节沿用已确认的 CAD 零位假设 | `--validate-only` 的 `independent_measurement_coverage` 返回具体关节列表。 配置检查通过表示合同可执行,不能解释为全关节精度通过。 上述运动策略已经覆盖各型号全部运动关节;完整发布仍要求实测来源验收通过、零位有来源且范围合法。 -O12 当前仍被三项几何缺口阻止完整发布;L6/O12/G20 的现有 CAD mimic/限位冲突保留诊断,不能放宽限位掩盖。 +四个产品配置的零位来源均已明确;L6/O12/G20 的原 CAD mimic/限位冲突保留诊断, +收尾保留限位约束内的线性近似;所有 JSON 查表角度和 URDF mimic 全行程都必须处于合法范围内。 现有 Tag 布局没有被补写成未经验证的新布局,新增参考动作也没有标为实机已验证。 ## 原按关节零位改造的软件验证记录 diff --git a/src/linkerhand_calibration/README.md b/src/linkerhand_calibration/README.md index 732af2f..d76e324 100644 --- a/src/linkerhand_calibration/README.md +++ b/src/linkerhand_calibration/README.md @@ -4,7 +4,7 @@ G20、L6、O6、O12 使用同一个产品启动器、在线状态机、采集器 验收和发布器。型号差异来自 `config/profiles/*.yaml` 和 SDK Adapter,不再调用型号节点。 本包按观测关节冻结实机 baseline 零位,输出实测曲线及明确声明的复制曲线组成的 JSON v3, -以及线性 mimic 近似 URDF;报告区分独立实测、迁移和 CAD 零位假设。 +以及保留线性 mimic 联动的修正 URDF;报告区分独立实测、迁移和 CAD 零位假设。 保留 CAD 连杆尺寸、轴位置、mesh、惯量。完整运动由 JSON 的全部关节角度配合修正 URDF 计算。 软件仿真通过不等于实机精度通过;当前原始 G20/L6/O12 存在需要确认的 mimic/限位冲突。 本轮实现与验证见 [v3 实施记录](CALIBRATION_V3_IMPLEMENTATION.md)。 @@ -27,10 +27,43 @@ ros2 run linkerhand_calibration calibrate_hand --config \ ``` 其他型号替换为 `g20_right_product.yaml`、`l6_right_product.yaml`、`o6_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: 4`、`joint_angle_source: calibration_json`, +并记录 `mimic_policy: preserve_source_mimic`:所有型号保留本侧原始 `` 元素, +不改来源、倍率、偏移或省略的默认属性;正式标定不再拟合 mimic 参数。JSON 保留各关节 +非线性曲线及方向分支,精度验收使用 JSON 提供的全部关节角。父子链、轴和连杆不变, +被动关节与驱动关节仍绑定同一 SDK 通道,所有数值编辑仍按 Profile 授权。 +`mimic_envelopes` 单独记录基础范围、原始联动可达范围和导出范围;在明确的实测范围策略下, +导出范围取二者的最小包络,实测范围证据与查找表不变。未声明该策略的 CAD 限位,以及 +另行声明的硬件安全限位,仍是硬边界;冲突时报告具体关节和所需范围,不压低倍率或放宽容差。 +关节 origin 按标定零位修正后,原始 mimic 作为输出坐标中的近似预览,不作为准确物理约束。 +旧 correction v1、移除 mimic 的 v2、拟合线性近似的 v3 产物继续兼容读取与重建。 +导出规则单独版本化,不改变采集配置指纹或旧原始记录,已发布产物不能原地覆盖。 + +已生成的 JSON 可单独重建对应 URDF,无需相机或 SDK: + +```bash +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 v3,256 项指令查表 | @@ -51,17 +84,179 @@ ros2 run linkerhand_calibration calibrate_hand --config \ 复制参数不等于接收关节独立实测;接收关节仍须有明确的绝对零位来源及合法机械范围。 `zero.cad_zero_assumptions` 显式记录“实机 baseline 与原 CAD 末节零位一致”的使用前提, 相应 DIP(及拇指末节 IP)δ=0、自身 `origin` 不改。这是假设,不是视觉测得的绝对 CAD 朝向。 -**O12 的 PIP 不采用此假设。** 当前 `pinky_pip`、`ring_pip` 和主动 `thumb_mcp` 仍缺绝对零位依据, -可以采集诊断数据,但这些缺口解决前不能发布完整 O12 v3 JSON/URDF。 +按用户确认,O12 的 `pinky_pip`、`ring_pip` 和主动 `thumb_mcp` 也采用原始 URDF 零位作为 baseline 假设。 +运动曲线仍按实际观测/明确复制关系生成,报告标记 `assumed_source_cad_zero`,不称为独立实测零偏。 +正式入口在驱动硬件前检查运动覆盖及零位来源,缺失依据时直接指出缺项,不采完才发现无法发布。 启动检查分别列出 `unmeasured_joints`(未独立观测)、`unresolved_motion_joints`(也未声明复制) 及 `missing_baseline_geometry`。旧迁移字段单独存在不会自动授权新的 v3 曲线复制。 本次沿用现有 Tag 和避让布局声明延后零位动作,没有把新增动作或缺失 Tag 布局标为实机验证通过。 -每任务四个往返周期:前三轮训练,第四轮独立验证。 -每个方向先连续扫描,再单通道访问稳态点。训练每方向默认 9 点,并包含该通道 baseline,第四轮为交错点加端点; +每任务的运动模型采集四个往返,共八趟单程:前三轮训练,第四轮独立双向验证。 +四个产品配置统一使用 `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_distal`。 +`thumb_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 姿态跳变时,可沿用相同产品配置添加诊断选项: + +```bash +ros2 run linkerhand_calibration calibrate_hand \ + --config src/linkerhand_calibration/config/o6_right_product.yaml \ + --diagnostic-thumb +``` + +诊断选择原 Profile 中的 `thumb_yaw_top` 和 `thumb_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.json`。 +`diagnostic_observation_frame` 在零位尝试和扫描阶段单独记录各帧候选,包括未接受的姿态,不能当作关节角度样本。 +报告 `unit_quality` 分别记录原始观测覆盖与 `accepted_pose_quality` 可信姿态覆盖; +`joint_zero_status`、`missing_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 故障。 + +已保存的数据可直接离线复算,无需启动相机或重复运动: + +```bash +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 方向差保持诊断 + +需要区分“到达指令后仍在缓慢稳定”和“保持后仍存在方向差”时,使用独立选项: + +```bash +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.jsonl` 和 `diagnostic_capture.json`,记录保持指令及时间; +不拟合或发布标定 JSON/URDF,不形成正式断点,也不算精度验收。 +本诊断用于查清实机稳定时间和方向差,不改变最终单值 `angle_rad` 的验收要求。 + ## 启动前相机检查 `--validate-only` 检查相机配置文件、序列号、内外参指纹和已有外参质量记录,不采集现场图像。 @@ -97,11 +292,26 @@ ros2 run linkerhand_calibration calibrate_hand --config \ - 相机相对位置改变时须重新确认/标定外参;仅重新锁定掌心 Tag 不能替代外参标定。 - 固定 Tag 被避让遮住时可用已锁定参考继续采集;软件不能保证检测完全不可见对象的移动。 运动 Tag 的刚性和几何一致性仍须通过最终独立验证。 -- 断点版本为 `unified_engine_v5_joint_zero`,不复用旧策略数据。 +- 采集断点版本为 `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 朝向。 @@ -113,6 +323,8 @@ O12 避让:小指和无名指到 Profile 的最大弯曲指令;标定食指 不要求反馈数值精确等于命令。SDK 指令最大值与实测 URDF 角度不是同一个量。 O12 使用 HCAN device 0/channel 0 的已锁定 vendor Python wheel,不检查 `can0`。 +连接参数由本包的 `config/o12_sdk.yaml` 保存,并通过产品配置中的 SHA256 校验; +不依赖第三方 SDK 子仓库中未提交的示例 YAML 修改。 只读反馈桥可在第一次运动命令之前取得状态,不会发送零位命令来“激活回读”。 POSITION 和活动故障由 SDK 确认;无法单独回读温度时使用 SDK 错误位过热保护,不伪造温度。 字节 SDK 当前没有独立硬件故障遥测,状态会明确注明这一能力边界。 @@ -123,6 +335,9 @@ POSITION 和活动故障由 SDK 确认;无法单独回读温度时使用 SDK 物理越限、明显运动要求下连续 2 秒无推进、已锁固定基准连续 10 帧漂移超过 5 px 而暂停。 开始后相机投影参数变化也会拒绝继续使用混合坐标数据。 当前关节持续缺失零位观测会暂停并列出关节、机位和所需 Tag;尚未轮到的手指遮挡不阻塞当前任务。 +零位和稳态点的反馈稳定窗口以最新独立反馈为终点,并保留跨越窗口起点的一条反馈。 +控制定时器的调度抖动或重复读取同一反馈不会清空稳定观测;真实反馈波动或反馈过期仍会使当前窗口失效。 +零位仍需至少 10 帧且姿态波动不超过 1°;拟合验收门限不变。 短时 Tag 丢失、PnP/同步失败、低检测率/低反馈 Hz、正常跟随滞后、固有耦合和非目标小幅运动 不会实时中断轨迹。无效视觉帧直接丢弃。 @@ -130,15 +345,150 @@ POSITION 和活动故障由 SDK 确认;无法单独回读温度时使用 SDK 所有型号的独立 Tag 姿态筛选共用 `core/geometry/pnp.py` 中的近似同误差阈值,默认 `0.03 px`。 ROS 和离线采集使用相同默认值,型号 YAML 不重复声明该值。`1.5 px` 是图像质量拒绝阈值, 不能用它判断两种姿态是否同样可信;仅在误差差值不超过近似同误差阈值时使用时间连续性。 -当另一分支的图像拟合明确更好时允许纠正旧分支。覆盖参数必须有限、非负且小于图像质量拒绝阈值。 -这项修复不保证解决所有平面 Tag 双解,真实角点噪声、采集时序和最终运动精度仍需实测验证。 +分支容差覆盖值必须有限、非负且小于图像质量拒绝阈值。 + +独立 Tag 首次接受姿态前,需要默认至少 5 帧、跨越至少 0.1 秒的图像证据确认。 +近似同误差的候选只有旋转相差不超过 1°、平移相差不超过 1 mm 时才视为同一姿态;这些参数统一定义于 +`core/geometry/tag_pose/parameters.py`,ROS 通过 `pnp_branch_confirmation_frames`、 +`pnp_branch_confirmation_minimum_seconds`、`pnp_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_ref`、`t = pivot + R × mount` 拟合原始角点的重投影残差。 +稀疏线性子问题使用 `atol=btol=1e-10`,避免近似计算误差导致外层耗尽迭代预算; +外层收敛条件、默认 150 次迭代预算和各项精度门限保持不变,记录每个候选的求解状态及迭代次数。 +父 Tag 本身运动时联合求解整条已声明的观测链;已冻结的上游几何不会重新拟合。 +角度来自当前图像,不使用 SDK 到角度的先验、旧 mimic 或 CAD 零位假设选择分支。 +单帧 PnP 深度噪声较大时,不再将其三维平移估计直接作为关节运动的真值。 + +当源 URDF 中两个转轴相邻且平行,并且观测 Tag 实际固定在对应连杆上时, +`profiles/observations.py` 提取零位修正后仍不变的平行关系和垂直轴距。 +`CadImageHingeBundle` 消去三个多余几何变量,使准备模型遵守最终 URDF 保留的几何; +不修改原骨长/轴向、不增加权重或 CAD 零位假设。不满足条件的观测链使用原模型。 +新冻结模型保存源 URDF 哈希和约束,最终处理从源文件重新推导核验;旧模型保持可读。 +修改前的原始图像对照实验及适用边界见 [流程评估](CALIBRATION_PIPELINE_REVIEW.md)。 + +准备段至少需要 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_model` 与 `image_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 精度。 +可复算摘要见[本次分支修复回放](../../calibration_output/O6_RIGHT_001/20260914_175815/motion_branch_fix_review/README.md), +测试夹具同时保留“yaw/pitch 可分”和“IP 不得强行通过”两类断言。 +后续 `20260914_200044` 的图像求解回放使用其真实归零准备段冻结模型,后续 838 帧可复算通过当前图像检查; +这仍是单轮诊断回放,不是完整四轮精度验收,也不会生成正式 JSON 或 URDF。 + +`20260915_104446` 正式实采中,拇指三个关节各冻结 10 帧零位并完成四轮, +pitch/IP 的旋转及指令单表子项均通过;yaw 旋转通过,但指令单表 MAE 为 1.0454°,超过 1°。 +两场独立会话均观察到稳定方向差,该批观测即使允许每个指令任取一个输出角,MAE 下界仍超过 1°。 +这不是通过重新拟合单表能够消除的误差;方向分支的诊断通过也不能冒充单表通过。 +详见[拇指实测子项报告](../../calibration_output/O6_RIGHT_001/20260915_104446/motion_holdout_review/README.md)。 +后续 `20260915_111401` yaw 保持实测在 64/128/192 各双向保持 10 秒;末 3 秒的方向差 +分别为 2.2965°/2.7885°/2.1248°,每个窗口均有 90 帧可靠观测,同一目标的 SDK 反馈相同。 +0.5–1 秒至末段的角度变化最多约 0.038°,延长等待没有消除方向差。 +本轮仅启动顶部机位,不能据此确定具体机械原因或证明整手精度;继续保留单值 JSON 与原验收门槛。 +复现脚本、时间曲线与来源哈希见[双向保持实测](../../calibration_output/O6_RIGHT_001/20260915_111401/yaw_hold_review/README.md)。 +`20260915_104446` 会话在小指准备求解超时后停止,未完成整手空间/FK 验收、未发布 JSON/URDF。 +小指同源准备段离线求解只需约 3.42 秒,但候选仍未达到统计区分要求;运行时超时来源尚待定位, +详见[小指准备段核查](../../calibration_output/O6_RIGHT_001/20260915_104446/side_preparation_review/README.md)。 +后续会话另存 `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 参数必须与受保护外参中的指纹一致。 @@ -155,7 +505,7 @@ linkerhand_calibration/ ├── profiles/ 加载、静态验证、Tag/CAD 观测关系 ├── core/domain/ Profile、样本、CalibrationResult、状态 ├── core/geometry/ 相机、变换、PnP 兼容导出 - │ └── tag_pose/ IPPE、连续跟踪、刚性组、轨迹分支选择 + │ └── tag_pose/ IPPE、连续跟踪、冻结前运动证据、刚性组和轨迹选择 ├── core/fitting/ 运动曲线、线性耦合、固定安装 │ └── spatial_solver/ 空间观测、基座、零位、统计与独立验收 ├── core/urdf/ 授权修正、FK、范围与最终文件验收 @@ -169,6 +519,9 @@ linkerhand_calibration/ ├── 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 整场断点证据检查 @@ -193,7 +546,8 @@ linkerhand_calibration/ PnP 的原导入路径继续可用。`tag_pose/parameters.py` 集中定义跟踪默认值, ROS 加载器将角度参数由 deg 转为 rad,离线采集直接使用同一套默认值。 -公共分支容差仍为 `0.03 px`,重投影质量、倾角、过滤原因和连续跟踪恢复策略保持一致。 +公共分支容差仍为 `0.03 px`,重投影质量和倾角限制保持一致;首次确认、参考冻结与失联恢复 +使用同一公共分支策略,ROS 和离线链路均不能绕过它。 `CalibrationSession.phase` 是流程状态来源。协调器在状态锁内开始、中止、推进和提交观测; 耗时姿态计算在锁外进行,提交时核对会话版本、采集对象、运动对象及稳态采样边界。 @@ -242,25 +596,66 @@ v1/v2 按原语义读取,不会自动升级为独立实测;保留的 v2 数 有效发布由 `release_manifest.json` 及产品发布指针共同证明;单独的 `PASS` 摘要或存在 URDF 文件不等于已发布。 manifest 记录 JSON、URDF、标定报告三文件 SHA256、受保护输入和独立 holdout 证据。任一验收失败都不会替换原发布指针。 `measurement_contract.json` 提前列出 Tag/link、零位观测关系、CAD 保留/迁移与原始 mimic 限位冲突。 -主 JSON 以原始 URDF 关节名为键,每个关节仅包含 `sdk_channel` 和 `angle_rad`; +主 JSON 以原始 URDF 关节名为键,单值关节包含 `sdk_channel` 和 `angle_rad`; O12 等 rad 输入还包含一一对应的 `input_values`(在有效节点间线性插值,不外推)。 -字节型号固定 256 项:SDK 指令 `v` 对应 `angle_rad[v]`,只能在训练指令实际覆盖 0–255 时导出。 +字节型号每条曲线固定 256 项:SDK 指令 `v` 对应所选曲线的第 `v` 项,只能在训练指令实际覆盖 0–255 时导出。 输出均是修正 URDF 的 q_output,不再加零偏。顶层记录身份、单位、完整 `baseline_command` 和共同零位约定。 独立观测的主动和被动关节直接导出各自视觉曲线,同一驱动通道可绑定多个关节。 显式复制的关节使用来源关节的同一组节点与角度,不重新拟合、归零或由 mimic 推导;接收通道保持不变。 -最终单条曲线在前三轮拟合中约束 baseline=0,正反向诊断分支共用冻结参考、保留回差。 -第四轮分别检验两个方向;超差时禁止发布,不隐藏切换分支。 -URDF mimic 单独用前三轮成对视觉角度拟合 `q_child≈a*q_parent`,共同零位下 offset=0; -复制的被动关节使用对应来源的线性近似,证据仍归属于来源关节。 -其近似误差不会否决已达标的完整映射,但结构、授权字段与机械限位始终强制检查。 +默认单值曲线在指定的稳态训练数据中约束 baseline=0,正反向诊断分支共用冻结参考、保留回差。 +独立稳态验证分别检验两个方向;超差时禁止发布,不隐藏切换分支。 -验收分别记录:最终 JSON 对第四轮独立视觉角度;完整 JSON+URDF FK 对第四轮 Tag 位姿; -同一主动映射配合标准 mimic 的近似损失。前两项决定精度通过,第三项仅报告。 +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 保留原被动关节 ``,单独拖动父关节滑条时,末节按 +`q_child ≈ multiplier × q_parent + offset` 联动。标准 URDF 不能将查找表表达为 mimic。 +前三轮成对视觉角度用于拟合线性近似,优化变量为父关节两端对应的子关节角度; +两端都限制在子关节原有输出范围内,因此整个联动行程合法,无需放大限位或裁剪小负角。 +偏移是近似直线在 baseline 的误差,不能当作新的物理零位;报告明确保存 +`baseline_error_rad`、`physical_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_json`、`urdf_mimic_policy` 和两种回放结果。 门限保持角度 MAE≤1°、P95≤2°、最大≤3°,Tag 位置 P95≤3 mm。 +平行转轴的零位计算先消除轴上参考点的任意坐标,再比较相机平面中的轴间方向; +改变同一轴线的表示点不能改变标定零位。这是所有型号共用的几何规则。 独立精度结论只覆盖实际观测的关节和 Tag;复制关节另检查来源、查表一致性、通道与机械范围, 不把来源关节的第四轮误差称为接收关节精度。混合产物 manifest 使用 `measured_and_transferred_json_urdf_v3`,列出实测和复制覆盖。 -两种 FK 使用相同的训练冻结安装与基座;第四轮不拟合零位、曲线、Tag 安装或基座。 +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 指标。 @@ -280,8 +675,11 @@ ros2 run linkerhand_calibration calibrated_joint_state_bridge --ros-args \ 需要反馈角度显示时显式选择 `input_kind:=feedback`,桥读取配套报告中的反馈映射。 桥检查三文件哈希、拒绝越域输入, -v3 全部关节角由 JSON 显式计算,完整 FK 必须启用 `independent_mimic_angles=True`,不加第二次零偏。 -标准 `robot_state_publisher` 的 mimic 更新会覆盖被动角;仅检查其加载兼容性,不用它验证完整非线性运动。 +v3 全部关节角由 JSON 显式计算,不加第二次零偏。完整 FK 使用 +`independent_mimic_angles=True`,以 JSON 给出的末节角度覆盖线性近似。 +标准 `robot_state_publisher` 和普通 URDF 网页查看器会执行 mimic,适合检查近似联动; +仅把完整 JointState 发给标准 RSP,不能让它停止覆盖被动角。 +消费实测非线性曲线的仿真程序必须明确采用 JSON 角度,关闭重复的线性被动更新。 仿真输出不能与实机反馈话题同名。 MuJoCo 是用户后续使用的外部验收项目,本轮不改动它、不增加 MuJoCo 依赖,自动标定不要求打开 GUI。 外部对比须确认加载的模型来自这份 URDF,指令转换使用配套 JSON;不能用实机反馈直接驱动仿真来证明指令映射。 @@ -301,6 +699,41 @@ ros2 run linkerhand_calibration compare_calibration_urdfs \ 旧会话不能自动获得当前哈希或新版本认证。`--publish-offline` 必须显式给出,且不会绕过验证。 当前坐标规则、生产职责与验收范围见 [v3 实施记录](CALIBRATION_V3_IMPLEMENTATION.md)。 -当前代码审查、已修复问题与未完成的精度验收见 -[2026-09-10 需求审查](CALIBRATION_CODE_REVIEW_20260910.md)。相机图像目前采用主机取帧时间; -USB/驱动积压情况下,图像与反馈的时间戳接近不能证明曝光时刻同步,不能据此宣称实机精度通过。 +早期需求审查见 [2026-09-10 需求审查](CALIBRATION_CODE_REVIEW_20260910.md); +当前修改依据、实机结果与未完成的精度验收见 [流程评估](CALIBRATION_PIPELINE_REVIEW.md)。 +相机和反馈目前均采用下述测量时间;时间配对正确仍须经过独立空间验收。 + + +### 实时调度边界 + +公共 ROS 节点顺序处理控制和反馈消息;耗时图像估计由每视角有界工作队列执行。 +图像最多在原同步容差内等候真实反馈时间覆盖,队列不反向持锁查询会话。 +增加执行器线程数不能提高共享状态的处理能力;反馈超时仍按真实测量判断。 +暂停或验收失败的会话仅提供诊断/候选文件,不能当作已发布的标定结果。 +调度对照依据与验证范围见 `CALIBRATION_PIPELINE_REVIEW.md` 第七轮评估。 + + +### 相机与反馈时间 + +连续扫描将 CAN 实测反馈与相机曝光中点配对。MVS 取帧/图像发布时间不参与替代曝光时间; +设备时钟由独立锁存换算到 ROS 时钟,换算依据保存在会话的 `camera_timing_.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 条件不一致时拒绝发布。 +这不表示联合求解或外参自校准已经通过验证。 diff --git a/src/linkerhand_calibration/config/g20_right_product.yaml b/src/linkerhand_calibration/config/g20_right_product.yaml index 097cee9..ecc2df5 100644 --- a/src/linkerhand_calibration/config/g20_right_product.yaml +++ b/src/linkerhand_calibration/config/g20_right_product.yaml @@ -1,7 +1,7 @@ schema_version: 3 profile_id: G20/right/g20_right_19/v1 profile_config: package://linkerhand_calibration/config/profiles/g20_right_19.yaml -profile_config_sha256: f73c8a391a8feb6cb3028fbd8278d3b18d6e2e875980235f7797fd8de2efbac1 +profile_config_sha256: 958f74c476730636d2dadb58a30296a7cdf0b25ad00345252754f9c1b6b50c7f model: G20 side: right tag_layout: g20_right_19 diff --git a/src/linkerhand_calibration/config/l6_right_product.yaml b/src/linkerhand_calibration/config/l6_right_product.yaml index 21d3b53..9b2a5ea 100644 --- a/src/linkerhand_calibration/config/l6_right_product.yaml +++ b/src/linkerhand_calibration/config/l6_right_product.yaml @@ -1,7 +1,7 @@ schema_version: 3 profile_id: L6/right/l6_right_8/v1 profile_config: package://linkerhand_calibration/config/profiles/l6_right_8.yaml -profile_config_sha256: 733094679e5477bbe97a633f4fb1ce25242804b667cd435ef9132f491e6ef247 +profile_config_sha256: d241e87ded3a29d416a876b27f1a251f8daf2f98e3676ab277264f1bdfaf5c60 model: L6 side: right tag_layout: l6_right_8 diff --git a/src/linkerhand_calibration/config/o12_right_product.yaml b/src/linkerhand_calibration/config/o12_right_product.yaml index 67c1161..c6aa662 100644 --- a/src/linkerhand_calibration/config/o12_right_product.yaml +++ b/src/linkerhand_calibration/config/o12_right_product.yaml @@ -1,7 +1,7 @@ schema_version: 3 profile_id: O12/right/o12_right_16/v1 profile_config: package://linkerhand_calibration/config/profiles/o12_right_16.yaml -profile_config_sha256: d0c1e2ac62f96162612f0ec45c25c6f467d045c1c1030b5be34ccc115dca4cf9 +profile_config_sha256: 652c2a82dfc7532b4f62c42cebdad57b169435cf950c5f2ee910cc67c7972621 model: O12 side: right tag_layout: o12_right_16 @@ -15,7 +15,7 @@ sdk: package_sha256: cae7a0d5bce7e7c9d72cc90a0dd15e152f11e2a1ecf04cb6761e8171399ff170 transport: hcan setup: src/agillink_omnihand_sdk/linux/x64/ros2/jazzy/setup.bash - config: src/agillink_omnihand_sdk/linux/x64/ros2/jazzy/share/omnihand_node/config/omnihand_pro_2025_node.yaml + config: package://linkerhand_calibration/config/o12_sdk.yaml config_sha256: 1e3c0942b32128943fbba27846a69a8483af6da9551d99ed687e45c4321ade9c cameras: diff --git a/src/linkerhand_calibration/config/o12_sdk.yaml b/src/linkerhand_calibration/config/o12_sdk.yaml new file mode 100644 index 0000000..706fe46 --- /dev/null +++ b/src/linkerhand_calibration/config/o12_sdk.yaml @@ -0,0 +1,45 @@ +# OmniHand Pro 2025 (O12) Configuration +# connection_type: "zlgcan" | "hcan" | "socketcan" | "rs485" | "usb" +# request_interval_ms: min gap between requests, SDK default 0 = no throttling, range 0..100 +# frame_recv_timeout_ms: single-frame reply wait, SDK default 50, range 10..1000 + +/**: + ros__parameters: + # This workstation uses one O12 right hand through HCAN device 0/channel 0. + # Leave left_hand undefined so the node does not try to open a second hand. + right_hand: + hand_device_id: 1 + connection_type: "hcan" + canfd_device_id: 0 + canfd_channel_id: 0 + # O12 POSITION streams set-points and has no G20-style firmware speed + # interpolation, so do not throttle the 50 Hz calibration command stream. + request_interval_ms: 0 + frame_recv_timeout_ms: 100 + show_data_details: false + + # Alternative ZLG CANFD example: + # right_hand: + # hand_device_id: 1 + # connection_type: "zlgcan" + # canfd_device_id: 0 + # canfd_channel_id: 0 + # request_interval_ms: 0 + # frame_recv_timeout_ms: 50 + # show_data_details: false + # socketcan example + # left_hand: + # hand_device_id: 1 + # connection_type: "socketcan" + # can_interface: "can0" + # request_interval_ms: 0 + # frame_recv_timeout_ms: 50 + # show_data_details: true + + # right_hand: + # hand_device_id: 1 + # connection_type: "socketcan" + # can_interface: "can1" + # request_interval_ms: 0 + # frame_recv_timeout_ms: 50 + # show_data_details: true diff --git a/src/linkerhand_calibration/config/o6_right_8_tags.yaml b/src/linkerhand_calibration/config/o6_right_8_tags.yaml index 5d06f3e..2c21dc9 100644 --- a/src/linkerhand_calibration/config/o6_right_8_tags.yaml +++ b/src/linkerhand_calibration/config/o6_right_8_tags.yaml @@ -3,7 +3,7 @@ image_transport: raw qos_profile: sensor_data family: 36h11 - size: 0.016 + size: 0.0165 max_hamming: 0 detector: threads: 4 @@ -16,14 +16,14 @@ tag: ids: [0, 1, 2] frames: [front_base, thumb_pitch, thumb_ip] - sizes: [0.016, 0.016, 0.016] + sizes: [0.0165, 0.0165, 0.0165] /o6_calibration/side/apriltag/apriltag: ros__parameters: image_transport: raw qos_profile: sensor_data family: 36h11 - size: 0.016 + size: 0.0165 max_hamming: 0 detector: threads: 4 @@ -36,14 +36,14 @@ tag: ids: [3, 4, 5] frames: [side_base, pinky_pitch, pinky_dip] - sizes: [0.016, 0.016, 0.016] + sizes: [0.0165, 0.0165, 0.0165] /o6_calibration/top/apriltag/apriltag: ros__parameters: image_transport: raw qos_profile: sensor_data family: 36h11 - size: 0.016 + size: 0.0165 max_hamming: 0 detector: threads: 4 @@ -56,4 +56,4 @@ tag: ids: [6, 7] frames: [top_base, thumb_yaw] - sizes: [0.016, 0.016] + sizes: [0.0165, 0.0165] diff --git a/src/linkerhand_calibration/config/o6_right_product.yaml b/src/linkerhand_calibration/config/o6_right_product.yaml index ef7c46b..78b48e0 100644 --- a/src/linkerhand_calibration/config/o6_right_product.yaml +++ b/src/linkerhand_calibration/config/o6_right_product.yaml @@ -1,7 +1,7 @@ schema_version: 3 profile_id: O6/right/o6_right_8/v1 profile_config: package://linkerhand_calibration/config/profiles/o6_right_8.yaml -profile_config_sha256: df6c4abb3c9d48b186c1bd6ab304ea9c0bbab54f9381ff222d5d8d3a3f65babe +profile_config_sha256: 72bf56b13eb2a0d4d14a5be42838b2ffc176722f470fe1a3ae5fd84604e1df4d model: O6 side: right tag_layout: o6_right_8 @@ -14,25 +14,25 @@ cameras: front: serial_number: DB2163742 camera_name: hikrobot_front_DB2163742 - camera_info: ~/.ros/camera_info/hikrobot_DB2163742.yaml + camera_info: config/o6_camera_intrinsics_20260915/hikrobot_DB2163742.yaml side: serial_number: DB2163749 camera_name: hikrobot_side_DB2163749 - camera_info: ~/.ros/camera_info/hikrobot_DB2163749.yaml + camera_info: config/o6_camera_intrinsics_20260915/hikrobot_DB2163749.yaml top: serial_number: DB2163739 camera_name: hikrobot_top_DB2163739 - camera_info: ~/.ros/camera_info/hikrobot_DB2163739.yaml + camera_info: config/o6_camera_intrinsics_20260915/hikrobot_DB2163739.yaml artifacts: source_urdf: package://linkerhand_calibration/urdf/o6_right/linkerhand_o6_right.urdf source_urdf_sha256: 8f184faad699fbf771e388f109a4e8793b5cb190c33a87b2eba8491a3a37dd62 - camera_extrinsics: config/o6_three_camera_extrinsics.yaml - camera_extrinsics_sha256: 29af61f7bf1bad6718cbbaa54b0536f0a471c83f5bb3554f264ab9d292e56ca4 + camera_extrinsics: config/o6_three_camera_extrinsics_20260915_192517.yaml + camera_extrinsics_sha256: d057183eaba592149ec4c63c4ceab8a70e1a0c9d7cf2460a1c8b7e86a6ca6638 calibration_config: package://linkerhand_calibration/config/o6_three_camera_calibration.yaml - calibration_config_sha256: 15adaec462d0895dc68d19760e8f333c62e57786d124f22cfc481007a3d976a4 + calibration_config_sha256: 49ce8a0d317695994c5b306e3a3a28d0470b8769c77f1173ca935178430587db tag_config: package://linkerhand_calibration/config/o6_right_8_tags.yaml - tag_config_sha256: 16abe7119b4764f86333dae8264247571d1e0bca45af959d558bef4fb5485f5e + tag_config_sha256: 219b8aa906fc9e2a13f3bbede46f542ff176ef2c7f60190f0dd2508993f04593 release: required_independent_passes: 1 diff --git a/src/linkerhand_calibration/config/o6_three_camera_calibration.yaml b/src/linkerhand_calibration/config/o6_three_camera_calibration.yaml index e92130d..229fe2d 100644 --- a/src/linkerhand_calibration/config/o6_three_camera_calibration.yaml +++ b/src/linkerhand_calibration/config/o6_three_camera_calibration.yaml @@ -21,9 +21,9 @@ o6_calibration: torque_u8: 80 repetitions: 4 preflight_checkpoints_u8: [255, 127, 0] - tag_size_m: 0.016 + tag_size_m: 0.0165 tag_size_override_ids: [0, 1, 2, 3, 4, 5, 6, 7] - tag_size_overrides_m: [0.016, 0.016, 0.016, 0.016, 0.016, 0.016, 0.016, 0.016] + tag_size_overrides_m: [0.0165, 0.0165, 0.0165, 0.0165, 0.0165, 0.0165, 0.0165, 0.0165] minimum_detection_rate: 0.95 minimum_joint_frame_rate: 0.85 diff --git a/src/linkerhand_calibration/config/profiles/g20_right_19.yaml b/src/linkerhand_calibration/config/profiles/g20_right_19.yaml index aad9158..cbb3155 100644 --- a/src/linkerhand_calibration/config/profiles/g20_right_19.yaml +++ b/src/linkerhand_calibration/config/profiles/g20_right_19.yaml @@ -1503,7 +1503,9 @@ artifacts: - urdf_zero_publication publish_corrected_urdf: true acquisition: - policy_version: unified_engine_v5_joint_zero + command_capture_mode: separate + joint_zero_timeout_seconds: 5.0 + policy_version: unified_engine_v8_all_view_images mapping_probe_maximum_rad: 0.0 automatic_rescan_limit: 1 minimum_valid_samples: 40 diff --git a/src/linkerhand_calibration/config/profiles/l6_right_8.yaml b/src/linkerhand_calibration/config/profiles/l6_right_8.yaml index 8acb0e3..50535b7 100644 --- a/src/linkerhand_calibration/config/profiles/l6_right_8.yaml +++ b/src/linkerhand_calibration/config/profiles/l6_right_8.yaml @@ -347,7 +347,9 @@ artifacts: - l6_partial_v1 publish_corrected_urdf: true acquisition: - policy_version: unified_engine_v5_joint_zero + command_capture_mode: separate + joint_zero_timeout_seconds: 5.0 + policy_version: unified_engine_v8_all_view_images mapping_probe_maximum_rad: 0.0 automatic_rescan_limit: 1 minimum_valid_samples: 40 diff --git a/src/linkerhand_calibration/config/profiles/o12_right_16.yaml b/src/linkerhand_calibration/config/profiles/o12_right_16.yaml index 15a2b94..0de20db 100644 --- a/src/linkerhand_calibration/config/profiles/o12_right_16.yaml +++ b/src/linkerhand_calibration/config/profiles/o12_right_16.yaml @@ -744,6 +744,9 @@ measurement: pose_axis_line_required: false zero: cad_zero_assumptions: + thumb_mcp: User accepts original CAD zero at SDK baseline; absolute zero is assumed, motion is measured. + pinky_pip: User accepts original CAD zero at SDK baseline; absolute zero is assumed, motion is measured. + ring_pip: User accepts recipient CAD zero at SDK baseline; motion is copied from pinky_pip, not independently measured. index_dip: User accepts original CAD joint zero as the physical baseline; not independently measured. middle_dip: User accepts original CAD joint zero as the physical baseline; not independently @@ -844,7 +847,6 @@ zero: - pinky_mcp_pitch depth_free_axis_projection: true accept_validated_zero_in_confidence_interval: true - project_axis_gauge_before_image: true axis_order: - thumb_cmc_roll - thumb_cmc_yaw @@ -929,7 +931,9 @@ artifacts: - full_sdk_range_v2 publish_corrected_urdf: true acquisition: - policy_version: unified_engine_v5_joint_zero + command_capture_mode: separate + joint_zero_timeout_seconds: 5.0 + policy_version: unified_engine_v8_all_view_images mapping_probe_maximum_rad: 0.05235987755982989 urdf: authorized_fields: @@ -997,8 +1001,10 @@ urdf: - mimic.multiplier - mimic.offset ring_pip: + - mimic.multiplier - mimic.offset ring_dip: + - mimic.multiplier - mimic.offset joint_coverage: thumb_cmc_roll: measured_static_dynamic diff --git a/src/linkerhand_calibration/config/profiles/o6_right_8.yaml b/src/linkerhand_calibration/config/profiles/o6_right_8.yaml index e85eeed..c5f949c 100644 --- a/src/linkerhand_calibration/config/profiles/o6_right_8.yaml +++ b/src/linkerhand_calibration/config/profiles/o6_right_8.yaml @@ -55,31 +55,40 @@ vision: - role: front_base fixed_reference: true id: 0 + size_m: 0.0165 - role: thumb_pitch fixed_reference: false id: 1 + size_m: 0.0165 - role: thumb_ip fixed_reference: false id: 2 + size_m: 0.0165 - name: side tags: - role: side_base fixed_reference: true id: 3 + size_m: 0.0165 - role: pinky_pitch fixed_reference: false id: 4 + size_m: 0.0165 - role: pinky_dip fixed_reference: false id: 5 + size_m: 0.0165 - name: top tags: - role: top_base fixed_reference: true id: 6 + size_m: 0.0165 - role: thumb_yaw fixed_reference: false id: 7 + size_m: 0.0165 + link: rh_thumb_distal common_frame: calibration_common extrinsic_reference_view: front extrinsics_quality_limits: @@ -329,6 +338,8 @@ scope: default_scope: partial artifacts: output_schema_version: 3 + directional_command_joints: + - rh_thumb_cmc_yaw calibration_filename: o6_right_{serial_number}_partial_calibration.json corrected_urdf_filename: linkerhand_o6_right_{serial_number}_partial_zero_calibrated.urdf protected_input_fields: @@ -343,7 +354,12 @@ artifacts: - o6_partial_v1 publish_corrected_urdf: true acquisition: - policy_version: unified_engine_v5_joint_zero + joint_zero_timeout_seconds: 5.0 + command_capture_mode: separate + steady_training_nodes: 9 + steady_extra_training_nodes: + pinky_pitch_dip_side: [239.0] + policy_version: unified_engine_v8_all_view_images mapping_probe_maximum_rad: 0.0 automatic_rescan_limit: 1 minimum_valid_samples: 40 @@ -358,6 +374,40 @@ acquisition: fixed_reference_maximum_drift_px: 5.0 fixed_reference_confirmation_frames: 10 urdf: + limit_policies: + rh_pinky_mcp_pitch: + source_limits: calibrated_motion_envelope + evidence_source: User accepts original CAD zeros but requests measured SDK motion amplitudes; source CAD limits are nominal motion envelopes. + rh_thumb_cmc_pitch: + source_limits: calibrated_motion_envelope + evidence_source: User accepts original CAD zeros but requests measured SDK motion amplitudes; source CAD limits are nominal motion envelopes. + rh_middle_mcp_pitch: + source_limits: calibrated_motion_envelope + evidence_source: User accepts original CAD zeros but requests measured SDK motion amplitudes; source CAD limits are nominal motion envelopes. + rh_index_mcp_pitch: + source_limits: calibrated_motion_envelope + evidence_source: User accepts original CAD zeros but requests measured SDK motion amplitudes; source CAD limits are nominal motion envelopes. + rh_ring_mcp_pitch: + source_limits: calibrated_motion_envelope + evidence_source: User accepts original CAD zeros but requests measured SDK motion amplitudes; source CAD limits are nominal motion envelopes. + rh_thumb_cmc_yaw: + source_limits: calibrated_motion_envelope + evidence_source: User accepts original CAD zeros but requests measured SDK motion amplitudes; source CAD limits are nominal motion envelopes. + rh_pinky_dip: + source_limits: calibrated_motion_envelope + evidence_source: User accepts original CAD zeros but requests measured SDK motion amplitudes; source CAD limits are nominal motion envelopes. + rh_thumb_ip: + source_limits: calibrated_motion_envelope + evidence_source: User accepts original CAD zeros but requests measured SDK motion amplitudes; source CAD limits are nominal motion envelopes. + rh_middle_dip: + source_limits: calibrated_motion_envelope + evidence_source: User accepts original CAD zeros but requests measured SDK motion amplitudes; source CAD limits are nominal motion envelopes. + rh_ring_dip: + source_limits: calibrated_motion_envelope + evidence_source: User accepts original CAD zeros but requests measured SDK motion amplitudes; source CAD limits are nominal motion envelopes. + rh_index_dip: + source_limits: calibrated_motion_envelope + evidence_source: User accepts original CAD zeros but requests measured SDK motion amplitudes; source CAD limits are nominal motion envelopes. authorized_fields: rh_pinky_mcp_pitch: - limit.lower @@ -386,18 +436,28 @@ urdf: rh_ring_dip: - mimic.multiplier - mimic.offset + - limit.lower + - limit.upper rh_index_dip: - mimic.multiplier - mimic.offset + - limit.lower + - limit.upper rh_pinky_dip: - mimic.multiplier - mimic.offset + - limit.lower + - limit.upper rh_thumb_ip: - mimic.multiplier - mimic.offset + - limit.lower + - limit.upper rh_middle_dip: - mimic.multiplier - mimic.offset + - limit.lower + - limit.upper joint_coverage: rh_pinky_mcp_pitch: measured_static_dynamic rh_thumb_cmc_pitch: measured_static_dynamic diff --git a/src/linkerhand_calibration/launch/three_camera_calibration.launch.py b/src/linkerhand_calibration/launch/three_camera_calibration.launch.py index 7dab455..039ebe7 100644 --- a/src/linkerhand_calibration/launch/three_camera_calibration.launch.py +++ b/src/linkerhand_calibration/launch/three_camera_calibration.launch.py @@ -29,6 +29,8 @@ def _launch_stack(context): ) from linkerhand_calibration.profiles import load_hand_profile from linkerhand_calibration.runtime.adapters.ros_topics import sdk_topics + from linkerhand_calibration.runtime.diagnostic_capture import resolve_diagnostic_capture + from linkerhand_calibration.runtime.observation_scope import required_observation_views model = LaunchConfiguration("model").perform(context).strip().upper() hand_type = LaunchConfiguration("hand_type").perform(context).lower() @@ -48,7 +50,11 @@ def _launch_stack(context): raise ValueError("launch identity differs from the protected Profile") except ValueError as error: raise RuntimeError(str(error)) from error - views = contract.typed_profile.vision.view_names + diagnostic = resolve_diagnostic_capture( + contract.typed_profile, + LaunchConfiguration("diagnostic_capture").perform(context).strip(), + ) + views = required_observation_views(contract.typed_profile, diagnostic) requested_tag_config = LaunchConfiguration("tag_config").perform(context) if not requested_tag_config: raise RuntimeError("tag_config is required; use calibrate_hand --config") @@ -110,9 +116,9 @@ def _launch_stack(context): for view in views } if any(not serial for serial in camera_serials.values()): - raise RuntimeError("all Profile camera serial numbers are required") + raise RuntimeError("all required observation camera serial numbers are required") if len(set(camera_serials.values())) != len(views): - raise RuntimeError("Profile camera serial numbers must be unique") + raise RuntimeError("required observation camera serial numbers must be unique") cameras = [] components = [] @@ -153,6 +159,8 @@ def _launch_stack(context): ), "image_width": 1624, "image_height": 1240, + "timestamp_journal_path": str(Path(LaunchConfiguration("session_dir").perform(context)) + / f"camera_timing_{view}.jsonl"), "frame_rate": ParameterValue( LaunchConfiguration("camera_frame_rate"), value_type=float, @@ -194,15 +202,10 @@ def _launch_stack(context): plugin="AprilTagNode", name="apriltag", namespace=detector_namespace, - parameters=[ - str(tag_config), - { - "detector.decimate": ParameterValue( - LaunchConfiguration("apriltag_decimate"), - value_type=float, - ) - }, - ], + # Detector settings belong to the protected model YAML. + # A generic launch default must not silently replace its + # full-resolution setting for the small calibration Tags. + parameters=[str(tag_config)], remappings=[ ("image_rect", rect_topic), ("camera_info", info_topic), @@ -318,6 +321,9 @@ def _launch_stack(context): "commands_enabled": ParameterValue( LaunchConfiguration("commands_enabled"), value_type=bool ), + "diagnostic_capture": ParameterValue( + LaunchConfiguration("diagnostic_capture"), value_type=str + ), }, ], ) @@ -364,6 +370,10 @@ def _launch_stack(context): ), *cameras, vision, + Node(package="linkerhand_calibration", executable="tag_border_filter_node", + name="tag_border_filter", output="screen", + arguments=["--profile-config", profile_path, "--profile-sha256", expected, + "--views", *views]), sdk, calibration, bag, @@ -400,7 +410,6 @@ def generate_launch_description() -> LaunchDescription: DeclareLaunchArgument("exposure_time_us", default_value="5000.0"), DeclareLaunchArgument("gain_db", default_value="0.0"), DeclareLaunchArgument("auto_exposure", default_value="false"), - DeclareLaunchArgument("apriltag_decimate", default_value="1.5"), DeclareLaunchArgument("can_interface", default_value="can0"), DeclareLaunchArgument("calibration_speed", default_value="15"), DeclareLaunchArgument("camera_extrinsics_file", default_value=""), @@ -430,6 +439,7 @@ def generate_launch_description() -> LaunchDescription: ), DeclareLaunchArgument("profile_config", default_value=""), DeclareLaunchArgument("commands_enabled", default_value="true"), + DeclareLaunchArgument("diagnostic_capture", default_value=""), DeclareLaunchArgument("start_cameras", default_value="true"), DeclareLaunchArgument("start_sdk", default_value="true"), DeclareLaunchArgument("record_bag", default_value="false"), diff --git a/src/linkerhand_calibration/linkerhand_calibration/acquisition.py b/src/linkerhand_calibration/linkerhand_calibration/acquisition.py index 188a9bc..79e09fb 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/acquisition.py +++ b/src/linkerhand_calibration/linkerhand_calibration/acquisition.py @@ -109,6 +109,7 @@ class Observation: class StateSample: stamp_ns: int position_u8: tuple[float, ...] + channel_stamps_ns: tuple[int, ...] = () def interpolate_state_u8( @@ -127,6 +128,29 @@ def interpolate_state_u8( raise ValueError("maximum_skew_ns must be non-negative") if not samples: return None + if any(sample.channel_stamps_ns for sample in samples): + # A multi-frame SDK does not observe all motors at the same instant. + # De-duplicate each channel by its CAN receipt, then interpolate it + # independently. Repeated publication never creates extra support. + samples = tuple(sample for sample in samples if sample.stamp_ns >= stamp_ns-maximum_skew_ns + and sample.channel_stamps_ns and min(sample.channel_stamps_ns) <= stamp_ns+maximum_skew_ns) + if not samples: + return None + count = len(samples[0].position_u8) + channels = [dict() for _ in range(count)] + for sample in samples: + if len(sample.position_u8) != count or len(sample.channel_stamps_ns) != count: + return None + for index, (stamp, value) in enumerate(zip(sample.channel_stamps_ns, sample.position_u8)): + if stamp in channels[index] and channels[index][stamp] != value: + return None + channels[index][stamp] = value + matches = [interpolate_state_u8(tuple(StateSample(stamp, (value,)) + for stamp, value in sorted(channel.items())), stamp_ns, maximum_skew_ns=maximum_skew_ns) + for channel in channels] + if any(match is None for match in matches): + return None + return tuple(match[0][0] for match in matches), max(match[1] for match in matches) stamps = [int(sample.stamp_ns) for sample in samples] index = bisect_left(stamps, int(stamp_ns)) diff --git a/src/linkerhand_calibration/linkerhand_calibration/camera_timing.py b/src/linkerhand_calibration/linkerhand_calibration/camera_timing.py new file mode 100644 index 0000000..9a73ad2 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/camera_timing.py @@ -0,0 +1,165 @@ +"""Map camera frame clocks to host time before pairing images with feedback. + +The MVS frame counter timestamps exposure start. USB delivery/publication is +later. Clock latches bracket the device/host correspondence independently of +hand motion, images, or fitted calibration curves. +""" + +from dataclasses import asdict, dataclass +import json +import math +from pathlib import Path +import time + + +CAMERA_TIMING_POLICY = "mvs_latched_exposure_time_v1" + + +@dataclass(frozen=True) +class ClockLatch: + device_tick: int + before_ns: int + after_ns: int + + @property + def midpoint_ns(self): + return (self.before_ns+self.after_ns)//2 + + @property + def half_width_ns(self): + return (self.after_ns-self.before_ns)/2 + + +class DeviceFrameClock: + """A nominal device clock with short, measured host-time brackets. + + A new latch is checked against the previous one before it can change the + mapping. This rejects wrong counter units, resets and host clock jumps. + Periodic updates bound accumulated drift; none depend on image delivery. + """ + + def __init__(self, ticks_per_second): + if not math.isfinite(ticks_per_second) or ticks_per_second <= 0: + raise ValueError("invalid camera clock frequency") + self.ticks_per_second = ticks_per_second + self.anchor = None + self.last_frame_tick = None + self.last_stamp_ns = None + + def elapsed_ns(self, ticks): + return round(ticks*1_000_000_000/self.ticks_per_second) + + def update(self, latches): + if not latches or any(x.device_tick <= 0 or x.before_ns <= 0 + or not 0 <= x.after_ns-x.before_ns <= 2_000_000 for x in latches): + raise ValueError("camera clock latch is invalid or too uncertain") + previous = self.anchor or latches[0] + if self.anchor is None and latches[-1].before_ns-latches[0].after_ns < 50_000_000: + raise ValueError("camera clock units need an independent elapsed-time check") + for current in latches: + elapsed = self.elapsed_ns(current.device_tick-previous.device_tick) + host_elapsed = current.midpoint_ns-previous.midpoint_ns + tolerance = current.half_width_ns+previous.half_width_ns+1_000_000 + if elapsed < 0 or abs(elapsed-host_elapsed) > tolerance: + raise ValueError("camera/host clock discontinuity or counter unit mismatch") + self.anchor = min(latches, key=lambda x: x.half_width_ns) + + def timestamp(self, tick, *, received_ns, exposure_us): + if (self.anchor is None or not 0 <= received_ns-self.anchor.midpoint_ns <= 2_000_000_000 + or not math.isfinite(exposure_us) or exposure_us < 0): + raise ValueError("camera frame has no fresh clock or valid exposure") + if self.last_frame_tick is not None and tick <= self.last_frame_tick: + raise ValueError("camera frame counter reset or duplicate frame") + stamp = self.anchor.midpoint_ns+self.elapsed_ns(tick-self.anchor.device_tick)+round(exposure_us*500) + if stamp > received_ns+2_000_000 or (self.last_stamp_ns is not None and stamp <= self.last_stamp_ns): + raise ValueError("camera image time is future or out of order") + self.last_frame_tick, self.last_stamp_ns = tick, stamp + return stamp + + +class MvsCameraTiming: + """The camera-specific clock latch and its optional append-only journal.""" + + def __init__(self, camera, mvs, clock_ns, *, exposure_us, journal_path=""): + self.camera, self.mvs, self.clock_ns = camera, mvs, clock_ns + self.exposure_us = exposure_us + self.frame_clock = None + self.next_refresh = 0. + self.journal = None + if journal_path: + path = Path(journal_path) + path.parent.mkdir(parents=True, exist_ok=True) + self.journal = path.open("a", encoding="utf-8") + + def _integer(self, name): + value = self.mvs.MVCC_INTVALUE_EX() + status = self.camera.MV_CC_GetIntValueEx(name, value) + if status != 0: + raise ValueError(f"camera clock cannot read {name}:0x{status:08x}") + return int(value.nCurValue) + + def _latch(self): + before = self.clock_ns() + status = self.camera.MV_CC_SetCommandValue("DeviceTimestampLatch") + if status != 0: + raise ValueError(f"camera clock latch failed:0x{status:08x}") + tick = self._integer("DeviceTimestamp") + return ClockLatch(tick, before, self.clock_ns()) + + def _write(self, row): + if self.journal is not None: + self.journal.write(json.dumps(row, separators=(",", ":"))+"\n") + + def start(self): + # The installed USB camera firmware returns ticks/second for this + # feature (despite its XML unit label). Verify that interpretation + # against elapsed host time; do not assume it for another firmware. + self.frame_clock = DeviceFrameClock(self._integer("DeviceTimestampIncrement")) + latches = [] + for index in range(40): + if index: + time.sleep(.02) + latches.append(self._latch()) + valid = [x for x in latches if 0 <= x.after_ns-x.before_ns <= 2_000_000] + if len(valid) >= 8 and valid[-1].before_ns-valid[0].after_ns >= 50_000_000: + self._update(latches) + return + raise ValueError("camera clock has no bounded-latency startup latches") + + def _update(self, latches): + valid = [x for x in latches if 0 <= x.after_ns-x.before_ns <= 2_000_000] + if not valid: + # A delayed USB control transaction provides no new clock evidence. + # Keep the original two-second freshness bound while trying again. + self._write(dict(kind="camera_clock_latch_rejected", latches=[asdict(x) for x in latches])) + self.next_refresh = time.monotonic()+.02 + return + self.frame_clock.update(valid) + self._write(dict(kind="camera_clock_sync", policy=CAMERA_TIMING_POLICY, + ticks_per_second=self.frame_clock.ticks_per_second, + latches=[asdict(x) for x in latches], anchor=asdict(self.frame_clock.anchor))) + if self.journal is not None: + self.journal.flush() + self.next_refresh = time.monotonic()+.5 + + def refresh_if_due(self): + if time.monotonic() >= self.next_refresh: + self._update([self._latch() for _ in range(3)]) + + def timestamp(self, info, received_ns): + tick = (int(info.nDevTimeStampHigh)<<32)|int(info.nDevTimeStampLow) + # Auto-exposure preview uses the exact exposure-start timestamp when + # the SDK supplies no per-frame duration. Calibration fixes exposure. + measured_exposure = float(info.fExposureTime) + exposure = measured_exposure if measured_exposure > 0 else self.exposure_us + stamp = self.frame_clock.timestamp(tick, received_ns=received_ns, exposure_us=exposure) + self._write(dict(kind="camera_frame_time", frame_number=int(info.nFrameNum), + device_tick=tick, sdk_host_stamp_ms=int(info.nHostTimeStamp), + received_ns=received_ns, stamp_ns=stamp, exposure_us=exposure, + timestamp_reference="exposure_midpoint" if exposure else "exposure_start")) + return stamp + + def close(self): + if self.journal is not None: + self.journal.close() + self.journal = None diff --git a/src/linkerhand_calibration/linkerhand_calibration/compat/legacy_diagnostic_tools/models/o6/left_transfer.py b/src/linkerhand_calibration/linkerhand_calibration/compat/legacy_diagnostic_tools/models/o6/left_transfer.py index 13131b9..9eca14f 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/compat/legacy_diagnostic_tools/models/o6/left_transfer.py +++ b/src/linkerhand_calibration/linkerhand_calibration/compat/legacy_diagnostic_tools/models/o6/left_transfer.py @@ -39,6 +39,8 @@ def build_typed_profile(): right, key=KEY, namespace="/o6_left_transferred", + # This historical mirror has no measured left-hand range evidence. + urdf_limit_policies={}, command=replace( right.command, command_index_by_joint=command_index, @@ -116,6 +118,7 @@ def build_typed_profile(): artifacts=replace( right.artifacts, output_schema_version=2, + directional_command_joints=frozenset(), calibration_filename=( "o6_left_{serial_number}_transferred_calibration.json" ), diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/__init__.py b/src/linkerhand_calibration/linkerhand_calibration/core/__init__.py index 6cd02ad..31fb5a6 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/__init__.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/__init__.py @@ -7,6 +7,7 @@ from .domain import ( CalibrationProfile, CalibrationResult, JointMapping, + JointLimitPolicy, CalibrationStatus, CommandLayout, MeasurementPolicy, @@ -53,6 +54,7 @@ __all__ = [ "CalibrationProfile", "CalibrationResult", "JointMapping", + "JointLimitPolicy", "CalibrationStatus", "CommandLayout", "DIRECTION_DECREASING", diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/artifacts/storage.py b/src/linkerhand_calibration/linkerhand_calibration/core/artifacts/storage.py index 13f9a36..879af70 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/artifacts/storage.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/artifacts/storage.py @@ -31,9 +31,14 @@ def append_jsonl(path: str | Path, payload: Mapping[str, Any]) -> None: def append_jsonl_many( - path: str | Path, payloads: Iterable[Mapping[str, Any]] + path: str | Path, payloads: Iterable[Mapping[str, Any]], *, durable: bool = True, ) -> None: - """Durably append a batch while paying the fsync cost only once.""" + """Append one ordered batch, durable by default. + + Continuous observations may defer fsync until a reference/checkpoint, + pause, or explicit sync_jsonl barrier. They are still flushed before + returning; only complete, durable scan units can authorize resume. + """ destination = Path(path) destination.parent.mkdir(parents=True, exist_ok=True) lines = [ @@ -45,6 +50,13 @@ def append_jsonl_many( with destination.open("a", encoding="utf-8") as stream: stream.write("\n".join(lines) + "\n") stream.flush() + if durable: + os.fsync(stream.fileno()) + + +def sync_jsonl(path: str | Path) -> None: + """Persist all previously flushed records before hashing or publishing.""" + with Path(path).open("rb") as stream: os.fsync(stream.fileno()) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/domain/__init__.py b/src/linkerhand_calibration/linkerhand_calibration/core/domain/__init__.py index 8eb504a..3af428e 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/domain/__init__.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/domain/__init__.py @@ -19,6 +19,7 @@ from .profile import ( ZeroSolvePolicy, JointZeroSpec, KnownBaselineGeometry, + JointLimitPolicy, validate_profile, ) from .result import CalibrationResult, JointMapping @@ -42,6 +43,7 @@ __all__ = [ "JointZeroSample", "JointZeroSpec", "KnownBaselineGeometry", + "JointLimitPolicy", "CalibrationStatus", "CommandLayout", "MeasurementPolicy", diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/domain/profile.py b/src/linkerhand_calibration/linkerhand_calibration/core/domain/profile.py index 035432d..119d9a3 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/domain/profile.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/domain/profile.py @@ -8,7 +8,7 @@ from pathlib import PurePath from typing import Mapping -ACQUISITION_POLICY_VERSION = "unified_engine_v5_joint_zero" +ACQUISITION_POLICY_VERSION = "unified_engine_v8_all_view_images" @dataclass(frozen=True, order=True) @@ -337,9 +337,21 @@ class AcquisitionPolicy: require_endpoint_observations: bool = False endpoint_tolerance_01: float = 2.0 / 255.0 steady_training_nodes: int = 9 + command_capture_mode: str = "interleaved" + steady_extra_training_nodes: Mapping[str, tuple[float, ...]] = field(default_factory=dict) steady_minimum_samples: int = 3 steady_window_seconds: float = 0.2 steady_timeout_seconds: float = 2.0 + joint_zero_timeout_seconds: float = 2.0 + + +def observation_streams(profile, task): + """Primary and declared secondary views of each measured joint.""" + for name in task.joints: + yield "joint", name, profile.measurement.measurements[name] + secondary = profile.measurement.cross_view_sources.get(name) + if secondary is not None: + yield "observation_joint", name, profile.measurement.measurements[secondary] @dataclass(frozen=True) @@ -364,6 +376,22 @@ class ArtifactPolicy: publication_pointer: str = "latest_passed" session_compatibility_tokens: frozenset[str] = frozenset() publish_corrected_urdf: bool = False + directional_command_joints: frozenset[str] = frozenset() + + +@dataclass(frozen=True) +class JointLimitPolicy: + """An explicit interpretation of CAD limits, separate from SDK safety. + + Independent safety bounds, when known, are in original CAD coordinates. + Their absence never means that a calibrated motion envelope is a hardware + safety certificate. + """ + + source_limits: str + evidence_source: str + independent_safety_limits_cad_rad: tuple[float, float] | None = None + safety_evidence_source: str = "" @dataclass(frozen=True) @@ -388,6 +416,7 @@ class CalibrationProfile: # transferred_static_dynamic, transferred_dynamic_cad_static, cad_nominal, # and mimic_nominal. joint_coverage: Mapping[str, str] = field(default_factory=dict) + urdf_limit_policies: Mapping[str, JointLimitPolicy] = field(default_factory=dict) @property def curve_input_domain(self) -> str: @@ -723,6 +752,16 @@ def validate_profile(profile: CalibrationProfile) -> None: artifacts = profile.artifacts if artifacts.output_schema_version < 1: errors.append("artifact schema version must be positive") + if artifacts.directional_command_joints and (artifacts.output_schema_version != 3 + or not artifacts.directional_command_joints <= profile.measurement.measurements.keys()): + errors.append("directional command curves require measured v3 joints") + for target, source in profile.measurement.transferred_motion_sources.items(): + if (target in artifacts.directional_command_joints) != (source in artifacts.directional_command_joints): + errors.append("transferred command curves must retain their source direction policy") + for task in profile.motion.tasks: + if artifacts.directional_command_joints.intersection(task.joints): + if profile.command.baseline_values[task.command_index] not in (task.start_value, task.end_value): + errors.append("directional command mapping requires an endpoint baseline") for label, filename in ( ("calibration", artifacts.calibration_filename), ("corrected URDF", artifacts.corrected_urdf_filename), @@ -742,9 +781,33 @@ def validate_profile(profile: CalibrationProfile) -> None: valid_urdf_fields ): errors.append("URDF authorization contains an unsupported field") + for name, policy in profile.urdf_limit_policies.items(): + if name not in all_joints or not isinstance(policy, JointLimitPolicy): + errors.append(f"URDF limit policy requires a known movable joint:{name}") + continue + if policy.source_limits != "calibrated_motion_envelope" or not policy.evidence_source.strip(): + errors.append(f"URDF motion envelope requires explicit source-limit interpretation:{name}") + if not {"limit.lower", "limit.upper"} <= set(profile.urdf_authorized_fields.get(name, ())): + errors.append(f"URDF motion envelope lacks limit authorization:{name}") + bounds = policy.independent_safety_limits_cad_rad + if bounds is None: + if policy.safety_evidence_source: + errors.append(f"URDF safety evidence has no independent bounds:{name}") + elif (len(bounds) != 2 or not all(math.isfinite(v) for v in bounds) + or bounds[0] >= bounds[1] or not policy.safety_evidence_source.strip()): + errors.append(f"URDF independent safety bounds require finite limits and evidence:{name}") acquisition = profile.acquisition + if acquisition.command_capture_mode not in {"interleaved", "separate"}: + errors.append("unsupported command capture mode") + task_by_key = {task.key: task for task in profile.motion.tasks} + for key, values in acquisition.steady_extra_training_nodes.items(): + task = task_by_key.get(key) + if task is None or any(not math.isfinite(value) or not min(task.start_value, task.end_value) < value < max(task.start_value, task.end_value) for value in values): + errors.append(f"extra steady nodes must lie inside a declared task:{key}") if acquisition.steady_training_nodes < 3 or acquisition.steady_minimum_samples < 3: errors.append("steady mapping requires at least three nodes and three images per node") + if not acquisition.steady_timeout_seconds <= acquisition.joint_zero_timeout_seconds <= 10.0: + errors.append("joint zero capture timeout must be between the steady timeout and 10 seconds") if not 0 < acquisition.steady_window_seconds < acquisition.steady_timeout_seconds <= 2.0: errors.append("steady acquisition must have a finite stability window and <=2 second wait") if acquisition.policy_version != ACQUISITION_POLICY_VERSION: diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/domain/reference.py b/src/linkerhand_calibration/linkerhand_calibration/core/domain/reference.py index 3b111b6..0e36fb4 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/domain/reference.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/domain/reference.py @@ -2,8 +2,9 @@ from __future__ import annotations -from dataclasses import asdict, dataclass +from dataclasses import asdict, dataclass, replace import math +import re from typing import Mapping, Sequence import numpy as np @@ -46,6 +47,22 @@ class JointZeroSample: JointZeroPose.from_mapping(row["parent_pose_common"]), JointZeroPose.from_mapping(row["child_pose_common"])) +@dataclass(frozen=True) +class JointZeroBranchReference: + """A moving physical Tag's immutable pre-zero motion authorization.""" + + view: str + tag_role: str + branch_revision: int + motion_evidence_id: str + + def __post_init__(self): + if (not self.view or not self.tag_role or type(self.branch_revision) is not int + or self.branch_revision < 1 or not isinstance(self.motion_evidence_id, str) + or re.fullmatch(r"[0-9a-f]{64}", self.motion_evidence_id) is None): + raise ValueError("joint_zero_motion_evidence_invalid") + + @dataclass(frozen=True) class JointZeroReference: joint: str @@ -55,6 +72,7 @@ class JointZeroReference: session_epoch: int motion_version: int samples: tuple[JointZeroSample, ...] + branch_references: tuple[JointZeroBranchReference, ...] = () def quaternion(self, view): rotations = Rotation.from_quat([s.relative_quaternion_xyzw for s in self.samples if s.view == view]) @@ -81,7 +99,57 @@ class JointZeroReference: **{key: tuple(s[key]) for key in ("relative_quaternion_xyzw", "relative_translation_xyz_m", "command", "feedback", "arrival_directions")}}) for s in row["samples"]) return cls(str(row["joint"]), str(row["task_key"]), float(row["baseline_command"]), - int(row["channel"]), int(row["session_epoch"]), int(row["motion_version"]), samples) + int(row["channel"]), int(row["session_epoch"]), int(row["motion_version"]), samples, + tuple(JointZeroBranchReference(**item) for item in row.get("branch_references", ()))) + + +def joint_zero_moving_roles(profile, joint): + """Physical roles that require motion evidence for this joint's zero.""" + names = (joint, profile.measurement.cross_view_sources.get(joint)) + roles = set() + for name in names: + if name is None: + continue + measurement = profile.measurement.measurements[name] + for view in profile.vision.views: + if view.name == measurement.view: + roles.update((view.name, tag.role) for tag in view.tags + if tag.role in (measurement.parent_role, measurement.child_role) and not tag.fixed_reference) + return roles + + +def _branch_references(profile, joint, rows): + allowed = joint_zero_moving_roles(profile, joint) + result, observed = {}, {} + for row in rows: + for item in row.get("pnp_observation_evidence", {}).values(): + key = (row["view"], item.get("tag_role")) + diagnostics = item.get("candidate_diagnostics", {}) + verified, identity = diagnostics.get("motion_evidence_verified"), diagnostics.get("motion_evidence_id") + observed.setdefault(key, []).append((verified, identity)) + if not identity and verified is not True: + continue # Legacy image confirmation remains readable as legacy. + if (key not in allowed or verified is not True or item.get("pose_source") not in { + "current_image", "image_constrained_current_image"} + or diagnostics.get("observation_stamp_ns") != row["image_stamp_ns"] + or diagnostics.get("branch_status") != "tracking"): + raise ValueError(f"joint_zero_motion_evidence_unconfirmed:{joint}") + reference = JointZeroBranchReference(*key, diagnostics.get("branch_revision"), identity) + if key in result and result[key] != reference: + raise ValueError(f"joint_zero_motion_evidence_changed:{joint}") + result[key] = reference + for key, reference in result.items(): + required = sum(row["view"] == key[0] for row in rows) + if len(observed[key]) != required or any( + verified is not True or identity != reference.motion_evidence_id for verified, identity in observed[key]): + raise ValueError(f"joint_zero_motion_evidence_incomplete:{joint}") + return tuple(result[key] for key in sorted(result)) + + +def _validate_branch_bindings(profile, reference): + keys = [(item.view, item.tag_role) for item in reference.branch_references] + if len(set(keys)) != len(keys) or not set(keys) <= joint_zero_moving_roles(profile, reference.joint): + raise ValueError(f"joint_zero_motion_evidence_binding_invalid:{reference.joint}") def build_joint_zero_reference(profile, joint, rows: Sequence[Mapping], *, session_epoch, motion_version): @@ -91,9 +159,19 @@ def build_joint_zero_reference(profile, joint, rows: Sequence[Mapping], *, sessi secondary = profile.measurement.cross_view_sources.get(joint) if secondary: views.add(profile.measurement.measurements[secondary].view) - samples = tuple(JointZeroSample.from_row(row, profile.command.unit) for row in rows) if session_epoch < 1 or motion_version < 1: raise ValueError(f"joint_zero_session_version_invalid:{joint}") + # Incomplete windows are checked by the control timer repeatedly. Check + # their image coverage before constructing and validating every pose. + minimum = profile.acquisition.fixed_reference_minimum_frames + for view in views: + count = len({int(row["image_stamp_ns"]) for row in rows if row["view"] == view}) + if count < minimum: + measurements = [profile.measurement.measurements[n] for n in (joint, secondary) if n is not None] + roles = {role for m in measurements if m.view == view for role in (m.parent_role, m.child_role)} + ids = [t.tag_id for v in profile.vision.views if v.name == view for t in v.tags if t.role in roles] + raise ValueError(f"joint_zero_samples_missing:{joint}:{view}:required_tags={ids}:{count}/{minimum}") + samples = tuple(JointZeroSample.from_row(row, profile.command.unit) for row in rows) for sample in samples: if sample.image_stamp_ns < 1 or sample.sample_id != f"{sample.view}:{sample.image_stamp_ns}": raise ValueError(f"joint_zero_image_identity_invalid:{joint}") @@ -105,14 +183,8 @@ def build_joint_zero_reference(profile, joint, rows: Sequence[Mapping], *, sessi or not np.allclose(rp.inv().apply(np.asarray(child.translation_xyz_m)-parent.translation_xyz_m), sample.relative_translation_xyz_m, atol=1e-8, rtol=0)): raise ValueError(f"joint_zero_relative_pose_inconsistent:{joint}") - minimum = profile.acquisition.fixed_reference_minimum_frames for view in views: selected = [s for s in samples if s.view == view] - if len({s.sample_id for s in selected}) < minimum: - measurements = [profile.measurement.measurements[n] for n in (joint, secondary) if n is not None] - roles = {role for m in measurements if m.view == view for role in (m.parent_role, m.child_role)} - ids = [t.tag_id for v in profile.vision.views if v.name == view for t in v.tags if t.role in roles] - raise ValueError(f"joint_zero_samples_missing:{joint}:{view}:required_tags={ids}:{len(selected)}/{minimum}") rotations = Rotation.from_quat([s.relative_quaternion_xyzw for s in selected]) if np.max((rotations.mean().inv()*rotations).magnitude()) > math.radians(1): raise ValueError(f"joint_zero_not_stable:{joint}:{view}") @@ -132,7 +204,7 @@ def build_joint_zero_reference(profile, joint, rows: Sequence[Mapping], *, sessi if sample.arrival_directions[task.command_index] != expected: raise ValueError(f"joint_zero_arrival_changed:{joint}") return JointZeroReference(joint, spec.task_key, profile.command.baseline_values[task.command_index], - task.command_index, session_epoch, motion_version, samples) + task.command_index, session_epoch, motion_version, samples, _branch_references(profile, joint, rows)) def read_joint_zero_references(profile, records): @@ -164,6 +236,8 @@ def read_joint_zero_references(profile, records): for s in reference.samples] checked = build_joint_zero_reference(profile, name, rows, session_epoch=reference.session_epoch, motion_version=reference.motion_version) + _validate_branch_bindings(profile, reference) + checked = replace(checked, branch_references=reference.branch_references) if checked.channel != reference.channel: raise ValueError(f"joint_zero_channel_changed:{name}") result[name] = checked diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/domain/result.py b/src/linkerhand_calibration/linkerhand_calibration/core/domain/result.py index 55c2956..e478c1a 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/domain/result.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/domain/result.py @@ -82,7 +82,9 @@ class JointMapping: if not math.isfinite(value) or not self.knots[0] <= value <= self.knots[-1]: raise ValueError(f"SDK input outside fitted support:{self.joint}:{value}") branch = getattr(self, direction + "_rad") if direction else self.angle_rad - if self.input_domain.endswith("_u8"): + # Commands become actual wire bytes. Feedback can be interpolated + # between CAN receipts at an image's exposure time and stays continuous. + if self.input_domain == "command_u8": value = math.floor(value + 0.5) return float(np.interp(value, self.knots, branch)) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/domain/sampling.py b/src/linkerhand_calibration/linkerhand_calibration/core/domain/sampling.py index a936ff1..a4be453 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/domain/sampling.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/domain/sampling.py @@ -3,9 +3,18 @@ import numpy as np +def command_training_cycles(profile): + return (4,) if profile.acquisition.command_capture_mode == "separate" else (0, 1, 2) + + +def command_holdout_cycle(profile): + return 5 if profile.acquisition.command_capture_mode == "separate" else 3 + + def command_nodes(profile, task, *, holdout=False): lo, hi = sorted((task.start_value, task.end_value)) train = np.linspace(lo, hi, profile.acquisition.steady_training_nodes) + train = np.unique(np.r_[train, profile.acquisition.steady_extra_training_nodes.get(task.key, ())]) if profile.artifacts.output_schema_version >= 3: baseline = profile.command.baseline_values[task.command_index] if not lo <= baseline <= hi: diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/domain/status.py b/src/linkerhand_calibration/linkerhand_calibration/core/domain/status.py index 2b5cb66..49526bf 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/domain/status.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/domain/status.py @@ -13,6 +13,7 @@ class TaskStatus: cycle: int | None = None cycle_count: int = 4 direction: str = "" + capture_stage: str = "motion" required_tag_ids_by_view: Mapping[str, tuple[int, ...]] = field( default_factory=dict ) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/command_mapping.py b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/command_mapping.py index c982f3b..18e553b 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/command_mapping.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/command_mapping.py @@ -12,11 +12,13 @@ from ..domain.result import JointMapping from ..urdf.acceptance import angular_metrics from .curve import isotonic_nonincreasing from .spatial import measure_joint_curve_observation -from ..domain.sampling import command_nodes +from ..domain.sampling import command_nodes, command_training_cycles, command_holdout_cycle def fit_command_mappings(profile, fit, records_by_joint): mappings, metrics, applicability = {}, {}, {} + training_cycles = command_training_cycles(profile) + holdout_cycle = command_holdout_cycle(profile) for joint in sorted(fit.output_mappings): donor = joint if profile.artifacts.output_schema_version >= 3 else profile.zero.transferred_zero_sources.get(joint, joint) rows = records_by_joint.get(donor, ()) @@ -36,9 +38,11 @@ def fit_command_mappings(profile, fit, records_by_joint): channel = fit.output_mappings[joint].motor_index source_channel = fit.output_mappings[donor].motor_index branches = {} + directional = joint in profile.artifacts.directional_command_joints task = next(t for t in profile.motion.tasks if donor in t.joints) lo, hi = sorted((task.start_value, task.end_value)) - knots = command_nodes(profile, task) + from .command_sampling import declared_training_nodes + knots = declared_training_nodes(profile, task, rows) sign = profile.command.joint_directions[source_channel] if profile.artifacts.output_schema_version >= 3: sign = 1 if curve.angle_rad[-1] > curve.angle_rad[0] else -1 @@ -46,7 +50,7 @@ def fit_command_mappings(profile, fit, records_by_joint): values = [] for knot in knots: medians = [] - for cycle in (0, 1, 2): + for cycle in training_cycles: selected = [r for r in rows if r["cycle"] == cycle and r["direction"] == direction and math.isclose(float(r["steady_target"]), knot, abs_tol=1e-9)] if len(selected) < profile.acquisition.steady_minimum_samples: @@ -61,6 +65,11 @@ def fit_command_mappings(profile, fit, records_by_joint): if np.max(np.abs(projected-values)) > math.radians(1): raise ValueError(f"steady_command_not_monotonic:{joint}:{direction}") branches[direction] = tuple(float(v) for v in projected) + if directional: + from .anchored_mapping import anchored_mean + branches = {direction: anchored_mean(knots, values, values, + fit.joint_zero_references[joint].baseline_command, sign) + for direction, values in branches.items()} mean = tuple((a+b)/2 for a, b in zip(branches["increasing"], branches["decreasing"])) if profile.artifacts.output_schema_version >= 3: from .anchored_mapping import anchored_mean @@ -68,7 +77,7 @@ def fit_command_mappings(profile, fit, records_by_joint): fit.joint_zero_references[joint].baseline_command, sign) mapping = JointMapping(joint, channel, f"command_{profile.command.unit}", knots, mean, branches["increasing"], branches["decreasing"], donor if donor != joint else None) - validation = [r for r in rows if r["cycle"] == 3] + validation = [r for r in rows if r["cycle"] == holdout_cycle] expected = command_nodes(profile, task, holdout=True) for direction in ("increasing", "decreasing"): for knot in set(expected): @@ -76,7 +85,7 @@ def fit_command_mappings(profile, fit, records_by_joint): for r in validation) < profile.acquisition.steady_minimum_samples: raise ValueError(f"steady_holdout_incomplete:{joint}:{direction}:{knot}") errors = [mapping.evaluate(float(r[f"command_{profile.command.unit}"]), - "" if profile.artifacts.output_schema_version >= 3 else r["direction"]) + r["direction"] if directional or profile.artifacts.output_schema_version < 3 else "") - observed[r["sample_id"]] for r in validation] quality = angular_metrics(errors) if not quality.passed: @@ -84,14 +93,15 @@ def fit_command_mappings(profile, fit, records_by_joint): mappings[joint] = mapping from dataclasses import asdict metrics[joint] = {**asdict(quality), "independently_measured": donor == joint, - "source_joint": donor, "training_cycles": [0, 1, 2], "holdout_cycle": 3} + "source_joint": donor, "training_cycles": list(training_cycles), "holdout_cycle": holdout_cycle, + "training_sample_ids": sorted(r["sample_id"] for r in rows if r["cycle"] in training_cycles)} if profile.artifacts.output_schema_version >= 3: - directional = {direction: angular_metrics([error for error, row in zip(errors, validation) + branch_metrics = {direction: angular_metrics([error for error, row in zip(errors, validation) if row["direction"] == direction]) for direction in ("increasing", "decreasing")} - if not all(value.passed for value in directional.values()): - raise ValueError(f"single_command_curve_direction_holdout_failed:{joint}") - metrics[joint]["by_direction"] = {key: asdict(value) for key, value in directional.items()} - training = [r for r in rows if r["cycle"] in {0, 1, 2}] + if not all(value.passed for value in branch_metrics.values()): + raise ValueError(f"command_curve_direction_holdout_failed:{joint}") + metrics[joint]["by_direction"] = {key: asdict(value) for key, value in branch_metrics.items()} + training = [r for r in rows if r["cycle"] in training_cycles] vectors = np.asarray([r[f"command_vector_{profile.command.unit}"] for r in training]) held = {str(i): float(np.median(vectors[:, i])) for i in range(profile.command.command_count) if i != source_channel} @@ -101,5 +111,8 @@ def fit_command_mappings(profile, fit, records_by_joint): applicability[joint] = {"task": task.key, "held_command_values": held, "scope": "single_channel_at_declared_pose", "arbitrary_multiaxis_validated": False, "branch_initialization": "full_range_endpoint_reset"} + if directional: + applicability[joint].update(command_mapping="directional", direction_source="raw_sdk_input", + interior_reversals_validated=False) return replace(fit, command_mappings=mappings, command_holdout_metrics=metrics, command_applicability=applicability) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/command_sampling.py b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/command_sampling.py new file mode 100644 index 0000000..3b8fb49 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/command_sampling.py @@ -0,0 +1,141 @@ +"""Training-only reduction of static command knots with immutable evidence. + +The continuous curve chooses where to stop, never supplies a static command +angle. Every retained static knot is measured afresh in both directions. The +original independent validation grid and all publication gates remain intact. +""" + +import hashlib +import json +import math + +import numpy as np + +from ..domain.sampling import command_nodes +from ..domain.profile import observation_streams +from .rotation_curve import RotationObservation, fit_rotation_curve + + +SAMPLING_POLICY = "training_shape_static_knots_v1" +MAXIMUM_REMOVAL_ERROR_RAD = math.radians(.25) + + +def digest(value): + return hashlib.sha256(json.dumps(value, sort_keys=True, separators=(",", ":"), allow_nan=False).encode()).hexdigest() + + +def training_sources(profile, task, records): + streams = {(field, name, spec.view) for field, name, spec in observation_streams(profile, task)} + latest = {} + for row in records: + if row.get("task_name") == task.key and row.get("cycle") in {0, 1, 2} and row.get("direction"): + key = (row["cycle"], row["direction"]) + latest[key] = max(latest.get(key, 0), int(row.get("attempt", 1))) + result = [] + for row in records: + if (row.get("task_name") != task.key or row.get("cycle") not in {0, 1, 2} + or row.get("sample_phase", "sweep") != "sweep" or "relative_quaternion_xyzw" not in row): + continue + if int(row.get("attempt", 1)) != latest.get((row["cycle"], row["direction"])): + continue + for field, name, view in sorted(streams): + if row.get(field) == name and row.get("view") == view: + result.append({"field": field, "joint": name, "view": view, "cycle": row["cycle"], + "direction": row["direction"], "image_stamp_ns": row["image_stamp_ns"], + "input": float(row[f"feedback_{profile.command.unit}"]), + "quaternion": list(row["relative_quaternion_xyzw"])}) + return sorted(result, key=lambda row: (row["field"], row["joint"], row["view"], row["image_stamp_ns"])) + + +def reduced_knots(grid, curves, mandatory): + """Shortest path through the original grid; each omitted knot is bounded.""" + grid, values = np.asarray(grid), np.asarray(curves) + paths = {0: (0,)} + for end in range(1, len(grid)): + candidates = [] + for start, path in paths.items(): + if any(start < index < end for index in mandatory): + continue + fraction = (grid[start:end+1]-grid[start])/(grid[end]-grid[start]) + prediction = values[:, start, None] + (values[:, end]-values[:, start])[:, None]*fraction + if np.max(np.abs(prediction-values[:, start:end+1])) <= MAXIMUM_REMOVAL_ERROR_RAD: + candidates.append((*path, end)) + paths[end] = min(candidates, key=lambda path: (len(path), path)) + return tuple(float(grid[index]) for index in paths[len(grid)-1]) + + +def build_sampling_plan(profile, task, records): + sources = training_sources(profile, task, records) + grid = command_nodes(profile, task) + knots, reason = grid, "full_grid_insufficient_training_shape" + curves = [] + try: + for field, name, spec in observation_streams(profile, task): + rows = [row for row in sources if (row["field"], row["joint"], row["view"]) == (field, name, spec.view)] + observations = [RotationObservation(f"{row['view']}:{row['image_stamp_ns']}", row["input"], + tuple(row["quaternion"]), row["cycle"], row["direction"]) for row in rows] + fitted = fit_rotation_curve(observations, input_domain=f"feedback_{profile.command.unit}", + sdk_to_joint_direction=profile.command.joint_directions[task.command_index]) + if fitted.maximum_monotonic_correction_rad > MAXIMUM_REMOVAL_ERROR_RAD: + raise ValueError("training_shape_noisy") + for branch in (fitted.increasing_rad, fitted.decreasing_rad): + curves.append(np.interp(grid, fitted.knots, branch)) + mandatory_values = {grid[0], grid[-1], grid[len(grid)//2], + profile.command.baseline_values[task.command_index], + *profile.acquisition.steady_extra_training_nodes.get(task.key, ())} + mandatory = {i for i, value in enumerate(grid) if value in mandatory_values} + knots = reduced_knots(grid, curves, mandatory) + reason = "training_shape_bounded" if len(knots) < len(grid) else "full_grid_required_by_training_shape" + except (ValueError, KeyError, TypeError, np.linalg.LinAlgError) as error: + reason = "full_grid:" + str(error) + plan = {"kind": "command_sampling_plan", "policy": SAMPLING_POLICY, "task_name": task.key, + "training_nodes": list(knots), "validation_nodes": list(command_nodes(profile, task, holdout=True)), + "source_sha256": digest(sources), "source_count": len(sources), "training_cycles": [0, 1, 2], + "maximum_removal_error_rad": MAXIMUM_REMOVAL_ERROR_RAD, "reason": reason} + return {**plan, "plan_sha256": digest(plan)} + + +def declared_training_nodes(profile, task, records): + """Read a plan in complete journals or self-contained individual units.""" + declarations = set() + for row in records: + if row.get("task_name") != task.key: + continue + if row.get("kind") == "command_sampling_plan": + if digest({k: v for k, v in row.items() if k != "plan_sha256"}) != row.get("plan_sha256"): + raise ValueError("command_sampling_plan_hash_changed") + declarations.add(tuple(row["training_nodes"])) + elif row.get("sample_phase") == "steady" and "steady_training_nodes" in row: + declarations.add(tuple(row["steady_training_nodes"])) + if len(declarations) > 1: + raise ValueError("command_sampling_plan_conflict") + if not declarations: + return command_nodes(profile, task) + nodes = next(iter(declarations)) + allowed = set(command_nodes(profile, task)) + if (len(nodes) < 3 or tuple(sorted(set(nodes))) != nodes or not set(nodes) <= allowed + or nodes[0] != min(allowed) or nodes[-1] != max(allowed)): + raise ValueError("command_sampling_nodes_invalid") + return nodes + + +def validate_sampling_plans(profile, records): + plans = {} + for row in records: + if row.get("kind") == "command_sampling_plan": + task = next(task for task in profile.motion.tasks if task.key == row["task_name"]) + if row != build_sampling_plan(profile, task, records): + raise ValueError("command_sampling_training_evidence_changed") + if task.key in plans and plans[task.key] != row: + raise ValueError("command_sampling_plan_conflict") + plans[task.key] = row + required = any(row.get("command_sampling_policy") == SAMPLING_POLICY for row in records) + for row in records: + if row.get("sample_phase") != "steady" or "steady_target" not in row: + continue + plan = plans.get(row["task_name"]) + if required or "sampling_plan_sha256" in row: + if (plan is None or row.get("sampling_plan_sha256") != plan["plan_sha256"] + or list(row.get("steady_training_nodes", ())) != plan["training_nodes"]): + raise ValueError("command_sampling_sample_unbound") + return plans diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/coupling.py b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/coupling.py index ea1816f..0e4313d 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/coupling.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/coupling.py @@ -106,6 +106,7 @@ def fit_standard_mimic( cycles: Sequence[int], offset_rad: float | None, offset_observable: bool = False, + multiplier_bounds: tuple[float, float] | None = None, ) -> LinearMimicFit: """Fit actual synchronous observations, never a vendor-generated curve. @@ -139,8 +140,16 @@ def fit_standard_mimic( raise ValueError("mimic parameters are not independently observable") residual = rhs - design @ initial scale = max(math.radians(0.1), 1.4826 * float(np.median(np.abs(residual - np.median(residual))))) + bounds = (-np.inf, np.inf) + if multiplier_bounds is not None: + if (offset_rad is None or len(multiplier_bounds) != 2 + or not all(math.isfinite(v) for v in multiplier_bounds) + or multiplier_bounds[0] >= multiplier_bounds[1]): + raise ValueError("bounded mimic requires a fixed offset and a finite feasible interval") + bounds = multiplier_bounds + initial = np.clip(initial, *bounds) solution = least_squares(lambda parameters: design @ parameters - rhs, - initial, loss="soft_l1", f_scale=scale) + initial, loss="soft_l1", f_scale=scale, bounds=bounds) if not solution.success or not np.all(np.isfinite(solution.x)): raise ValueError("linear mimic fit failed") multiplier = float(solution.x[0]) @@ -149,6 +158,38 @@ def fit_standard_mimic( offset_rad is None, (0, 1, 2), tuple(float(v) for v in (multiplier*x + offset - y))) +def fit_range_bounded_mimic(source_joint, target_joint, active_rad, passive_rad, *, + cycles, parent_range, child_range): + """Best robust affine approximation within the unchanged joint limits. + + Optimize the two child angles at the parent's endpoints. Bounding those + two values guarantees that the entire affine motion stays in the child's + interval, including tiny negative baseline excursions. The intercept is + approximation error at baseline, NOT an observed physical zero correction. + """ + initial_fit = fit_standard_mimic(source_joint, target_joint, active_rad, passive_rad, + cycles=cycles, offset_rad=0.0) + for interval in (parent_range, child_range): + if (len(interval) != 2 or not all(math.isfinite(v) for v in interval) + or interval[0] >= interval[1]): + raise ValueError("invalid_mimic_motion_interval") + x, y = np.asarray(active_rad, dtype=float), np.asarray(passive_rad, dtype=float) + lower, upper = parent_range + fraction = (x - lower) / (upper - lower) + design = np.column_stack((1 - fraction, fraction)) + residual = np.asarray(initial_fit.residuals_rad) + scale = max(math.radians(0.1), 1.4826 * float(np.median(np.abs(residual - np.median(residual))))) + initial = np.clip(np.asarray(parent_range) * initial_fit.multiplier, *child_range) + solution = least_squares(lambda endpoints: design @ endpoints - y, initial, + loss="soft_l1", f_scale=scale, bounds=child_range) + if not solution.success or not np.all(np.isfinite(solution.x)): + raise ValueError("bounded_affine_mimic_fit_failed") + multiplier = float((solution.x[1] - solution.x[0]) / (upper - lower)) + offset = float(solution.x[0] - multiplier * lower) + return LinearMimicFit(source_joint, target_joint, multiplier, offset, False, + (0, 1, 2), tuple(float(v) for v in multiplier*x + offset - y)) + + @dataclass(frozen=True) class CouplingFit: source_joint: str diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/mimic_approximation.py b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/mimic_approximation.py index b796171..5beeb85 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/mimic_approximation.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/mimic_approximation.py @@ -1,9 +1,9 @@ -"""A training-only URDF approximation, separate from measured joint curves.""" +"""Training-only URDF approximations, separate from exact JSON lookup tables.""" -from dataclasses import dataclass +from dataclasses import dataclass, replace import math -from .coupling import LinearMimicFit, fit_standard_mimic +from .coupling import LinearMimicFit, fit_standard_mimic, fit_range_bounded_mimic from .observed_motion import paired_visual_observations @@ -14,16 +14,43 @@ class MimicApproximation: holdout_sample_ids: tuple[str, ...] holdout_errors_rad: tuple[float, ...] transferred_from_joint: str | None = None + multiplier_bounds: tuple[float, float] | None = None + parent_range_rad: tuple[float, float] | None = None + child_range_rad: tuple[float, float] | None = None def in_cad_coordinates(self, offsets): from dataclasses import replace + if self.parent_range_rad is not None: + raise ValueError("affine_approximation_intercept_is_not_a_physical_zero") fit = self.output_fit # The offset is supported by the TWO baseline geometry datums. return replace(fit, offset_rad=offsets[fit.target_joint]-fit.multiplier*offsets[fit.source_joint], offset_observed=True) -def fit_mimic_approximations(model, curves, records): +def shared_zero_multiplier_bounds(parent_range, child_range): + """Intersect endpoint constraints for q_child = a*q_parent. + + These ranges come from training support or mechanical bounds, never from + holdout observations. Neither measured curve nor joint range is modified. + """ + lower, upper = -math.inf, math.inf + if any(len(interval) != 2 or not all(math.isfinite(v) for v in interval) + or interval[0] >= interval[1] for interval in (parent_range, child_range)): + raise ValueError("invalid_mimic_motion_interval") + for endpoint in parent_range: + if endpoint == 0: + if not child_range[0] <= 0 <= child_range[1]: + raise ValueError("mimic_range_excludes_shared_zero") + continue + values = sorted(value / endpoint for value in child_range) + lower, upper = max(lower, values[0]), min(upper, values[1]) + if not math.isfinite(lower) or not math.isfinite(upper) or lower >= upper: + raise ValueError("no_feasible_shared_zero_mimic") + return lower, upper + + +def fit_mimic_approximations(model, curves, records, *, output_ranges=None, affine=False): result = {} for name, joint in model.joints.items(): parent = joint.mimic_joint @@ -36,10 +63,45 @@ def fit_mimic_approximations(model, curves, records): raise ValueError(f"paired_mimic_observations_missing:{name}:{cycle}:{direction}") training = [r for r in rows if r.cycle != 3] holdout = [r for r in rows if r.cycle == 3] - fit = fit_standard_mimic(parent, name, [r.source_rad for r in training], - [r.target_rad for r in training], cycles=[r.cycle for r in training], offset_rad=0.0) + bounds = None + arguments = dict(source_joint=parent, target_joint=name, + active_rad=[r.source_rad for r in training], passive_rad=[r.target_rad for r in training], + cycles=[r.cycle for r in training]) + if affine: + if output_ranges is None: + raise ValueError("affine_mimic_requires_training_motion_ranges") + fit = fit_range_bounded_mimic(**arguments, + parent_range=output_ranges[parent], child_range=output_ranges[name]) + else: + bounds = None if output_ranges is None else shared_zero_multiplier_bounds( + output_ranges[parent], output_ranges[name]) + fit = fit_standard_mimic(**arguments, offset_rad=0.0, multiplier_bounds=bounds) if not math.isfinite(fit.multiplier) or fit.multiplier == 0: raise ValueError(f"invalid_mimic_approximation:{name}") result[name] = MimicApproximation(fit, tuple(r.sample_id for r in training), - tuple(r.sample_id for r in holdout), tuple(fit.multiplier*r.source_rad-r.target_rad for r in holdout)) + tuple(r.sample_id for r in holdout), + tuple(fit.multiplier*r.source_rad + fit.offset_rad - r.target_rad for r in holdout), + multiplier_bounds=bounds, + parent_range_rad=tuple(output_ranges[parent]) if affine else None, + child_range_rad=tuple(output_ranges[name]) if affine else None) return result + + +def fit_bounded_mimic_approximations(profile, model, fit, records): + """Fit the URDF affine fallback after training establishes both ranges. + + Only independently observed pairs are fitted; explicit donor expansion is + performed afterwards. JSON continues to use the independent nonlinear maps. + """ + ranges = {} + for name, mapping in fit.output_mappings.items(): + joint = model.joints[name] + if name in profile.urdf_limit_policies or joint.mimic_joint is None: + command = fit.command_mappings[name] + ranges[name] = (min(0, mapping.bounds_rad[0], command.bounds_rad[0]), + max(0, mapping.bounds_rad[1], command.bounds_rad[1])) + else: + delta = fit.zero_offsets_rad[name] + ranges[name] = (joint.lower - delta, joint.upper - delta) + return replace(fit, mimic_approximations=fit_mimic_approximations( + model, fit.curves, records, output_ranges=ranges, affine=True)) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/motion_fit.py b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/motion_fit.py index 32f4c00..61f7f3f 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/motion_fit.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/motion_fit.py @@ -89,7 +89,7 @@ def fit_profile_motion(profile, source_model, records_by_joint, *, cross_view_re observed[name], holdout[name] = fit_native_observed_rotation(rows, input_domain=profile.curve_input_domain, input_to_joint_direction=joint_input_direction(profile, source_model, name), - reference_xyzw=reference.quaternion(view)) + reference_xyzw=reference.quaternion(view), joint=name, view=view) references[name] = reference.input_reference(view, feedback=True) coordinates = observed elif profile.command.unit == "u8": @@ -105,7 +105,8 @@ def fit_profile_motion(profile, source_model, records_by_joint, *, cross_view_re observed, coordinates, holdout, evidence, references = {}, {}, {}, {}, {} for name, rows in sorted(records_by_joint.items()): curve, errors = fit_native_observed_rotation(rows, input_domain=profile.curve_input_domain, - input_to_joint_direction=joint_input_direction(profile, source_model, name)) + input_to_joint_direction=joint_input_direction(profile, source_model, name), + joint=name, view=profile.measurement.measurements[name].view) observed[name], holdout[name] = curve, errors coordinates[name], references[name] = native_coordinate_curve(curve, profile.command.baseline_values[channel_for_joint(profile, name)]) @@ -124,7 +125,9 @@ def fit_profile_motion(profile, source_model, records_by_joint, *, cross_view_re curve, errors = fit_native_observed_rotation(rows, input_domain=profile.curve_input_domain, input_to_joint_direction=joint_input_direction(profile, source_model, name), reference_xyzw=(zero_references[name].quaternion(rows[0]["view"]) - if profile.artifacts.output_schema_version >= 3 else None)) + if profile.artifacts.output_schema_version >= 3 else None), + joint=name, view=profile.measurement.measurements[ + profile.measurement.cross_view_sources[name]].view) if "input_knots" not in observed[name].circle: raise ValueError("cross-view comparison requires native input knots") comparison = compare_native_views(observed[name], curve) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/motion_transfer.py b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/motion_transfer.py index 600e64d..4566e99 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/motion_transfer.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/motion_transfer.py @@ -42,7 +42,9 @@ def expand_profile_transfers(profile: CalibrationProfile, fit: CalibrationResult if profile.zero.transferred_zero_sources.get(target) == source and source in offsets: offsets[target] = offsets[source] methods[target] = "transferred_static_zero_on_own_cad" - if target in profile.zero.passive_joints: + # Historical fitted approximations remain transferable for replay. + # Current measured fitting emits no mimic fit; each recipient keeps its CAD coupling. + if target in profile.zero.passive_joints and approximations: if source not in fit.mimic_approximations: raise ValueError(f"motion_transfer_requires_paired_donor_mimic:{target}:{source}") donor = fit.mimic_approximations[source] diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/observed_motion.py b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/observed_motion.py index f2957a5..b10aa98 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/observed_motion.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/observed_motion.py @@ -10,6 +10,7 @@ from .image_curve import fit_joint_image_curve from ..urdf.acceptance import angular_metrics from ..domain.measurement import JointCurveFit from .rotation_curve import RotationObservation, fit_rotation_curve +from .rotation_diagnostics import RotationHoldoutFailure, rotation_holdout_diagnostic def image_identity(row: Mapping[str, Any]) -> str: @@ -44,7 +45,8 @@ def paired_visual_observations(source_rows, target_rows, source_fit, target_fit) return output -def fit_native_observed_rotation(rows, *, input_domain, input_to_joint_direction, reference_xyzw=None): +def fit_native_observed_rotation(rows, *, input_domain, input_to_joint_direction, + reference_xyzw=None, joint=None, view=None): """Native-coordinate 3+1 hinge primitive with the shared geometric result. The metadata carries real knots, not a synthetic byte command domain. @@ -60,10 +62,12 @@ def fit_native_observed_rotation(rows, *, input_domain, input_to_joint_direction fitted = fit_rotation_curve([row for row in observations if row.cycle != 3], input_domain=input_domain, sdk_to_joint_direction=input_to_joint_direction, reference_xyzw=reference_xyzw) - errors = fitted.holdout_errors([row for row in observations if row.cycle == 3]) + holdout = [row for row in observations if row.cycle == 3] + errors = fitted.holdout_errors(holdout) metrics = angular_metrics(errors) if not metrics.passed: - raise ValueError("independent_rotation_holdout_failed") + raise RotationHoldoutFailure(rotation_holdout_diagnostic( + fitted, holdout, errors, metrics, joint=joint, view=view)) hysteresis = float(np.max(np.abs(np.asarray(fitted.increasing_rad) - fitted.decreasing_rad))) curve = JointCurveFit(fitted.angle_rad, fitted.decreasing_rad, fitted.increasing_rad, {"space": "relative_rotation_3d", "input_domain": input_domain, "input_knots": fitted.knots, diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/rotation_diagnostics.py b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/rotation_diagnostics.py new file mode 100644 index 0000000..3279b31 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/rotation_diagnostics.py @@ -0,0 +1,78 @@ +"""Explain a failed frozen rotation holdout without changing its acceptance.""" + +from __future__ import annotations + +from dataclasses import dataclass +import math +from typing import Sequence + +from ..urdf.acceptance import ErrorMetrics, angular_metrics +from .rotation_curve import RotationCurve, RotationObservation + + +@dataclass(frozen=True) +class RotationHoldoutSample: + sample_id: str + cycle: int + direction: str + input_value: float + full_so3_error_deg: float + axial_error_deg: float + off_axis_error_deg: float + + +@dataclass(frozen=True) +class RotationHoldoutDiagnostic: + joint: str | None + view: str | None + input_domain: str + full_so3: ErrorMetrics + axial: ErrorMetrics + off_axis: ErrorMetrics + worst_sample: RotationHoldoutSample + training_cycles: tuple[int, ...] = (0, 1, 2) + holdout_cycle: int = 3 + + +class RotationHoldoutFailure(ValueError): + """A rejected rotation fit with serializable, read-only sample evidence.""" + + def __init__(self, diagnostics: RotationHoldoutDiagnostic): + self.diagnostics = diagnostics + metrics = diagnostics.full_so3 + super().__init__( + "independent_rotation_holdout_failed:" + f"joint={diagnostics.joint or 'unknown'}:view={diagnostics.view or 'unknown'}:" + f"mae_deg={metrics.mae_deg:.6f}:p95_deg={metrics.p95_deg:.6f}:" + f"max_deg={metrics.maximum_deg:.6f}") + + +def rotation_holdout_diagnostic( + fitted: RotationCurve, + observations: Sequence[RotationObservation], + errors_rad: Sequence[float], + metrics: ErrorMetrics, + *, + joint: str | None, + view: str | None, +) -> RotationHoldoutDiagnostic: + """Describe the existing failed fit; never fit, re-zero or filter samples. + + Axial error compares projected log angles. Off-axis error measures the + observed rotation's departure from the frozen hinge axis. These explain + different effects; their magnitudes are not additive SO(3) components. + Only the original full SO(3) metrics determine acceptance. + """ + measurements = [fitted.measure(row.quaternion_xyzw) for row in observations] + axial = [angle - fitted.predict(row.input_value, row.direction) + for row, (angle, _) in zip(observations, measurements)] + off_axis = [error for _, error in measurements] + worst_index = max(range(len(errors_rad)), key=errors_rad.__getitem__) + worst = observations[worst_index] + return RotationHoldoutDiagnostic( + joint, view, fitted.input_domain, metrics, + angular_metrics(axial), angular_metrics(off_axis), + RotationHoldoutSample( + worst.sample_id, worst.cycle, worst.direction, worst.input_value, + math.degrees(errors_rad[worst_index]), math.degrees(axial[worst_index]), + math.degrees(off_axis[worst_index]))) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/session.py b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/session.py index cf21cbc..f1d0eeb 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/session.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/session.py @@ -32,7 +32,7 @@ def compile_spatial_profile(profile: CalibrationProfile) -> ZeroCalibrationProfi "directed_base_axis_joints", "depth_free_axis_projection", "axis_order", "same_view_axis_pair_by_offset", "parallel_root_pattern", - "accept_validated_zero_in_confidence_interval", "project_axis_gauge_before_image"} + "accept_validated_zero_in_confidence_interval"} if not data or set(data) - allowed: raise ValueError("spatial observation graph is missing or has unsupported fields") direct = tuple(profile.zero.direct_zero_joints) @@ -42,6 +42,12 @@ def compile_spatial_profile(profile: CalibrationProfile) -> ZeroCalibrationProfi axes = tuple(data.get("axis_order", profile.zero.axis_joints)) if set(axes) != set(profile.zero.axis_joints) or len(set(axes)) != len(axes): raise ValueError("spatial axis order differs from declared observations") + # Coordinate descent must solve an upstream zero before the downstream + # phase that depends on it. The declaration of required zeros is a set of + # requirements, not an optimization order (O6 used pitch before yaw). + if not set(direct) <= set(axes): + raise ValueError("spatial zero has no position in the declared axis order") + direct = tuple(name for name in axes if name in direct) return ZeroCalibrationProfile( hand=SpatialHandContract(profile.key.side, profile.key.layout, tuple(profile.zero.active_joints), stable_cross_view_cone_bias=profile.measurement.stable_cross_view_cone_bias), @@ -60,7 +66,6 @@ def compile_spatial_profile(profile: CalibrationProfile) -> ZeroCalibrationProfi orientation_anchor_joint=data.get("orientation_anchor_joint"), directed_base_axis_joints=frozenset(data.get("directed_base_axis_joints", ())), accept_validated_zero_in_confidence_interval=bool(data.get("accept_validated_zero_in_confidence_interval", False)), - project_axis_gauge_before_image=bool(data.get("project_axis_gauge_before_image", False)), parallel_root_pattern=bool(data.get("parallel_root_pattern", False)), independent_observed_motion=profile.artifacts.output_schema_version >= 3) @@ -136,7 +141,6 @@ def fit_profile_calibration(profile: CalibrationProfile, source_urdf: Path, reco offsets = {name: float(zero.direct_offsets_rad[name]) for name in profile.zero.direct_zero_joints} methods = {name: "urdf_serial_axis_geometry" for name in offsets} if measured: - from .mimic_approximation import fit_mimic_approximations for name, datum in profile.zero.known_baseline_geometry.items(): offsets[name], methods[name] = datum.cad_angle_rad, "independent_known_baseline_geometry" for name in profile.zero.cad_zero_assumptions: @@ -145,8 +149,7 @@ def fit_profile_calibration(profile: CalibrationProfile, source_urdf: Path, reco tuple(curve.circle["input_knots"]), curve.angle_rad, curve.increasing_rad, curve.decreasing_rad) for name, curve in curves.items()} return CalibrationResult(curves, mappings, offsets, methods, {}, motion.holdout_errors_rad, zero, - motion.cross_view_metrics, motion.reference_inputs, joint_zero_references=dict(zero_references), - mimic_approximations=fit_mimic_approximations(model, curves, records_by_joint)) + motion.cross_view_metrics, motion.reference_inputs, joint_zero_references=dict(zero_references)) for name in profile.zero.active_joints - offsets.keys() - profile.zero.transferred_zero_sources.keys(): if name not in profile.zero.cad_frozen_joints: raise ValueError(f"active static zero has no measured or CAD-retention policy:{name}") diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/spatial_solver/residuals.py b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/spatial_solver/residuals.py index f10ec47..42f8d64 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/spatial_solver/residuals.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/spatial_solver/residuals.py @@ -93,7 +93,6 @@ class ObservationGeometry: observed_child_point: np.ndarray, view_normal_common: Sequence[float] | None = None, ) -> float: - profile = self.profile parent_axis = predicted_parent_axis / np.linalg.norm(predicted_parent_axis) observed_axis = observed_parent_axis / np.linalg.norm(observed_parent_axis) if float(parent_axis @ observed_axis) < 0.0: @@ -129,19 +128,11 @@ class ObservationGeometry: # mounts therefore choose different along-axis gauges. # Reusing delta here would reintroduce that arbitrary # coordinate and turn it into a phase at oblique views. - predicted_image_input = ( - predicted_radial if profile.project_axis_gauge_before_image - else predicted_delta + predicted_radial = predicted_radial - view_normal * float( + predicted_radial @ view_normal ) - observed_image_input = ( - observed_radial if profile.project_axis_gauge_before_image - else observed_delta - ) - predicted_radial = predicted_image_input - view_normal * float( - predicted_image_input @ view_normal - ) - observed_radial = observed_image_input - view_normal * float( - observed_image_input @ view_normal + observed_radial = observed_radial - view_normal * float( + observed_radial @ view_normal ) angle_axis = view_normal if float(angle_axis @ parent_axis) < 0.0: diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/spatial_solver/types.py b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/spatial_solver/types.py index 25e3207..b2ae27c 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/spatial_solver/types.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/spatial_solver/types.py @@ -58,11 +58,6 @@ class ZeroCalibrationProfile: # contains it. The frozen zero still faces every geometry/cycle/holdout # check below. Default False preserves existing G20/L6/O6 decisions. accept_validated_zero_in_confidence_interval: bool = False - # Axis-line points have no unique coordinate along the axis. Remove that - # gauge before projecting a parallel-axis phase into the camera plane. - # Opt in explicitly while legacy profiles retain their reviewed policy. - project_axis_gauge_before_image: bool = False - parallel_root_pattern: bool = False common_mode_zero_joints: tuple[str, ...] = () small_offset_joints: frozenset[str] = frozenset() diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/tag_installation.py b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/tag_installation.py index 2f4f3ab..9fdbfb0 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/fitting/tag_installation.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/fitting/tag_installation.py @@ -61,6 +61,17 @@ class FrozenTagInstallation: training_translation_p95_m: float +class TagInstallationFailure(ValueError): + """All role residuals from one failed rigid-chain validation.""" + + def __init__(self, diagnostics): + self.diagnostics = diagnostics + failed = [name for name, row in diagnostics.items() if not row["passed"]] + super().__init__("tag_installation_not_rigid:" + ";".join( + f"{name}:rotation_p95_deg={diagnostics[name]['rotation_p95_deg']:.6f}:" + f"translation_p95_m={diagnostics[name]['translation_p95_m']:.6f}" for name in failed)) + + def fit_tag_installations( *, source_model: UrdfKinematicModel, common_from_base, link_by_role: Mapping[str, str], observations: Sequence[TagTrainingPose], @@ -84,8 +95,8 @@ def fit_tag_installations( if previous is not None and (previous[0] != row.cycle or not np.allclose(previous[1], candidate, atol=1e-8, rtol=0)): raise ValueError(f"conflicting duplicate Tag image:{row.role}:{row.sample_id}") grouped[row.role][row.sample_id] = (row.cycle, candidate) - output = {} - for role, unique in grouped.items(): + output, diagnostics = {}, {} + for role, unique in sorted(grouped.items()): if len(unique) < minimum_samples or {value[0] for value in unique.values()} != {0, 1, 2}: raise ValueError(f"Tag installation lacks three-cycle training evidence:{role}") matrices = np.asarray([value[1] for value in unique.values()]) @@ -96,12 +107,15 @@ def fit_tag_installations( distances = np.linalg.norm(matrices[:, :3, 3] - translation, axis=1) rotation_p95 = float(np.rad2deg(np.percentile(errors, 95))) translation_p95 = float(np.percentile(distances, 95)) - if rotation_p95 > 2.0 or translation_p95 > 0.003: - raise ValueError(f"Tag installation is not rigid across training tasks:{role}:rotation_p95_deg={rotation_p95}:translation_p95_m={translation_p95}") + diagnostics[role] = {"count": len(unique), "rotation_p95_deg": rotation_p95, + "translation_p95_m": translation_p95, + "passed": rotation_p95 <= 2.0 and translation_p95 <= 0.003} transform = np.eye(4) transform[:3, :3], transform[:3, 3] = rotation.as_matrix(), translation output[role] = FrozenTagInstallation(role, link_by_role[role], matrix_tuple(transform), tuple(sorted(unique)), rotation_p95, translation_p95) + if any(not row["passed"] for row in diagnostics.values()): + raise TagInstallationFailure(diagnostics) return output diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/candidate_selection.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/candidate_selection.py index 2766cf8..64cd8e9 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/candidate_selection.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/candidate_selection.py @@ -5,11 +5,17 @@ poses, substitute SDK angles, or relax the spatial solver's acceptance gates. """ from itertools import product import math +from collections.abc import Mapping import numpy as np from scipy.spatial.transform import Rotation as R from .pnp import solve_square_tag_ippe +from .tag_pose.ippe import fixed_pose_reprojection_error +from .tag_pose.branch_confirmation import BranchConfirmation +from .tag_pose.parameters import DEFAULT_POSE_TRACKING_PARAMETERS, FROZEN_BRANCH_POLICY_VERSIONS +from .tag_pose.types import SquareTagPose +from .image_motion_replay import IMAGE_POSE_SOURCE, has_image_model, replay_image_models, validate_image_sample POLICY = 'training_frozen_articulated_candidates_v1' MATRIX_SOURCE = 'CameraInfo.P[:3,:3]' @@ -41,15 +47,107 @@ def _trajectory_score(parent, child): 'score': rms/.0015 + off_axis/math.radians(1)} +def _recorded_pose(payload): + if not isinstance(payload, Mapping): + raise ValueError('frozen_branch_evidence_missing:selected_pose') + quaternion = tuple(payload.get('quaternion_xyzw', ())) + translation = tuple(payload.get('translation_xyz_m', ())) + if (len(quaternion) != 4 or len(translation) != 3 + or not np.all(np.isfinite(quaternion + translation)) + or not np.isclose(np.linalg.norm(quaternion), 1, atol=1e-5)): + raise ValueError('frozen_branch_evidence_invalid:selected_pose') + return SquareTagPose(quaternion, translation, 0.) + + +def _check_frozen_paths(rows, evidence, selected_paths, role_pairs, fixed_roles): + """A geometry preference cannot replace any already-frozen image branch. + + Revisions identify a role within its capture, not a physical installation + across sessions. JointResume verifies the latter before importing rows. + Compare each path pose with that image's original confirmed pose instead + of assuming numerically equal revisions authorize cross-session reuse. + """ + equivalence = BranchConfirmation(DEFAULT_POSE_TRACKING_PARAMETERS) + for row in rows: + stamp, view = int(row['image_stamp_ns']), row.get('view') + frame = evidence[stamp] + identity = f"joint={row['joint']}:view={view}:image={stamp}:cycle={row['cycle']}" + if (not view or frame.get('view') != view or any( + frame.get(key) != row.get(key) for key in ('cycle', 'direction', 'attempt'))): + raise ValueError(f'frozen_branch_frame_identity_changed:{identity}') + observed = row.get('pnp_observation_evidence', {}) + if not isinstance(observed, Mapping): + raise ValueError(f'frozen_branch_evidence_missing:{identity}') + for field, role in zip(('parent', 'child'), role_pairs[row['joint']]): + item = observed.get(field, {}) + if not isinstance(item, Mapping) or item.get('tag_role') != role: + raise ValueError(f'frozen_branch_evidence_missing:{identity}:role={role}') + source = item.get('pose_source') + fixed_source = source in {'cached_fixed_reference', 'verified_fixed_reference'} + if (fixed_source + != bool(frame['roles'][role].get('locked_reference'))): + raise ValueError(f'frozen_branch_pose_source_changed:{identity}:role={role}') + if fixed_source and role not in fixed_roles: + raise ValueError(f'frozen_branch_fixed_role_invalid:{identity}:role={role}') + if source == 'cached_fixed_reference' and role in fixed_roles: + revision = item.get('reference_branch_revision') + else: + diagnostics = item.get('candidate_diagnostics', {}) + if (source not in {'current_image', 'verified_fixed_reference', IMAGE_POSE_SOURCE} or not isinstance(diagnostics, Mapping) + or diagnostics.get('observation_stamp_ns') != stamp + or diagnostics.get('branch_status') != 'tracking' + or diagnostics.get('branch_frozen') is not True): + raise ValueError(f'frozen_branch_evidence_unconfirmed:{identity}:role={role}') + revision = diagnostics.get('branch_revision') + if source == 'verified_fixed_reference' and ( + diagnostics.get('fixed_reference_verified') is not True + or item.get('reference_branch_revision') != revision): + raise ValueError(f'frozen_branch_fixed_reference_unverified:{identity}:role={role}') + if isinstance(revision, bool) or not isinstance(revision, int) or revision < 1: + raise ValueError(f'frozen_branch_revision_invalid:{identity}:role={role}') + original = _recorded_pose(item.get('selected_pose')) + if source == 'verified_fixed_reference': + _check_fixed_projection(original, item, frame, role, identity) + recorded = _recorded_pose(frame['roles'][role].get('selected')) + if not equivalence.equivalent(original, recorded): + raise ValueError(f'frozen_branch_recorded_pose_changed:{identity}:role={role}') + proposed = _recorded_pose(selected_paths[stamp][role]) + if not equivalence.equivalent(original, proposed): + angle, translation = equivalence.distance(original, proposed) + raise ValueError(f'frozen_branch_reselection_forbidden:{identity}:role={role}:' + f'rotation_deg={math.degrees(angle):.6f}:translation_mm={translation*1000:.6f}') + + +def _check_fixed_projection(pose, item, frame, role, identity): + """A fixed-pose marker cannot bypass current-image geometric evidence.""" + data = frame['roles'][role] + try: + if not np.array_equal(np.asarray(item['corners_xy']), np.asarray(data['corners_xy'])): + raise ValueError('corners changed') + error = fixed_pose_reprojection_error(pose, data['corners_xy'], + tag_size_m=data.get('tag_size_m', frame['tag_size_m']), camera_matrix=frame['camera_matrix']) + limit = float(data.get('maximum_reprojection_error_px', 1.5)) + if not math.isfinite(limit) or limit <= 0 or not math.isfinite(error) or error > limit: + raise ValueError('fixed pose no longer explains this image') + except (KeyError, TypeError, ValueError) as exc: + raise ValueError(f'frozen_branch_fixed_projection_invalid:{identity}:role={role}') from exc + + def resolve_chain_observations(records, *, task_key, role_pairs, - projection_override=None, solve_pose=solve_square_tag_ippe, fixed_roles=()): + projection_override=None, solve_pose=solve_square_tag_ippe, fixed_roles=(), + require_frozen_branches=False): """Return copied observations and auditable candidate-selection evidence. Older captures did not save P. They remain on the legacy path; K is never guessed to be P. An explicit override is for isolated offline diagnosis and must be reported as such, not advertised as a verified whole session. + A current capture automatically preserves its frozen branches; production + callers enforce this even when a journal's policy header was removed. """ rows = [dict(r) for r in records] + preserve_frozen = require_frozen_branches or any( + row.get('kind') == 'session_start' + and row.get('pose_tracking_policy_version') in FROZEN_BRANCH_POLICY_VERSIONS for row in rows) roles = tuple(dict.fromkeys(role for pair in role_pairs.values() for role in pair)) if not role_pairs or len(roles) > 8 or any(len(pair) != 2 or pair[0] == pair[1] for pair in role_pairs.values()): raise ValueError('candidate selection requires a bounded articulated observation graph') @@ -62,6 +160,8 @@ def resolve_chain_observations(records, *, task_key, role_pairs, 'projection_override_used': projection_override is not None, 'is_accuracy_certificate': False} if not evidence: + if preserve_frozen: + raise ValueError(f'frozen_branch_evidence_missing:task={task_key}:candidate_frames') return rows, {**report, 'status': 'legacy_no_corners'} if projection_override is None and any( r.get('camera_matrix_source') != MATRIX_SOURCE for r in evidence.values()): @@ -81,6 +181,23 @@ def resolve_chain_observations(records, *, task_key, role_pairs, raise ValueError('corner replay requires training and independent holdout') if np.flatnonzero(training)[-1] > np.flatnonzero(~training)[0]: raise ValueError('holdout must follow the complete training trajectory') + if any(has_image_model(item) for stamp in stamps for item in evidence[stamp]['roles'].values()): + if projection_override is not None: + raise ValueError('frozen image model cannot use a different camera projection') + lookup, verified = {}, {} + for stamp in stamps: + verified[stamp] = replay_image_models(evidence[stamp]) + if not verified[stamp]: + raise ValueError('image_motion_provenance_mixed_pose_policies') + lookup[stamp] = {role: evidence[stamp]['roles'][role]['selected'] for role in roles} + for row in chosen_rows: + stamp = int(row['image_stamp_ns']) + validate_image_sample(row, evidence[stamp], verified[stamp]) + _check_frozen_paths(chosen_rows, evidence, lookup, role_pairs, fixed_roles) + return rows, {**report, 'policy': 'frozen_image_motion_replay_v1', 'status': 'resolved', + 'frozen_branches_preserved': True, 'geometry_refitted': False, + 'training_frames': int(sum(training)), 'holdout_frames': int(sum(~training)), + 'changed_joint_rows': 0} paths = {role: [[], []] for role in roles} for stamp in stamps: frame = evidence[stamp] @@ -90,7 +207,8 @@ def resolve_chain_observations(records, *, task_key, role_pairs, if data.get('locked_reference'): if role not in fixed_roles or not data.get('selected'): raise ValueError('cached fixed reference has no frozen pose') - cs = [dict(data['selected'], reprojection_error_px=0.0)] + cs = [dict(data['selected'], + reprojection_error_px=float(data['selected'].get('reprojection_error_px', 0.0)))] else: poses = solve_pose(data['corners_xy'], tag_size_m=data.get('tag_size_m', frame['tag_size_m']), camera_matrix=matrix) limit = float(data.get('maximum_reprojection_error_px', 1.5)) @@ -120,6 +238,9 @@ def resolve_chain_observations(records, *, task_key, role_pairs, selected = min(scores, key=lambda s: s['score']) lookup = {stamp: {role: paths[role][branch][i] for role, branch in zip(roles, selected['branches'])} for i, stamp in enumerate(stamps)} + if preserve_frozen: + _check_frozen_paths(chosen_rows, evidence, lookup, role_pairs, fixed_roles) + report['frozen_branches_preserved'] = True changed = 0 for row in chosen_rows: stamp = int(row['image_stamp_ns']) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/image_motion_replay.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/image_motion_replay.py new file mode 100644 index 0000000..997a747 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/image_motion_replay.py @@ -0,0 +1,167 @@ +"""Read back image-constrained poses against their frozen model and raw corners. + +This never trains geometry or chooses a new trajectory. The image solver may +estimate only this image's joint angles, including on the independent holdout. +Raw IPPE candidates remain separate from the selected constrained poses. +""" + +from collections.abc import Mapping +import hashlib +import json + +import numpy as np +from scipy.spatial.transform import Rotation + +from .tag_pose.motion_evidence import MotionEvidenceFrame, RoleCandidates +from .tag_pose.motion_image_types import ImageMotionFrame, ImageRoleObservation +from .tag_pose.types import SquareTagPose + + +IMAGE_POSE_SOURCE = "image_constrained_current_image" + + +def image_model_sha256(payload): + encoded = json.dumps(payload, sort_keys=True, separators=(",", ":"), allow_nan=False) + return hashlib.sha256(encoded.encode()).hexdigest() + + +def frozen_image_model(item): + """Read a named, content-bound model; a marker alone grants no permission.""" + from .tag_pose.production_image_motion import image_motion_model_from_dict + + payload, identity = item.get("frozen_image_model"), item.get("image_model_sha256") + if not isinstance(payload, Mapping) or image_model_sha256(payload) != identity: + raise ValueError("image_motion_provenance_model_hash_invalid") + return image_motion_model_from_dict(payload) + + +def has_image_model(item): + return (item.get("pose_source") == IMAGE_POSE_SOURCE + or "frozen_image_model" in item or "image_model_sha256" in item) + + +def _pose(payload): + try: + q, t = tuple(payload["quaternion_xyzw"]), tuple(payload["translation_xyz_m"]) + error = float(payload["reprojection_error_px"]) + if (len(q) != 4 or len(t) != 3 or not np.all(np.isfinite((*q, *t, error))) + or not np.isclose(np.linalg.norm(q), 1., atol=1e-5) or error < 0): + raise ValueError("invalid pose") + return SquareTagPose(q, t, error) + except (KeyError, TypeError, ValueError) as exc: + raise ValueError("image_motion_provenance_pose_invalid") from exc + + +def _same_pose(actual, expected): + return (float((Rotation.from_quat(actual.quaternion_xyzw).inv() + * Rotation.from_quat(expected.quaternion_xyzw)).magnitude()) <= 1e-6 + and np.linalg.norm(np.subtract(actual.translation_xyz_m, expected.translation_xyz_m)) <= 1e-8 + and abs(actual.reprojection_error_px - expected.reprojection_error_px) <= 1e-6) + + +def replay_image_models(frame): + """Recompute every constrained role from the original frame, without fitting.""" + from .tag_pose.production_image_motion import select_image_motion_frame + + if (frame.get("camera_matrix_source") != "CameraInfo.P[:3,:3]" + or frame.get("input_is_rectified") is not True): + raise ValueError("image_motion_provenance_projection_unverified") + models, selected = {}, {} + for role, item in frame["roles"].items(): + if not has_image_model(item): + continue + if item.get("pose_source") != IMAGE_POSE_SOURCE: + raise ValueError("image_motion_provenance_pose_source_changed") + identity = item.get("image_model_sha256") + model = frozen_image_model(item) + models[identity] = model + if role not in {geometry.relation.child_role for geometry in model.geometry}: + raise ValueError("image_motion_provenance_role_not_in_model") + for identity, model in models.items(): + if (max(model.source_stamps_ns) >= frame["image_stamp_ns"] + or not np.array_equal(np.asarray(frame["camera_matrix"]), model.camera_matrix)): + raise ValueError("image_motion_provenance_model_not_frozen_before_image") + children = {geometry.relation.child_role for geometry in model.geometry} + roles, observations = [], [] + for role, size in model.tag_sizes: + item = frame["roles"].get(role) + if item is None or float(item.get("tag_size_m", -1)) != size: + raise ValueError("image_motion_provenance_role_or_size_changed") + if role in children: + if (item.get("pose_source") != IMAGE_POSE_SOURCE + or item.get("image_model_sha256") != identity): + raise ValueError("image_motion_provenance_chain_model_changed") + candidates = tuple(_pose(pose) for pose in item.get("reprojection_valid_candidates", ())) + if not candidates: + raise ValueError("image_motion_provenance_raw_candidates_missing") + else: + # A root may itself be an earlier frozen moving parent. Its + # current selected pose is used, never its previous image. + diagnostics = item.get("candidate_diagnostics", {}) + if (item.get("pose_source") not in {"verified_fixed_reference", IMAGE_POSE_SOURCE} + or diagnostics.get("branch_frozen") is not True + or diagnostics.get("branch_status") != "tracking" + or diagnostics.get("observation_stamp_ns") != frame["image_stamp_ns"]): + raise ValueError("image_motion_provenance_root_not_frozen_in_current_image") + candidates = (_pose(item["selected"]),) + points = item.get("corners_xy") + if points is not None: + array = np.asarray(points, dtype=float) + if array.shape != (4, 2) or not np.all(np.isfinite(array)): + raise ValueError("image_motion_provenance_corners_invalid") + observations.append(ImageRoleObservation(role, size, tuple(map(tuple, array)))) + elif role in children: + raise ValueError("image_motion_provenance_corners_missing") + roles.append(RoleCandidates(role, candidates, role not in children)) + result = select_image_motion_frame(model, ImageMotionFrame( + MotionEvidenceFrame(frame["image_stamp_ns"], tuple(roles)), + tuple(map(tuple, frame["camera_matrix"])), tuple(observations))) + if not result.resolved: + raise ValueError(f"image_motion_provenance_frame_unresolved:{result.reason}") + by_role = {item.role: item.pose for item in result.selections} + for role in children: + if role not in by_role or not _same_pose(_pose(frame["roles"][role]["selected"]), by_role[role]): + raise ValueError(f"image_motion_provenance_selected_pose_changed:{role}") + selected[role] = by_role[role] + return selected + + +def validate_image_sample(row, frame, replayed): + """Bind sample pose, corners, model identity and relative geometry to a frame.""" + if (row.get("view") != frame.get("view") or row.get("image_stamp_ns") != frame.get("image_stamp_ns") + or any(row.get(key) != frame.get(key) for key in ("task_name", "cycle", "direction", "attempt"))): + raise ValueError("image_motion_provenance_sample_identity_changed") + evidence = row.get("pnp_observation_evidence", {}) + if set(evidence) != {"parent", "child"}: + raise ValueError("image_motion_provenance_sample_roles_missing") + for item in evidence.values(): + role = item.get("tag_role") + data = frame["roles"].get(role) + if data is None: + raise ValueError("image_motion_provenance_sample_role_changed") + if not _same_pose(_pose(item["selected_pose"]), _pose(data["selected"])): + raise ValueError("image_motion_provenance_sample_pose_changed") + if has_image_model(item): + if (item.get("pose_source") != IMAGE_POSE_SOURCE or role not in replayed + or item.get("image_model_sha256") != data.get("image_model_sha256") + or image_model_sha256(item["frozen_image_model"]) != data.get("image_model_sha256") + or not np.array_equal(item.get("corners_xy"), data.get("corners_xy"))): + raise ValueError("image_motion_provenance_sample_model_or_corners_changed") + elif has_image_model(data): + raise ValueError("image_motion_provenance_sample_solver_marker_removed") + parent = _pose(evidence["parent"]["selected_pose"]) + child = _pose(evidence["child"]["selected_pose"]) + rp, rc = Rotation.from_quat(parent.quaternion_xyzw), Rotation.from_quat(child.quaternion_xyzw) + relative = Rotation.from_quat(row["relative_quaternion_xyzw"]) + translation = rp.inv().apply(np.subtract(child.translation_xyz_m, parent.translation_xyz_m)) + if ((relative.inv()*rp.inv()*rc).magnitude() > 1e-7 + or not np.allclose(translation, row["relative_translation_xyz_m"], atol=1e-8, rtol=0)): + raise ValueError("image_motion_provenance_relative_pose_changed") + common_parent, common_child = row["parent_pose_common"], row["child_pose_common"] + rp = Rotation.from_quat(common_parent["quaternion_xyzw"]) + rc = Rotation.from_quat(common_child["quaternion_xyzw"]) + common_translation = rp.inv().apply(np.subtract(common_child["translation_xyz_m"], + common_parent["translation_xyz_m"])) + if ((relative.inv()*rp.inv()*rc).magnitude() > 1e-7 + or not np.allclose(translation, common_translation, atol=1e-8, rtol=0)): + raise ValueError("image_motion_provenance_common_pose_changed") diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/pnp.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/pnp.py index 6986ab3..33c4270 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/pnp.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/pnp.py @@ -16,3 +16,4 @@ from .tag_pose.rigid_group import SquareTagGroupPoseTracker as SquareTagGroupPos from .tag_pose.parameters import PoseTrackingParameters as PoseTrackingParameters from .tag_pose.parameters import DEFAULT_POSE_TRACKING_PARAMETERS as DEFAULT_POSE_TRACKING_PARAMETERS from .tag_pose.parameters import DEFAULT_REPROJECTION_TIE_PX as DEFAULT_REPROJECTION_TIE_PX +from .tag_pose.parameters import POSE_TRACKING_POLICY_VERSION as POSE_TRACKING_POLICY_VERSION diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_border.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_border.py new file mode 100644 index 0000000..a197fc5 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_border.py @@ -0,0 +1,44 @@ +"""Independent pixel support for a decoded square's four outer edges. + +A partially covered AprilTag can still decode without a bit error, while its +quad follows the occluder. Such a quad is not evidence of reference motion. +This check uses the current detected quad, never the locked reference: a fully +visible translated/rotated marker remains eligible for the movement guard. +""" + +import cv2 +import numpy as np + + +def inspect_tag_border(gray, corners): + points = np.asarray(corners, dtype=np.float32) + if (gray.ndim != 2 or points.shape != (4, 2) or not np.all(np.isfinite(points))): + return {"passed": False, "reason": "border_image_or_quad_invalid"} + edge = float(np.min(np.linalg.norm(points-np.roll(points, 1, axis=0), axis=1))) + if edge < 12: + return {"passed": False, "reason": "border_image_underresolved"} + # Sample opposite sides of the printed black/white boundary, including + # near-corner portions. A wider band would miss curved occluder edges. + inset = max(.025, 1.25/edge) + u = np.linspace(.05, .95, 25, dtype=np.float32) + def bands(offset): + return np.asarray([np.c_[u, np.full(25, offset)], + np.c_[np.full(25, 1-offset), u], np.c_[u, np.full(25, 1-offset)], + np.c_[np.full(25, offset), u]], dtype=np.float32) + transform = cv2.getPerspectiveTransform(np.float32([[0, 0], [1, 0], [1, 1], [0, 1]]), points) + def sample(normalized): + xy = cv2.perspectiveTransform(normalized.reshape(-1, 1, 2), transform).reshape(4, 25, 2) + if (np.min(xy) < 1 or np.max(xy[:, :, 0]) > gray.shape[1]-2 + or np.max(xy[:, :, 1]) > gray.shape[0]-2): + return None + return cv2.remap(gray, xy[:, :, 0], xy[:, :, 1], cv2.INTER_LINEAR).astype(float) + inside, outside = sample(bands(inset)), sample(bands(-inset)) + if inside is None or outside is None: + return {"passed": False, "reason": "border_image_out_of_bounds"} + black, white = float(np.percentile(inside, 25)), float(np.percentile(outside, 75)) + threshold = (black+white)/2 + support = np.mean((inside < threshold) & (outside > threshold), axis=1) + passed = white-black >= 30 and bool(np.all(support >= .9)) + return {"passed": passed, "reason": "" if passed else "tag_border_incomplete", + "policy": "current_quad_border_v1", "edge_support": support.tolist(), + "contrast_gray": white-black, "minimum_edge_support": .9, "minimum_contrast_gray": 30} diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/branch_confirmation.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/branch_confirmation.py new file mode 100644 index 0000000..0e44e93 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/branch_confirmation.py @@ -0,0 +1,299 @@ +"""Image-evidence confirmation and frozen-reference branch consistency. + +Confirmation is repeatable image evidence, not a claim of physical truth. +After a reference is frozen, conflicting evidence must invalidate capture; +it cannot authorize a different planar solution for the same reference. +""" + +from dataclasses import dataclass +import math +from typing import Sequence + +import numpy as np + +from .ippe import rotation_distance_rad +from .fixed_reference import FixedTagReference +from .parameters import BRANCH_TRANSLATION_EQUIVALENCE_M, PoseTrackingParameters +from .types import SquareTagPose + + +@dataclass +class PendingBranch: + pose: SquareTagPose + started_ns: int + count: int = 1 + + +@dataclass +class BranchState: + observed_ns: int = 0 + pose: SquareTagPose | None = None + revision: int = 0 + pending: PendingBranch | None = None + frozen: bool = False + recovery_required: bool = False + conflict: bool = False + status: str = "initializing" + reason: str = "pose_branch_unconfirmed" + motion_evidence_id: str | None = None + + +class BranchConfirmation: + """Mutable metadata, accessed only under the owning tracker's short lock.""" + + def __init__(self, parameters: PoseTrackingParameters): + self.parameters = parameters + count = parameters.branch_confirmation_frames + if isinstance(count, bool) or not isinstance(count, int) or count < 2: + raise ValueError("branch_confirmation_frames must be an integer >= 2") + for name in ("branch_confirmation_minimum_seconds", "branch_equivalence_rad"): + value = getattr(parameters, name) + if not math.isfinite(value) or value <= 0: + raise ValueError(f"{name} must be finite and positive") + if parameters.branch_equivalence_rad > math.radians(1.0): + raise ValueError("branch_equivalence_rad must not exceed 1 degree") + self.states: dict[str, BranchState] = {} + self.corrections: dict[str, int] = {} + + def reset(self): + self.states.clear() + self.corrections.clear() + + def distance(self, first: SquareTagPose, second: SquareTagPose) -> tuple[float, float]: + return ( + rotation_distance_rad(first.quaternion_xyzw, second.quaternion_xyzw), + float(np.linalg.norm(np.asarray(first.translation_xyz_m) - second.translation_xyz_m)), + ) + + def continuous(self, first: SquareTagPose, second: SquareTagPose) -> bool: + angle, translation = self.distance(first, second) + return (angle <= self.parameters.maximum_pose_jump_rad + and translation <= self.parameters.maximum_translation_jump_m) + + def equivalent(self, first: SquareTagPose, second: SquareTagPose) -> bool: + angle, translation = self.distance(first, second) + # A 4 cm image acceptance limit is not equivalence of two solutions. + return (angle <= self.parameters.branch_equivalence_rad + and translation <= BRANCH_TRANSLATION_EQUIVALENCE_M) + + def nearest(self, reference: SquareTagPose, candidates: Sequence[SquareTagPose]) -> SquareTagPose: + def cost(candidate): + angle, translation = self.distance(reference, candidate) + return (angle / self.parameters.maximum_pose_jump_rad + + translation / self.parameters.maximum_translation_jump_m) + return min(candidates, key=cost) + + @staticmethod + def reject(state: BranchState, reason: str, status: str) -> tuple[None, str]: + state.status, state.reason = status, reason + return None, reason + + @staticmethod + def accept(state: BranchState, selected: SquareTagPose) -> tuple[SquareTagPose, str]: + state.pose = selected + state.pending = None + state.status, state.reason = "tracking", "" + return selected, "" + + def confirmed(self, state: BranchState, selected: SquareTagPose, stamp: int, + candidates: Sequence[SquareTagPose]) -> bool: + pending = state.pending + if (pending is None or not self.continuous(pending.pose, selected) + or not self.equivalent(self.nearest(pending.pose, candidates), selected)): + state.pending = PendingBranch(selected, stamp) + return False + pending.pose = selected + pending.count += 1 + return (pending.count >= self.parameters.branch_confirmation_frames + and stamp - pending.started_ns >= round( + self.parameters.branch_confirmation_minimum_seconds * 1e9)) + + def _observe(self, role: str, stamp: int) -> tuple[BranchState | None, bool]: + state = self.states.setdefault(role, BranchState()) + if stamp <= state.observed_ns: + return None, False + gap = state.observed_ns > 0 and stamp - state.observed_ns > round( + self.parameters.reset_after_seconds * 1e9) + state.observed_ns = stamp # Rejections are observations, not a lost Tag. + return state, gap + + def update_fixed(self, role: str, reference: FixedTagReference, + checked_pose: SquareTagPose | None, stamp: int) -> tuple[SquareTagPose | None, str]: + """Validate stationarity in the image; never track a new base pose. + + The fixed pose was confirmed and frozen before this call. IPPE image + ranking alone cannot contradict the explicit stationary constraint. + A failed image invalidates observations using this reference, not the + frozen pose itself. Actual reference movement is monitored separately; + prolonged missing data is handled by the task's normal quality gates. + """ + state, gap = self._observe(role, stamp) + if state is None: + return None, "pose_observation_not_new" + if (type(reference.branch_revision) is not int or not state.frozen + or state.revision != reference.branch_revision): + return self.reject(state, "pose_branch_reference_invalid", "unavailable") + if state.conflict: + return self.reject(state, "pose_branch_conflict", "frozen_conflict") + if gap: + state.pending = None + state.recovery_required = True + if checked_pose is None: + state.pending = None + state.recovery_required = True + return self.reject(state, "fixed_reference_pose_unverified", "fixed_reference_unavailable") + if state.recovery_required: + if state.status != "recovering": + state.pending = None + if not self.confirmed(state, reference.pose, stamp, (reference.pose,)): + return self.reject(state, "pose_branch_unconfirmed", "recovering") + state.recovery_required = False + return self.accept(state, checked_pose) + + def observe_motion(self, role: str, candidates: Sequence[SquareTagPose], + stamp: int) -> tuple[None, str]: + """Register a frame whose branch will be selected jointly, outside locks. + + Recording candidates is not confirmation. In particular, repeated image + winners cannot change a motion-authorized identity while the geometric + selector is evaluating this frame. + """ + state, gap = self._observe(role, stamp) + if state is None: + return None, "pose_observation_not_new" + if state.conflict: + return self.reject(state, "pose_branch_conflict", "frozen_conflict") + if gap: + state.pending = None + if state.frozen or state.motion_evidence_id is not None: + state.recovery_required = True + else: + state.pose = None + if not candidates: + state.pending = None + return self.reject(state, "no_pose_within_reprojection_or_tilt_limit", "unavailable") + # Preserve recovery confirmation between frames; merely receiving a + # frame must not clear PendingBranch before the joint selection commits. + state.reason = "pose_motion_evidence_required" + if not state.recovery_required: + state.status = "motion_pending" + return None, state.reason + + def select_motion(self, state: BranchState, selected: SquareTagPose, + evidence_id: str, *, establish: bool) -> tuple[SquareTagPose | None, str]: + """Apply an externally verified geometric selection to one metadata copy. + + The tracker checks image membership and atomically commits all copies. + This method never fits a model or changes an existing evidence identity. + """ + if state.conflict: + return self.reject(state, "pose_branch_conflict", "frozen_conflict") + if state.motion_evidence_id is None: + if state.frozen or not establish: + return self.reject(state, "pose_motion_evidence_required", "motion_unconfirmed") + state.motion_evidence_id = evidence_id + state.revision += 1 + state.recovery_required = False + return self.accept(state, selected) + if evidence_id != state.motion_evidence_id: + return self.reject(state, "pose_motion_evidence_changed", "motion_unconfirmed") + if state.pose is None: + return self.reject(state, "pose_branch_reference_invalid", "unavailable") + if state.recovery_required: + if not self.equivalent(state.pose, selected): + state.pending = None + return self.reject(state, "pose_branch_recovery_required", "recovery_required") + if not self.confirmed(state, selected, state.observed_ns, (selected,)): + return self.reject(state, "pose_branch_unconfirmed", "recovering") + state.recovery_required = False + elif not self.continuous(state.pose, selected): + state.pending = None + return self.reject(state, "pose_motion_branch_discontinuous", "motion_unconfirmed") + return self.accept(state, selected) + + def update(self, role: str, candidates: Sequence[SquareTagPose], stamp: int, + tie: float) -> tuple[SquareTagPose | None, str]: + state, gap = self._observe(role, stamp) + if state is None: + return None, "pose_observation_not_new" + if state.conflict: + return self.reject(state, "pose_branch_conflict", "frozen_conflict") + if gap: + state.pending = None + if state.frozen: + state.recovery_required = True + else: + state.pose = None + if not candidates: + state.pending = None + return self.reject(state, "no_pose_within_reprojection_or_tilt_limit", "unavailable") + candidates = sorted(candidates, key=lambda c: c.reprojection_error_px) + best = candidates[0] + distinct = [c for c in candidates[1:] if not self.equivalent(best, c)] + decisive = not distinct or all(c.reprojection_error_px > best.reprojection_error_px + tie for c in distinct) + if state.recovery_required: + # Without a temporal path, only a return to the last known pose + # supplies a conservative reference for independent tracking. + near = self.nearest(state.pose, candidates) + if (not self.equivalent(state.pose, near) + or best.reprojection_error_px + tie < near.reprojection_error_px): + state.pending = None + return self.reject(state, "pose_branch_recovery_required", "recovery_required") + if not self.confirmed(state, near, stamp, candidates): + return self.reject(state, "pose_branch_unconfirmed", "recovering") + state.recovery_required = False + return self.accept(state, near) + if state.pose is None: + if not decisive: + state.pending = None + return self.reject(state, "pose_branch_unconfirmed", "initializing") + if not self.confirmed(state, best, stamp, candidates): + return self.reject(state, "pose_branch_unconfirmed", "initializing") + state.revision += 1 + return self.accept(state, best) + + incumbent = self.nearest(state.pose, candidates) + continuous = self.continuous(state.pose, incumbent) + advantage = best.reprojection_error_px + tie < incumbent.reprojection_error_px + if continuous and (not advantage or self.equivalent(incumbent, best)): + return self.accept(state, incumbent) + + # Propagate the incumbent's identity while evidence is rejected. This + # does not emit a sample, and cannot age into a five-second reset. + if continuous: + state.pose = incumbent + if state.frozen and continuous: + # Both image-valid branches still exist and the incumbent has a + # continuous path. A different image-error winner is ambiguity, + # not evidence that the reference moved. Quarantine the image; + # retain this path so later observations can recover without a + # reset, branch switch, or new joint zero. + state.pending = None + return self.reject(state, "pose_branch_ambiguous", "frozen_ambiguous") + if not decisive: + state.pending = None + return self.reject(state, "pose_jump", "unconfirmed") + if not self.confirmed(state, best, stamp, candidates): + return self.reject(state, "pose_branch_unconfirmed", "conflict_pending" if state.frozen else "correcting") + if state.frozen: + state.conflict = True + return self.reject(state, "pose_branch_conflict", "frozen_conflict") + state.revision += 1 + self.corrections[role] = self.corrections.get(role, 0) + 1 + self.accept(state, best) + # A corrected tracker can establish NEW references, but the transition + # itself must never be interpreted as a measured joint displacement. + return self.reject(state, "pose_branch_corrected", "corrected") + + def diagnostics(self, role: str) -> dict[str, int | str | bool]: + state = self.states[role] + return { + "observation_stamp_ns": state.observed_ns, + "branch_revision": state.revision, + "branch_status": state.status, + "branch_reason": state.reason, + "branch_confirmation_count": 0 if state.pending is None else state.pending.count, + "branch_frozen": state.frozen, + "motion_evidence_verified": state.motion_evidence_id is not None, + "motion_evidence_id": state.motion_evidence_id or "", + } diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/cad_hinge.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/cad_hinge.py new file mode 100644 index 0000000..cff1135 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/cad_hinge.py @@ -0,0 +1,111 @@ +"""Image hinges constrained by geometry retained in the output URDF. + +Only adjacent parallel axes are represented here. Their signed parallelism +and perpendicular separation are invariant under joint-zero corrections. +Camera poses, Tag installations and image angles remain measured quantities. +""" + +from dataclasses import dataclass +import math + +import numpy as np +from scipy.spatial.transform import Rotation + +from .motion_image_solver import ImageHingeBundle + + +@dataclass(frozen=True) +class ParallelAxisGeometry: + parent_joint: str + child_joint: str + axis_sign: int + distance_m: float + + def __post_init__(self): + if (not self.parent_joint or not self.child_joint or self.parent_joint == self.child_joint + or self.axis_sign not in {-1, 1} + or not math.isfinite(self.distance_m) or self.distance_m <= 1e-6): + raise ValueError("invalid_parallel_axis_geometry") + + +class CadImageHingeBundle(ImageHingeBundle): + """Eliminate three variables per constrained hinge, without penalty weights. + + The child axis is the parent axis expressed in the parent Tag frame. Its + pivot is on the known-radius circle about the parent axis; one phase is + estimated. This keeps the model's axial-pivot gauge fixed and leaves no + redundant optimizer columns. The usual full Jacobian includes image-angle + uncertainty and dependencies on the parent's installation parameters. + """ + + def __init__(self, frames, relations, poses, *, constraints, source_urdf_sha256): + super().__init__(frames, relations, poses) + self.constraints = tuple(constraints) + self.source_urdf_sha256 = source_urdf_sha256 + indices = {relation.joint: i for i, relation in enumerate(relations)} + self._constraints = {} + for constraint in self.constraints: + parent = indices.get(constraint.parent_joint) + child = indices.get(constraint.child_joint) + if (parent is None or child is None or parent >= child + or relations[child].parent_role != relations[parent].child_role + or child in self._constraints): + raise ValueError("parallel_axis_constraint_outside_observed_chain") + self._constraints[child] = (parent, constraint) + self._full_initial = self.initial.copy() + removed = {10*i + local for i in self._constraints for local in (0, 1, 6)} + self._kept = np.asarray([i for i in range(len(self.initial)) if i not in removed]) + self._columns = {original: column for column, original in enumerate(self._kept)} + for index, (parent, _) in sorted(self._constraints.items()): + parameters = self._full_initial[self._kept] + _, _, _, parent_mount = self.geometry_values(parameters, parent) + _, _, old_pivot, _ = super().geometry_values(self._full_initial, index) + tangent = self.axis_basis(parameters, index) + vector = parent_mount + old_pivot + self._full_initial[10*index + 5] = math.atan2(tangent[:, 1] @ vector, tangent[:, 0] @ vector) + self.scale[10*index + 5] = 1. + self.initial = self._full_initial[self._kept] + self.scale = self.scale[self._kept] + + @property + def static_parameter_count(self): + return 10 * self.joints - 3 * len(self._constraints) + + def geometry_parameter_indices(self, index): + return tuple(self._columns[original] for original in range(10*index, 10*(index+1)) + if original in self._columns) + + def geometry_dependency_indices(self, index): + result = self.geometry_parameter_indices(index) + if index in self._constraints: + result += self.geometry_dependency_indices(self._constraints[index][0]) + return result + + def _expand(self, parameters): + full = self._full_initial.copy() + full[self._kept] = parameters + return full + + def axis_basis(self, parameters, index): + if index not in self._constraints: + values = self._expand(parameters)[10*index:10*index+2] + return Rotation.from_rotvec(self.bases[index] @ values).as_matrix() @ self.bases[index] + parent, constraint = self._constraints[index] + _, reference, _, _ = self.geometry_values(parameters, parent) + basis = reference.inv().as_matrix() @ self.axis_basis(parameters, parent) + basis[:, 1] *= constraint.axis_sign + return basis + + def geometry_values(self, parameters, index): + full = self._expand(parameters) + if index not in self._constraints: + return super().geometry_values(full, index) + parent, constraint = self._constraints[index] + parent_axis, parent_reference, _, parent_mount = self.geometry_values(parameters, parent) + axis = constraint.axis_sign * parent_reference.inv().apply(parent_axis) + values = full[10*index:10*(index+1)] + reference = Rotation.from_rotvec(values[2:5]) * self.references[index] + phase = values[5] + pivot = constraint.distance_m * (self.axis_basis(parameters, index) @ [math.cos(phase), math.sin(phase)]) + pivot -= parent_mount - axis * (axis @ parent_mount) + return axis, reference, pivot, values[7:10] diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/cad_image_model.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/cad_image_model.py new file mode 100644 index 0000000..746ab90 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/cad_image_model.py @@ -0,0 +1,44 @@ +"""A frozen image model with explicit, source-bound URDF constraints.""" + +from dataclasses import dataclass +from typing import ClassVar + +import numpy as np +from scipy.spatial.transform import Rotation + +from .cad_hinge import ParallelAxisGeometry +from .image_motion_model import ImageMotionModel + +CAD_IMAGE_MOTION_MODEL_POLICY = "image_hinge_v2_source_parallel_geometry" + + +@dataclass(frozen=True) +class CadImageMotionModel(ImageMotionModel): + POLICY: ClassVar[str] = CAD_IMAGE_MOTION_MODEL_POLICY + policy: str = CAD_IMAGE_MOTION_MODEL_POLICY + constraints: tuple[ParallelAxisGeometry, ...] = () + source_urdf_sha256: str = "" + + def __post_init__(self): + super().__post_init__() + if (not isinstance(self.constraints, tuple) or not self.constraints + or len(self.source_urdf_sha256) != 64 + or any(c not in "0123456789abcdef" for c in self.source_urdf_sha256)): + raise ValueError("image_model_source_geometry_missing") + geometry = {item.relation.joint: item for item in self.geometry} + children = set() + for constraint in self.constraints: + if not isinstance(constraint, ParallelAxisGeometry): + raise ValueError("image_model_source_geometry_invalid") + parent, child = geometry.get(constraint.parent_joint), geometry.get(constraint.child_joint) + if (parent is None or child is None or parent.relation.child_role != child.relation.parent_role + or constraint.child_joint in children): + raise ValueError("image_model_source_geometry_graph_changed") + children.add(constraint.child_joint) + axis = Rotation.from_quat(parent.reference_quaternion_xyzw).inv().apply(parent.axis_parent_xyz) + if not np.allclose(child.axis_parent_xyz, constraint.axis_sign * axis, atol=1e-8, rtol=0): + raise ValueError("image_model_source_axis_changed") + vector = np.asarray(parent.mount_child_xyz_m) + child.pivot_parent_xyz_m + distance = np.linalg.norm(vector - axis * (axis @ vector)) + if not np.isclose(distance, constraint.distance_m, atol=1e-8, rtol=0): + raise ValueError("image_model_source_axis_distance_changed") diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/fixed_reference.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/fixed_reference.py new file mode 100644 index 0000000..4ecc8f0 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/fixed_reference.py @@ -0,0 +1,11 @@ +"""An immutable camera-local pose for a declared stationary reference Tag.""" + +from dataclasses import dataclass + +from .types import SquareTagPose + + +@dataclass(frozen=True) +class FixedTagReference: + pose: SquareTagPose + branch_revision: int diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_model_selection.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_model_selection.py new file mode 100644 index 0000000..fb2a29b --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_model_selection.py @@ -0,0 +1,90 @@ +"""Training-only model choice and held-out, motion-stratified comparisons. + +All comparisons use squared image loss, matching the geometry optimizer. +Holm step-down controls the nominal familywise level for the complete set of +comparisons. These are conditional image tests, not a calibration certificate. +""" +import math + +import numpy as np +from scipy.stats import ttest_1samp, wilcoxon + +from .motion_evidence import MotionEvidenceParameters, _equivalent + +IMAGE_MODEL_SELECTION_POLICY = "training_squared_loss_holm_v1" + + +def training_candidate_index(hypotheses): + """Never replace a training-selected model because of validation scores.""" + eligible = [i for i, hypothesis in enumerate(hypotheses) if hypothesis.training_accepted] + if not eligible: + return None + return min(eligible, key=lambda i: float(np.mean( + [quality.rms_px ** 2 for quality in hypotheses[i].training_quality]))) + + +def equivalent_frame_selections(first, second): + if not first.resolved or not second.resolved: + return False + left = {selection.role: selection.pose for selection in first.selections} + right = {selection.role: selection.pose for selection in second.selections} + return bool(left and len(left) == len(first.selections) and len(right) == len(second.selections) + and left.keys() == right.keys() + and all(_equivalent(pose, right[role]) for role, pose in left.items())) + + +def compare_image_losses(best, alternative, angles, tolerance): + """Compare paired squared errors with the existing pixel-resolution margin. + + One observation per angular stratum prevents repeated images inflating + evidence. Medians and both mean/rank tests prevent an isolated large error + from deciding a branch. The candidate is selected on training images only. + """ + angles = np.asarray(angles, dtype=float) + best, alternative = np.asarray(best, dtype=float), np.asarray(alternative, dtype=float) + if (angles.ndim != 2 or not angles.shape[1] or best.ndim != 1 + or len(best) != len(angles) or alternative.shape != best.shape or not len(best) + or not np.all(np.isfinite(angles)) or not np.all(np.isfinite(best)) + or not np.all(np.isfinite(alternative)) or np.any(best < 0) or np.any(alternative < 0) + or not math.isfinite(tolerance) or tolerance < 0): + return 1. + moving = int(np.argmax(np.ptp(angles, axis=0))) + values = angles[:, moving] + span = float(np.ptp(values)) + parameters = MotionEvidenceParameters() + count = min(24, int(np.floor(span / parameters.angular_bin_rad))) + if count < parameters.minimum_motion_bins: + return 1. + indices = np.minimum(count - 1, np.floor((values - min(values)) / span * count).astype(int)) + losses = alternative ** 2 - (best + tolerance) ** 2 + differences = np.asarray([float(np.median(losses[indices == index])) + for index in range(count) if np.any(indices == index)]) + if (len(differences) < parameters.minimum_motion_bins + or not np.all(np.isfinite(differences)) or np.mean(differences) <= 0): + return 1. + if np.ptp(differences) < 1e-12: + return 2. ** -len(differences) if differences[0] > 0 else 1. + mean_probability = float(ttest_1samp(differences, 0., alternative="greater").pvalue) + rank_probability = float(wilcoxon(differences, alternative="greater", zero_method="wilcox").pvalue) + if not math.isfinite(mean_probability) or not math.isfinite(rank_probability): + return 1. + return max(mean_probability, rank_probability) + + +def holm_adjusted_probabilities(comparisons): + """Order-independent Holm probabilities, returned in input identity order. + + A geometric equivalence check may supply zero. It is still included in the + family; no candidate is omitted merely because of its initialization label. + Nonfinite or invalid probabilities fail closed. + """ + comparisons = tuple(comparisons) + if len({index for index, _ in comparisons}) != len(comparisons): + raise ValueError("duplicate_image_model_comparison") + values = [(index, probability if math.isfinite(probability) and 0 <= probability <= 1 else 1.) + for index, probability in comparisons] + adjusted, previous = {}, 0. + for rank, (index, probability) in enumerate(sorted(values, key=lambda item: item[1])): + previous = max(previous, min(1., probability * (len(values) - rank))) + adjusted[index] = previous + return tuple((index, adjusted[index]) for index, _ in comparisons) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_motion_commit.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_motion_commit.py new file mode 100644 index 0000000..75498a8 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_motion_commit.py @@ -0,0 +1,157 @@ +"""Bind one image solve to versioned tracker inputs, then commit it once. + +The numerical solve runs without tracker locks. Immutable tracker-owned pixels +and frozen root snapshots prevent callers substituting observations; a weak +identity registry prevents copied or manually assembled decisions being used as +commit authority. All tracker locks are acquired only for metadata checks. +""" +from __future__ import annotations + +from contextlib import ExitStack +from collections.abc import Mapping, Sequence +from dataclasses import asdict, dataclass +import hashlib +import json +from threading import RLock +from weakref import WeakValueDictionary +from typing import TYPE_CHECKING + +from .image_motion_model import ImageMotionModel +from .image_motion_projection import select_image_motion_frame +from .image_projection_worker import ImageProjectionWorker +from .motion_image_types import ImageMotionFrame, ImageRoleQuality + +if TYPE_CHECKING: + from .tracking import MotionFrameCommit, MotionPoseSelection, MotionTrackerSnapshot + + +@dataclass(frozen=True) +class VerifiedImageMotionDecision: + accepted: bool + reason: str + selections: tuple[MotionPoseSelection, ...] = () + root_snapshots: tuple[MotionTrackerSnapshot, ...] = () + model: ImageMotionModel | None = None + model_sha256: str = "" + quality: tuple[ImageRoleQuality, ...] = () + + +_verified = WeakValueDictionary() +_registry_lock = RLock() + + +def _root_current(snapshot): + tracker, role = snapshot.tracker, snapshot.role + state = tracker._branches.states.get(role) + diagnostics = tracker.last_candidate_diagnostics_by_role.get(role, {}) + evidence_verified = (diagnostics.get("motion_selection_committed") is True and snapshot.evidence_id is not None + if snapshot.motion_managed else diagnostics.get("fixed_reference_verified") is True) + return (tracker._epoch == snapshot.epoch and state is not None + and state.observed_ns == snapshot.stamp_ns and state.revision == snapshot.revision + and state.frozen and snapshot.frozen and state.status == "tracking" + and state.pose == snapshot.previous_pose and evidence_verified + and not state.conflict and not state.recovery_required + and tracker._image_observations_by_role.get(role) is snapshot.image_observation) + + +def _inputs_match(snapshots, roots, model, frame): + items = {r.role: r for r in frame.evidence.roles} + observations = {o.role: o for o in frame.observations} + if len(items) != len(frame.evidence.roles) or len(observations) != len(frame.observations): + return False + for snapshot in (*snapshots, *roots): + image = snapshot.image_observation + item, observation = items.get(snapshot.role), observations.get(snapshot.role) + if (image is None or item is None or observation is None + or snapshot.stamp_ns != frame.evidence.stamp_ns or image.stamp_ns != snapshot.stamp_ns + or image.camera_matrix != model.camera_matrix or image.camera_matrix != frame.camera_matrix + or image.tag_size_m != observation.tag_size_m or image.corners_xy != observation.corners_xy): + return False + if snapshot in roots: + if not item.frozen or item.candidates != (snapshot.previous_pose,): + return False + elif item.frozen or item.candidates != snapshot.candidates: + return False + return True + + +def verify_image_motion_frame(snapshots: Sequence[MotionTrackerSnapshot], model: ImageMotionModel, + frame: ImageMotionFrame, evidence_ids: Mapping[str, str], *, + root_snapshots: Sequence[MotionTrackerSnapshot] = (), + projection_worker=None) -> VerifiedImageMotionDecision: + """Calculate a decision once; never accept caller-proposed output poses.""" + from .tracking import MotionPoseSelection, SquareTagPoseTracker + if projection_worker is not None and type(projection_worker) is not ImageProjectionWorker: + return VerifiedImageMotionDecision(False, "pose_image_motion_worker_invalid") + snapshots, roots = tuple(snapshots), tuple(root_snapshots) + if not isinstance(model, ImageMotionModel) or not snapshots: + return VerifiedImageMotionDecision(False, "pose_image_motion_inputs_invalid") + child_roles = {g.relation.child_role for g in model.geometry} + root_roles = set(dict(model.tag_sizes)) - child_roles + if (set(evidence_ids) != child_roles or {s.role for s in snapshots} != child_roles + or len(snapshots) != len(child_roles) + or any(not isinstance(value, str) or not value for value in evidence_ids.values())): + return VerifiedImageMotionDecision(False, "pose_image_motion_roles_invalid") + if not roots: + collected = [] + for role in sorted(root_roles): + found = [snapshot for tracker in {s.tracker for s in snapshots} + if (snapshot := tracker.motion_snapshot(role, frame.evidence.stamp_ns)) is not None] + if len(found) != 1: + return VerifiedImageMotionDecision(False, "pose_image_motion_root_missing") + collected.append(found[0]) + roots = tuple(collected) + if {s.role for s in roots} != root_roles or len(roots) != len(root_roles): + return VerifiedImageMotionDecision(False, "pose_image_motion_root_missing") + if not _inputs_match(snapshots, roots, model, frame): + return VerifiedImageMotionDecision(False, "pose_image_motion_observation_mismatch") + with ExitStack() as stack: + for tracker in sorted({s.tracker for s in (*snapshots, *roots)}, key=id): + stack.enter_context(tracker._lock) + if not all(SquareTagPoseTracker._motion_snapshot_is_current(s) for s in snapshots) or not all( + _root_current(s) for s in roots): + return VerifiedImageMotionDecision(False, "pose_observation_superseded") + # This is the only expensive operation. Commit below never repeats it. + result = (select_image_motion_frame(model, frame) if projection_worker is None + else ImageProjectionWorker.solve(projection_worker, model, frame)) + if not result.resolved: + return VerifiedImageMotionDecision(False, result.reason, quality=result.quality) + poses = {item.role: item.pose for item in result.selections} + identity = hashlib.sha256(json.dumps(asdict(model), sort_keys=True, separators=(",", ":"), + allow_nan=False).encode()).hexdigest() + selections = tuple(MotionPoseSelection(s, poses[s.role], evidence_ids[s.role]) for s in snapshots) + decision = VerifiedImageMotionDecision(True, "", selections, roots, model, identity, result.quality) + with _registry_lock: + _verified[id(decision)] = decision + return decision + + +def commit_image_motion_frame(decision: VerifiedImageMotionDecision, *, + establish: bool = False) -> MotionFrameCommit: + """Consume a verifier-created decision and atomically update all children.""" + from .tracking import MotionFrameCommit, SquareTagPoseTracker + if not isinstance(decision, VerifiedImageMotionDecision): + return MotionFrameCommit(False, "pose_image_motion_decision_invalid") + if not decision.accepted: + return MotionFrameCommit(False, decision.reason) + with _registry_lock: + if _verified.pop(id(decision), None) is not decision: + return MotionFrameCommit(False, "pose_image_motion_decision_invalid") + snapshots = tuple(item.snapshot for item in decision.selections) + with ExitStack() as stack: + for tracker in sorted({s.tracker for s in (*snapshots, *decision.root_snapshots)}, key=id): + stack.enter_context(tracker._lock) + if not all(SquareTagPoseTracker._motion_snapshot_is_current(s) for s in snapshots) or not all( + _root_current(s) for s in decision.root_snapshots): + return MotionFrameCommit(False, "pose_observation_superseded") + for snapshot in snapshots: + tracker, role = snapshot.tracker, snapshot.role + if tracker._image_observations_by_role.get(role) is not snapshot.image_observation: + return MotionFrameCommit(False, "pose_image_motion_observation_mismatch") + previous = tracker._image_model_sha256_by_role.get(role) + if previous is not None and previous != decision.model_sha256: + return MotionFrameCommit(False, "pose_image_motion_model_changed") + if previous is None and snapshot.evidence_id is not None: + return MotionFrameCommit(False, "pose_motion_solver_changed") + return SquareTagPoseTracker._commit_motion_selections_locked(decision.selections, + establish=establish, image_decision=decision) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_motion_model.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_motion_model.py new file mode 100644 index 0000000..6a68cf3 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_motion_model.py @@ -0,0 +1,136 @@ +"""Frozen image-derived hinge models and their auditable authorization evidence.""" +from __future__ import annotations + +from dataclasses import dataclass +import math +from typing import ClassVar, Mapping + +import numpy as np + +from .motion_evidence import MotionRelation, MotionRoleSelection +from .motion_image_types import ImageHingeGeometry, ImageRoleQuality +from .parameters import DEFAULT_POSE_TRACKING_PARAMETERS + +IMAGE_MOTION_MODEL_POLICY = "image_hinge_v1_pre_zero_frozen" + + +@dataclass(frozen=True) +class ImageMotionModel: + POLICY: ClassVar[str] = IMAGE_MOTION_MODEL_POLICY + geometry: tuple[ImageHingeGeometry, ...] + camera_matrix: tuple[tuple[float, float, float], ...] + tag_sizes: tuple[tuple[str, float], ...] + source_stamps_ns: tuple[int, ...] + training_stamps_ns: tuple[int, ...] + validation_stamps_ns: tuple[int, ...] + branches: tuple[tuple[str, int], ...] + maximum_reprojection_error_px: float = 1.5 + reprojection_tie_px: float = .03 + policy: str = IMAGE_MOTION_MODEL_POLICY + + @property + def relations(self): + return tuple(g.relation for g in self.geometry) + + def __post_init__(self): + from .motion_image_diagnostics import _ordered_relations + if self.policy != self.POLICY: + raise ValueError("image_motion_model_policy_mismatch") + sequences = (self.geometry, self.camera_matrix, self.tag_sizes, self.source_stamps_ns, + self.training_stamps_ns, self.validation_stamps_ns, self.branches) + if any(not isinstance(value, tuple) for value in sequences) or any( + not isinstance(value, tuple) for values in (self.camera_matrix, self.tag_sizes, self.branches) + for value in values): + raise ValueError("image_motion_model_requires_immutable_sequences") + relations = self.relations + if tuple(_ordered_relations(relations)) != relations: + raise ValueError("image_motion_model_graph_not_ordered") + p = DEFAULT_POSE_TRACKING_PARAMETERS + if (self.maximum_reprojection_error_px != p.maximum_reprojection_error_px + or self.reprojection_tie_px != p.reprojection_tie_px): + raise ValueError("image_motion_model_quality_policy_changed") + matrix = np.asarray(self.camera_matrix) + if (matrix.shape != (3, 3) or not np.all(np.isfinite(matrix)) + or matrix[0, 0] <= 0 or matrix[1, 1] <= 0 + or not np.array_equal(matrix[2], [0, 0, 1])): + raise ValueError("image_motion_model_camera_invalid") + roles = {r for g in self.geometry for r in (g.relation.parent_role, g.relation.child_role)} + if (len(dict(self.tag_sizes)) != len(self.tag_sizes) or set(dict(self.tag_sizes)) != roles + or any(not math.isfinite(s) or s <= 0 for _, s in self.tag_sizes)): + raise ValueError("image_motion_model_tag_sizes_invalid") + if len(dict(self.branches)) != len(self.branches) or set(dict(self.branches)) != roles or any( + type(v) is not int or v not in (0, 1) for _, v in self.branches): + raise ValueError("image_motion_model_branches_invalid") + for stamps in (self.source_stamps_ns, self.training_stamps_ns, self.validation_stamps_ns): + if not stamps or any(type(v) is not int or v <= 0 for v in stamps) or any( + b <= a for a, b in zip(stamps, stamps[1:])): + raise ValueError("image_motion_model_sources_invalid") + source, train, validation = map(set, (self.source_stamps_ns, self.training_stamps_ns, + self.validation_stamps_ns)) + if not train.isdisjoint(validation) or not train | validation <= source: + raise ValueError("image_motion_model_source_overlap") + for geometry in self.geometry: + vectors = (geometry.axis_parent_xyz, geometry.reference_quaternion_xyzw, + geometry.pivot_parent_xyz_m, geometry.mount_child_xyz_m) + if any(not isinstance(v, tuple) or len(v) != n for v, n in zip(vectors, (3, 4, 3, 3))) or not all( + math.isfinite(x) for v in vectors for x in v): + raise ValueError("image_motion_model_geometry_invalid") + if not np.isclose(np.linalg.norm(vectors[0]), 1., atol=1e-7) or not np.isclose( + np.linalg.norm(vectors[1]), 1., atol=1e-7): + raise ValueError("image_motion_model_rotation_invalid") + + +@dataclass(frozen=True) +class ImageMotionFrameSelection: + resolved: bool + reason: str + selections: tuple[MotionRoleSelection, ...] = () + quality: tuple[ImageRoleQuality, ...] = () + angles_rad: tuple[tuple[str, float], ...] = () + + +@dataclass(frozen=True) +class ImageMotionHypothesis: + branches: tuple[tuple[str, int], ...] + converged: bool + reason: str + training_quality: tuple[ImageRoleQuality, ...] = () + validation_quality: tuple[ImageRoleQuality, ...] = () + motion_span_rad: tuple[tuple[str, float], ...] = () + axis_uncertainty_95_rad: tuple[tuple[str, float], ...] = () + pivot_uncertainty_95_m: tuple[tuple[str, float], ...] = () + comparison_p_values: tuple[tuple[int, float], ...] = () + training_solver_status: int | None = None + training_solver_evaluations: int = 0 + training_accepted: bool = False + comparison_adjusted_p_values: tuple[tuple[int, float], ...] = () + + +@dataclass(frozen=True) +class ImageMotionResolution: + resolved: bool + reason: str + model: ImageMotionModel | None = None + hypotheses: tuple[ImageMotionHypothesis, ...] = () + selections: tuple[MotionRoleSelection, ...] = () + + +def image_motion_model_from_dict(raw: Mapping) -> ImageMotionModel: + """Read an immutable model; hashes/provenance are enforced by the caller.""" + geometry = tuple(ImageHingeGeometry(MotionRelation(**item["relation"]), + tuple(item["axis_parent_xyz"]), tuple(item["reference_quaternion_xyzw"]), + tuple(item["pivot_parent_xyz_m"]), tuple(item["mount_child_xyz_m"])) for item in raw["geometry"]) + model_type, extra = ImageMotionModel, {} + from .cad_image_model import CAD_IMAGE_MOTION_MODEL_POLICY, CadImageMotionModel + if raw["policy"] == CAD_IMAGE_MOTION_MODEL_POLICY: + from .cad_hinge import ParallelAxisGeometry + model_type = CadImageMotionModel + extra = {"constraints": tuple(ParallelAxisGeometry(**value) for value in raw["constraints"]), + "source_urdf_sha256": raw["source_urdf_sha256"]} + return model_type(geometry=geometry, camera_matrix=tuple(tuple(v) for v in raw["camera_matrix"]), + tag_sizes=tuple((str(r), float(s)) for r, s in raw["tag_sizes"]), + source_stamps_ns=tuple(raw["source_stamps_ns"]), training_stamps_ns=tuple(raw["training_stamps_ns"]), + validation_stamps_ns=tuple(raw["validation_stamps_ns"]), + branches=tuple((str(r), i) for r, i in raw["branches"]), + maximum_reprojection_error_px=raw["maximum_reprojection_error_px"], + reprojection_tie_px=raw["reprojection_tie_px"], policy=raw["policy"], **extra) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_motion_projection.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_motion_projection.py new file mode 100644 index 0000000..a75cbcc --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_motion_projection.py @@ -0,0 +1,131 @@ +"""Current-image angle estimates with immutable, pre-zero hinge geometry.""" +from itertools import product + +import numpy as np +from scipy.optimize import least_squares +from scipy.spatial.transform import Rotation + +from .image_motion_model import ImageMotionFrameSelection +from .ippe import square_object_points +from .motion_evidence import MotionEvidenceParameters, MotionRoleSelection, _eligible, _equivalent +from .motion_image_diagnostics import _quality +from .relative import _relative_pose +from .types import SquareTagPose + + +def project_image_model(model, angles, roots): + transforms = {role: (Rotation.from_quat(pose.quaternion_xyzw), + np.array(pose.translation_xyz_m)) for role, pose in roots.items()} + pixels = [] + positive = True + matrix, sizes = np.array(model.camera_matrix), dict(model.tag_sizes) + for geometry, angle in zip(model.geometry, angles): + relative = Rotation.from_rotvec(np.array(geometry.axis_parent_xyz) * angle) * Rotation.from_quat( + geometry.reference_quaternion_xyzw) + parent_rotation, parent_translation = transforms[geometry.relation.parent_role] + rotation = parent_rotation * relative + translation = parent_translation + parent_rotation.apply(np.array(geometry.pivot_parent_xyz_m) + + relative.apply(geometry.mount_child_xyz_m)) + role = geometry.relation.child_role + transforms[role] = (rotation, translation) + points = rotation.apply(square_object_points(sizes[role])) + translation + positive = positive and bool(np.all(points[:, 2] > 1e-6)) + homogeneous = points @ matrix.T + pixels.append(homogeneous[:, :2] / np.maximum(homogeneous[:, 2:], 1e-6)) + return np.asarray(pixels), transforms, positive + + +def _frame_inputs(model, frame): + if tuple(tuple(v) for v in frame.camera_matrix) != model.camera_matrix: + raise ValueError("image_motion_camera_changed") + observations = {o.role: o for o in frame.observations} + candidates = {r.role: r for r in frame.evidence.roles} + roles, sizes = tuple(dict(model.tag_sizes)), dict(model.tag_sizes) + if (len(observations) != len(frame.observations) or len(candidates) != len(frame.evidence.roles) + or not set(roles) <= observations.keys() or not set(roles) <= candidates.keys()): + raise ValueError("image_motion_frame_roles_missing") + for role in roles: + observation = observations[role] + if observation.tag_size_m != sizes[role] or np.shape(observation.corners_xy) != (4, 2) or not np.all( + np.isfinite(observation.corners_xy)): + raise ValueError("image_motion_frame_corners_invalid") + children = tuple(g.relation.child_role for g in model.geometry) + roots = set(roles) - set(children) + poses = {} + for role in roles: + selected = tuple(p for p in candidates[role].candidates if _eligible(p)) + if not selected or (role in roots and (not candidates[role].frozen or len(selected) != 1)): + raise ValueError("image_motion_current_root_or_candidates_unavailable") + poses[role] = selected + frozen = {role: poses[role][0] for role in roots} + # A frozen root is still verified against this image, never silently moved. + for role, pose in frozen.items(): + xyz = Rotation.from_quat(pose.quaternion_xyzw).apply(square_object_points(sizes[role])) + xyz += pose.translation_xyz_m + if np.any(xyz[:, 2] <= 1e-6): + raise ValueError("image_motion_root_behind_camera") + image = xyz @ np.array(model.camera_matrix).T + delta = image[:, :2] / image[:, 2:] - observations[role].corners_xy + if np.sqrt(np.mean(np.sum(delta ** 2, axis=1))) > model.maximum_reprojection_error_px: + raise ValueError("image_motion_current_root_reprojection_failed") + return poses, frozen, np.asarray([observations[r].corners_xy for r in children]), children + + +def select_image_motion_frame(model, frame): + """Fit only current geometric angles, retaining all candidate initializations. + + No model parameter, baseline or previous pose is changed. Distinct solutions + within the public 0.03 pixel tolerance are rejected rather than ranked. + """ + try: + poses, roots, observed, children = _frame_inputs(model, frame) + roles = tuple(poses) + if np.prod([len(poses[r]) for r in roles]) > 256: + raise ValueError("image_motion_frame_hypothesis_limit") + fits = [] + for values in product(*(poses[role] for role in roles)): + selected = dict(zip(roles, values)) + initial = [] + for geometry in model.geometry: + rotation, _ = _relative_pose(selected[geometry.relation.parent_role], + selected[geometry.relation.child_role]) + initial.append(float((rotation * Rotation.from_quat( + geometry.reference_quaternion_xyzw).inv()).as_rotvec() @ geometry.axis_parent_xyz)) + def residual(angles): + pixels, _, _ = project_image_model(model, angles, roots) + return (pixels - observed).ravel() + fit = least_squares(residual, initial, max_nfev=40, ftol=1e-9, xtol=1e-9, gtol=1e-7) + _, transforms, positive = project_image_model(model, fit.x, roots) + if not fit.success or not positive or not np.all(np.isfinite(fit.fun)): + raise ValueError("image_motion_frame_optimizer_unresolved") + quality = _quality(fit.fun, children) + output = dict(roots) + for role, checked in zip(children, quality): + rotation, translation = transforms[role] + output[role] = SquareTagPose(tuple(rotation.as_quat()), tuple(translation), checked.rms_px) + score = float(np.sqrt(np.mean(np.square(fit.fun.reshape(-1, 2)).sum(axis=1)))) + fits.append((score, output, quality, fit.x, fit.jac, fit.fun)) + if not fits: + raise ValueError("image_motion_frame_reprojection_or_tilt_failed") + fits.sort(key=lambda item: item[0]) + best = fits[0] + for other in fits[1:]: + if other[0] <= best[0] + model.reprojection_tie_px and not all( + _equivalent(best[1][r], other[1][r]) for r in roles): + raise ValueError("image_motion_frame_angles_ambiguous") + jacobian = np.asarray(best[4]) + _, singular, vh = np.linalg.svd(jacobian, full_matrices=False) + if singular[-1] <= singular[0] * np.finfo(float).eps * max(jacobian.shape): + raise ValueError("image_motion_frame_angles_unobservable") + variance = max(model.reprojection_tie_px ** 2, + float(np.sum(best[5] ** 2) / max(1, len(best[5]) - len(best[3])))) + covariance = (vh.T / singular ** 2) @ vh * variance + if np.any(3 * np.sqrt(np.maximum(0., np.diag(covariance))) > + MotionEvidenceParameters().maximum_frame_off_axis_rad): + raise ValueError("image_motion_frame_angles_uncertain") + accepted = all(_eligible(p) for p in best[1].values()) + return ImageMotionFrameSelection(accepted, "" if accepted else "image_motion_frame_reprojection_or_tilt_failed", + tuple(MotionRoleSelection(r, best[1][r]) for r in roles), best[2], + tuple((g.relation.joint, float(a)) for g, a in zip(model.geometry, best[3]))) + except (ValueError, TypeError, KeyError, FloatingPointError, np.linalg.LinAlgError) as error: + return ImageMotionFrameSelection(False, str(error)) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_projection_worker.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_projection_worker.py new file mode 100644 index 0000000..2938fdd --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/image_projection_worker.py @@ -0,0 +1,38 @@ +"""Bounded process execution of the unchanged, pure current-image solver.""" + +from concurrent.futures import ProcessPoolExecutor, TimeoutError +from concurrent.futures.process import BrokenProcessPool +import multiprocessing +import threading + +from .image_motion_model import ImageMotionFrameSelection +from .image_motion_projection import select_image_motion_frame + + +class ImageProjectionWorker: + def __init__(self, workers=3): + self._pool = ProcessPoolExecutor(max_workers=workers, + mp_context=multiprocessing.get_context("spawn")) + self._slots = threading.BoundedSemaphore(workers) + self._closed = False + + def solve(self, model, frame): + if self._closed or not self._slots.acquire(blocking=False): + return ImageMotionFrameSelection(False, "image_motion_frame_worker_busy") + try: + future = self._pool.submit(select_image_motion_frame, model, frame) + except (RuntimeError, BrokenProcessPool): + self._slots.release() + return ImageMotionFrameSelection(False, "image_motion_frame_worker_unavailable") + future.add_done_callback(lambda _: self._slots.release()) + try: + return future.result(timeout=2.) + except TimeoutError: + future.cancel() + return ImageMotionFrameSelection(False, "image_motion_frame_worker_timeout") + except BrokenProcessPool: + return ImageMotionFrameSelection(False, "image_motion_frame_worker_unavailable") + + def close(self): + self._closed = True + self._pool.shutdown(wait=False, cancel_futures=True) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/ippe.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/ippe.py index f7da3a9..2c2a302 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/ippe.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/ippe.py @@ -111,6 +111,33 @@ def solve_square_tag_ippe( return candidates +def fixed_pose_reprojection_error( + pose: SquareTagPose, + corners_xy: Sequence[Sequence[float]], + *, + tag_size_m: float, + camera_matrix: Sequence[Sequence[float]], +) -> float: + """Check a frozen pose against rectified corners without solving a new pose.""" + corners = np.asarray(corners_xy, dtype=np.float64) + if corners.shape != (4, 2) or not np.all(np.isfinite(corners)): + raise ValueError("corners_xy must contain four finite image points") + intrinsic = _as_camera_matrix(camera_matrix) + points = square_object_points(tag_size_m) + rotation = Rotation.from_quat(pose.quaternion_xyzw) + translation = np.asarray(pose.translation_xyz_m, dtype=np.float64) + camera_points = rotation.apply(points) + translation + if not np.all(np.isfinite(camera_points)) or np.min(camera_points[:, 2]) <= 0: + raise ValueError("fixed pose must have finite, positive-depth corners") + projected, _ = cv2.projectPoints(points, rotation.as_rotvec(), translation, + intrinsic, np.zeros(4, dtype=np.float64)) + residual = projected.reshape(4, 2) - corners + error = float(np.sqrt(np.mean(np.sum(residual * residual, axis=1)))) + if not math.isfinite(error): + raise ValueError("fixed pose reprojection error must be finite") + return error + + def rotation_distance_rad( first_xyzw: Sequence[float], second_xyzw: Sequence[float], diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_evidence.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_evidence.py new file mode 100644 index 0000000..65f5540 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_evidence.py @@ -0,0 +1,413 @@ +"""Pre-freeze planar-branch evidence from a single articulated motion. + +The measurements are actual image candidates, never SDK-derived angles. A +hinge has one rotation axis and one fixed pivot in its parent Tag coordinates: +``R = exp(theta * axis) R_ref`` and ``t = pivot + R * mount``. These are the +same fixed-axis/fixed-pivot relations used by candidate_selection's trajectory +score. Unlike that diagnostic ranking, this module requires a unique hypothesis +to pass absolute quality and observability checks before authorizing a branch. + +The 1.5 mm / 1 degree RMS scales come from that existing trajectory score. +Motion span, cross-validation and conditioning gates are additional evidence +requirements, not changes to final calibration accuracy limits. Calibration +holdout and previously frozen references must never enter this fit API. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from itertools import permutations, product +import math +from typing import Sequence + +import numpy as np +from scipy.spatial.transform import Rotation + +from .parameters import DEFAULT_POSE_TRACKING_PARAMETERS +from .relative import _relative_pose +from .types import SquareTagPose + + +@dataclass(frozen=True) +class MotionRelation: + joint: str + parent_role: str + child_role: str + + +@dataclass(frozen=True) +class RoleCandidates: + """Actual candidates; ``frozen`` means already selected by prior evidence. + + A previously frozen moving parent must be selected in this image using its + existing constraint before a new relation is trained. Its last image's pose + is not a substitute. Raw current-frame roots passed to select_motion_frame + remain a candidate set; the tracker's separate frozen flag is not this flag. + """ + role: str + candidates: tuple[SquareTagPose, ...] + frozen: bool = False + + +@dataclass(frozen=True) +class MotionEvidenceFrame: + stamp_ns: int + roles: tuple[RoleCandidates, ...] + + +@dataclass(frozen=True) +class MotionRoleSelection: + role: str + pose: SquareTagPose + + +@dataclass(frozen=True) +class MotionEvidenceParameters: + minimum_motion_rad: float = math.radians(15.0) + angular_bin_rad: float = math.radians(1.0) + minimum_motion_bins: int = 12 + minimum_translation_span_m: float = 0.003 + maximum_pivot_condition: float = 100.0 + maximum_pivot_rms_m: float = 0.0015 + maximum_pivot_p95_m: float = 0.003 + maximum_off_axis_rms_rad: float = math.radians(1.0) + maximum_off_axis_p95_rad: float = math.radians(2.0) + maximum_split_axis_difference_rad: float = math.radians(3.0) + maximum_frame_off_axis_rad: float = math.radians(3.0) + maximum_frame_pivot_error_m: float = 0.003 + + def __post_init__(self): + for name, value in vars(self).items(): + if not math.isfinite(value) or value <= 0: + raise ValueError(f"{name} must be finite and positive") + if type(self.minimum_motion_bins) is not int or self.minimum_motion_bins < 12: + raise ValueError("minimum_motion_bins must be an integer >= 12") + + +@dataclass(frozen=True) +class MotionConstraintResidual: + off_axis_rad: float + pivot_error_m: float + accepted: bool + + +@dataclass(frozen=True) +class FrozenMotionConstraint: + relation: MotionRelation + reference_quaternion_xyzw: tuple[float, float, float, float] + axis_parent_xyz: tuple[float, float, float] + pivot_parent_xyz_m: tuple[float, float, float] + mount_child_xyz_m: tuple[float, float, float] + source_stamps_ns: tuple[int, ...] + maximum_off_axis_rad: float + maximum_pivot_error_m: float + + def evaluate(self, parent: SquareTagPose, child: SquareTagPose) -> MotionConstraintResidual: + rotation, translation = _relative_pose(parent, child) + off_axis, pivot = _residuals(self, Rotation.from_quat([rotation.as_quat()]), + np.asarray([translation])) + a, p = float(off_axis[0]), float(pivot[0]) + return MotionConstraintResidual(a, p, bool( + _eligible(parent) and _eligible(child) + and a <= self.maximum_off_axis_rad and p <= self.maximum_pivot_error_m)) + + +@dataclass(frozen=True) +class MotionRelationQuality: + joint: str + reason: str + motion_rad: float = 0.0 + motion_bins: int = 0 + translation_span_m: float = 0.0 + pivot_condition: float = 0.0 + pivot_rms_m: float = 0.0 + pivot_p95_m: float = 0.0 + off_axis_rms_rad: float = 0.0 + off_axis_p95_rad: float = 0.0 + split_axis_difference_rad: float = 0.0 + + +@dataclass(frozen=True) +class MotionHypothesis: + branches: tuple[tuple[str, int], ...] + relations: tuple[MotionRelationQuality, ...] + accepted: bool + + +@dataclass(frozen=True) +class MotionBranchResolution: + resolved: bool + reason: str + selections: tuple[MotionRoleSelection, ...] = () + constraints: tuple[FrozenMotionConstraint, ...] = () + hypotheses: tuple[MotionHypothesis, ...] = () + + +@dataclass(frozen=True) +class MotionFrameSelection: + resolved: bool + reason: str + selections: tuple[MotionRoleSelection, ...] = () + + +def _eligible(pose: SquareTagPose) -> bool: + values = (*pose.quaternion_xyzw, *pose.translation_xyz_m, pose.reprojection_error_px) + if (len(pose.quaternion_xyzw) != 4 or len(pose.translation_xyz_m) != 3 + or not np.all(np.isfinite(values)) + or not np.isclose(np.linalg.norm(pose.quaternion_xyzw), 1.0, atol=1e-5) + or pose.translation_xyz_m[2] <= 0 + or not 0 <= pose.reprojection_error_px <= DEFAULT_POSE_TRACKING_PARAMETERS.maximum_reprojection_error_px): + return False + normal = Rotation.from_quat(pose.quaternion_xyzw).apply([0, 0, 1]) + tilt = math.acos(float(np.clip(abs(normal[2]), 0, 1))) + return tilt <= DEFAULT_POSE_TRACKING_PARAMETERS.maximum_tag_tilt_rad + + +def _distance(first: SquareTagPose, second: SquareTagPose) -> tuple[float, float]: + return (float((Rotation.from_quat(first.quaternion_xyzw).inv() + * Rotation.from_quat(second.quaternion_xyzw)).magnitude()), + float(np.linalg.norm(np.subtract(first.translation_xyz_m, second.translation_xyz_m)))) + + +def _equivalent(first: SquareTagPose, second: SquareTagPose) -> bool: + angle, distance = _distance(first, second) + return angle <= math.radians(1.0) and distance <= 0.001 + + +def _validate_relations(relations: Sequence[MotionRelation]) -> tuple[str, ...]: + if not relations or len({r.joint for r in relations}) != len(relations): + raise ValueError("motion relations need non-empty unique joints") + roles = tuple(dict.fromkeys(role for r in relations for role in (r.parent_role, r.child_role))) + if len(roles) > 8 or any(not r.joint or not r.parent_role or not r.child_role + or r.parent_role == r.child_role for r in relations): + raise ValueError("motion relations need a bounded articulated graph") + return roles + + +def _candidate_map(items: Sequence[RoleCandidates], roles: Sequence[str]): + result = {item.role: item for item in items} + if len(result) != len(items) or any(role not in result for role in roles): + return None + for role in roles: + item = result[role] + if not 1 <= len(item.candidates) <= 2 or (item.frozen and len(item.candidates) != 1): + return None + valid = tuple(candidate for candidate in item.candidates if _eligible(candidate)) + if not valid: + return None + result[role] = RoleCandidates(role, valid, item.frozen) + return result + + +def _connect_paths(frames: Sequence[MotionEvidenceFrame], roles: Sequence[str]): + """Preserve both continuous families without treating continuity as truth.""" + candidates = [_candidate_map(frame.roles, roles) for frame in frames] + if any(item is None for item in candidates): + return None, "motion_candidates_missing_or_invalid" + paths = {} + for role in roles: + items = [frame[role] for frame in candidates] + if any(item.frozen != items[0].frozen for item in items): + return None, "motion_candidate_family_interrupted" + # Anchor where both eligible roots are available. If an alternative + # temporarily fails the image-quality gate it stays missing; never copy + # the surviving root into both families to invent evidence. + anchor = max(range(len(items)), key=lambda i: len(items[i].candidates)) + count = len(items[anchor].candidates) + families = [[None] * len(frames) for _ in range(count)] + for index, candidate in enumerate(items[anchor].candidates): + families[index][anchor] = candidate + for sequence in (range(anchor + 1, len(frames)), range(anchor - 1, -1, -1)): + previous = list(items[anchor].candidates) + for frame_index in sequence: + current = items[frame_index].candidates + def cost(order): + return sum(_distance(previous[family], candidate)[0] + for family, candidate in zip(order, current)) + orders = sorted(permutations(range(count), len(current)), key=cost) + if (len(orders) == 2 and abs(cost(orders[0]) - cost(orders[1])) < 1e-9 + and not _equivalent(previous[0], previous[1])): + return None, "motion_candidate_assignment_ambiguous" + for family, candidate in zip(orders[0], current): + angle, translation = _distance(previous[family], candidate) + p = DEFAULT_POSE_TRACKING_PARAMETERS + if angle > p.maximum_pose_jump_rad or translation > p.maximum_translation_jump_m: + return None, "motion_candidate_family_discontinuous" + families[family][frame_index] = candidate + previous[family] = candidate + paths[role] = families + # Near-coincident IPPE roots represent one physical hypothesis, not two. + for role, families in paths.items(): + if len(families) == 2 and all(a is not None and b is not None and _equivalent(a, b) + for a, b in zip(*families)): + paths[role] = families[:1] + return paths, "" + + +def _axis(rotations: Rotation) -> tuple[np.ndarray, np.ndarray]: + vectors = (rotations * rotations[0].inv()).as_rotvec() + axis = np.linalg.svd(vectors, full_matrices=False)[2][0] + if np.dot(axis, vectors[-1]) < 0: + axis = -axis + return axis, vectors @ axis + + +def _fit_constraint(relation, rotations, points, stamps, parameters): + axis, angles = _axis(rotations) + projected = Rotation.from_rotvec(angles[:, None] * axis) * rotations[0] + # One axial pivot gauge is unobservable for any revolute joint. Projecting + # onto the measured rotation axis gives rank five instead of fitting that + # gauge to tiny orientation noise with an arbitrary sixth parameter. + design = np.concatenate((np.tile(np.eye(3), (len(points), 1, 1)), + projected.as_matrix()), axis=2).reshape(-1, 6) + u, singular, vh = np.linalg.svd(design, full_matrices=False) + condition = float(singular[0] / max(singular[4], np.finfo(float).tiny)) + solution = vh[:5].T @ ((u[:, :5].T @ points.ravel()) / singular[:5]) + model = FrozenMotionConstraint(relation, tuple(rotations[0].as_quat()), tuple(axis), + tuple(solution[:3]), tuple(solution[3:]), tuple(stamps), + parameters.maximum_frame_off_axis_rad, parameters.maximum_frame_pivot_error_m) + return model, condition + + +def _residuals(model: FrozenMotionConstraint, rotations: Rotation, points: np.ndarray): + delta = rotations * Rotation.from_quat(model.reference_quaternion_xyzw).inv() + axis = np.asarray(model.axis_parent_xyz) + projected = Rotation.from_rotvec((delta.as_rotvec() @ axis)[:, None] * axis) + angular = (projected.inv() * delta).magnitude() + predicted = np.asarray(model.pivot_parent_xyz_m) + rotations.apply(model.mount_child_xyz_m) + return angular, np.linalg.norm(points - predicted, axis=1) + + +def _metrics(angular, pivot): + return (float(np.sqrt(np.mean(pivot**2))), float(np.percentile(pivot, 95)), + float(np.sqrt(np.mean(angular**2))), float(np.percentile(angular, 95))) + + +def _passes(values, parameters): + return all(value <= limit for value, limit in zip(values, ( + parameters.maximum_pivot_rms_m, parameters.maximum_pivot_p95_m, + parameters.maximum_off_axis_rms_rad, parameters.maximum_off_axis_p95_rad))) + + +def _relation_evidence(relation, parent, child, stamps, parameters): + supported = [index for index, (p, c) in enumerate(zip(parent, child)) if p is not None and c is not None] + if len(supported) < parameters.minimum_motion_bins: + return None, MotionRelationQuality(joint=relation.joint, reason="insufficient_candidate_support") + pairs = [_relative_pose(parent[index], child[index]) for index in supported] + stamps = tuple(stamps[index] for index in supported) + rotations = Rotation.from_quat([rotation.as_quat() for rotation, _ in pairs]) + points = np.asarray([point for _, point in pairs]) + _, angles = _axis(rotations) + motion = float(np.ptp(angles)) + # Use one actual image per angular bin; stationary endpoints and high frame + # rates cannot inflate the evidence count or overwhelm the moving arc. + bins: dict[int, list[int]] = {} + for index, angle in enumerate(angles): + key = int(np.floor((angle - float(np.min(angles))) / parameters.angular_bin_rad + 1e-9)) + bins.setdefault(key, []).append(index) + indices = sorted(values[len(values) // 2] for values in bins.values()) + span = float(np.linalg.norm(np.ptp(points[indices], axis=0))) + common = dict(joint=relation.joint, motion_rad=motion, motion_bins=len(indices), translation_span_m=span) + if motion < parameters.minimum_motion_rad or len(indices) < parameters.minimum_motion_bins: + return None, MotionRelationQuality(reason="insufficient_rotation_evidence", **common) + if motion >= math.radians(170): + return None, MotionRelationQuality(reason="rotation_log_wrap_unsupported", **common) + if span < parameters.minimum_translation_span_m: + return None, MotionRelationQuality(reason="insufficient_pivot_translation", **common) + rotations, points = rotations[indices], points[indices] + selected_stamps = tuple(stamps[index] for index in indices) + model, condition = _fit_constraint(relation, rotations, points, selected_stamps, parameters) + values = _metrics(*_residuals(model, rotations, points)) + quality = dict(common, pivot_condition=condition, pivot_rms_m=values[0], pivot_p95_m=values[1], + off_axis_rms_rad=values[2], off_axis_p95_rad=values[3]) + if condition > parameters.maximum_pivot_condition: + return None, MotionRelationQuality(reason="ill_conditioned_pivot", **quality) + if not _passes(values, parameters): + return None, MotionRelationQuality(reason="motion_geometry_residual", **quality) + # Check interleaved bins and separate early/late intervals in both + # directions. Dense or temporally correlated samples cannot independently + # confirm each other just by being adjacent. This is pre-freeze evidence + # only; no fourth calibration round is consumed here. + count = len(indices) + splits = ((np.arange(0, count, 2), np.arange(1, count, 2)), + (np.arange(count // 2), np.arange(count // 2, count))) + maximum_difference = 0.0 + for first, second in splits: + models = [] + for train, validate in ((first, second), (second, first)): + split, split_condition = _fit_constraint(relation, rotations[train], points[train], + tuple(selected_stamps[i] for i in train), parameters) + models.append(split) + if (split_condition > parameters.maximum_pivot_condition + or not _passes(_metrics(*_residuals(split, rotations[validate], points[validate])), parameters)): + return None, MotionRelationQuality(reason="motion_split_validation_failed", **quality) + difference = math.acos(float(np.clip(abs(np.dot(models[0].axis_parent_xyz, + models[1].axis_parent_xyz)), 0, 1))) + maximum_difference = max(maximum_difference, difference) + quality["split_axis_difference_rad"] = maximum_difference + if maximum_difference > parameters.maximum_split_axis_difference_rad: + return None, MotionRelationQuality(reason="motion_split_axis_inconsistent", **quality) + return model, MotionRelationQuality(reason="", **quality) + + +def resolve_motion_branches( + frames: Sequence[MotionEvidenceFrame], relations: Sequence[MotionRelation], *, + parameters: MotionEvidenceParameters = MotionEvidenceParameters(), +) -> MotionBranchResolution: + """Resolve only a unique geometrically admissible pre-freeze hypothesis. + + The caller authorizes this input as the current session's declared zero + approach, before any affected joint is frozen. A previously frozen role is + represented by its sole already-verified candidate, never both IPPE roots. + """ + roles = _validate_relations(relations) + if len(frames) < parameters.minimum_motion_bins: + return MotionBranchResolution(False, "motion_evidence_insufficient_frames") + stamps = tuple(frame.stamp_ns for frame in frames) + if any(type(stamp) is not int or stamp < 0 for stamp in stamps) or any( + a >= b for a, b in zip(stamps, stamps[1:])): + return MotionBranchResolution(False, "motion_evidence_image_identity_invalid") + paths, reason = _connect_paths(frames, roles) + if paths is None: + return MotionBranchResolution(False, reason) + hypotheses, accepted = [], [] + for branches in product(*(range(len(paths[role])) for role in roles)): + selected = {role: paths[role][branch] for role, branch in zip(roles, branches)} + models, qualities = [], [] + for relation in relations: + model, quality = _relation_evidence(relation, selected[relation.parent_role], + selected[relation.child_role], stamps, parameters) + qualities.append(quality) + if model is not None: + models.append(model) + valid = len(models) == len(relations) and all(selected[role][-1] is not None for role in roles) + hypotheses.append(MotionHypothesis(tuple(zip(roles, branches)), tuple(qualities), valid)) + if valid: + accepted.append((selected, tuple(models))) + if len(accepted) != 1: + reason = "motion_geometry_ambiguous" if accepted else "motion_geometry_unresolved" + return MotionBranchResolution(False, reason, hypotheses=tuple(hypotheses)) + selected, models = accepted[0] + return MotionBranchResolution(True, "", tuple(MotionRoleSelection(role, selected[role][-1]) + for role in roles), models, tuple(hypotheses)) + + +def select_motion_frame( + constraints: Sequence[FrozenMotionConstraint], current_candidates: Sequence[RoleCandidates], +) -> MotionFrameSelection: + """Apply immutable evidence to this frame; never refit or synthesize poses.""" + roles = _validate_relations(tuple(model.relation for model in constraints)) + items = _candidate_map(current_candidates, roles) + if items is None: + return MotionFrameSelection(False, "motion_candidates_missing_or_invalid") + accepted = [] + for combination in product(*(items[role].candidates for role in roles)): + poses = dict(zip(roles, combination)) + if not all(model.evaluate(poses[model.relation.parent_role], poses[model.relation.child_role]).accepted + for model in constraints): + continue + if not any(all(_equivalent(poses[role], previous[role]) for role in roles) for previous in accepted): + accepted.append(poses) + if len(accepted) != 1: + return MotionFrameSelection(False, "motion_geometry_ambiguous" if accepted else "motion_geometry_inconsistent") + return MotionFrameSelection(True, "", tuple(MotionRoleSelection(role, accepted[0][role]) for role in roles)) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_image_diagnostics.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_image_diagnostics.py new file mode 100644 index 0000000..f1cceb5 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_image_diagnostics.py @@ -0,0 +1,223 @@ +"""Image-domain articulated motion diagnosis, never branch authorization. + +Each candidate family keeps its own fit. Frozen geometry is evaluated on held +out images by solving image angles only. No SDK values, calibration zero or +fourth-round fitting data enter this API. Callers must retain source provenance +and run this bounded but expensive analysis outside runtime state locks. +""" +from __future__ import annotations + +from itertools import product +from typing import Sequence + +import numpy as np +from scipy.spatial.transform import Rotation + +from .motion_evidence import ( + MotionEvidenceParameters, MotionRelation, _axis, _connect_paths, _fit_constraint, _residuals, +) +from .motion_image_solver import ImageHingeBundle +from .motion_image_types import ( + ImageAxisRepeatability, ImageHypothesisDiagnostic, ImageIntervalDiagnostic, + ImageMotionFrame, ImageRoleObservation, ImageRoleQuality, MotionImageDiagnosticParameters, + MotionImageDiagnosticReport, PnpHingeDiagnostic, +) +from .relative import _relative_pose + +__all__ = ["ImageMotionFrame", "ImageRoleObservation", "MotionImageDiagnosticParameters", + "MotionImageDiagnosticReport", "analyze_motion_images"] + + +def _ordered_relations(relations): + if not relations or len({r.joint for r in relations}) != len(relations): + raise ValueError("relations_missing_or_duplicate_joint") + children = {r.child_role for r in relations} + if len(children) != len(relations) or any(r.parent_role == r.child_role for r in relations): + raise ValueError("multiple_parent_or_self_relation") + known = {r.parent_role for r in relations} - children + pending, ordered = list(relations), [] + while pending: + available = [r for r in pending if r.parent_role in known] + if not available: + raise ValueError("cyclic_motion_graph") + for relation in available: + ordered.append(relation) + known.add(relation.child_role) + pending.remove(relation) + return tuple(ordered) + + +def _validate_frames(frames, relations, limits): + if len(frames) < limits.minimum_frames: + raise ValueError("insufficient_image_frames") + if len(frames) > 512: + raise ValueError("source_frame_limit_exceeded") + stamps = [f.evidence.stamp_ns for f in frames] + if any(type(stamp) is not int or stamp <= 0 for stamp in stamps) or any( + newer <= older for older, newer in zip(stamps, stamps[1:])): + raise ValueError("image_identity_invalid") + roles = tuple(dict.fromkeys(role for r in relations for role in (r.parent_role, r.child_role))) + roots = set(roles) - {r.child_role for r in relations} + sizes = {} + first_matrix = np.asarray(frames[0].camera_matrix) + for frame in frames: + matrix = np.asarray(frame.camera_matrix) + if (matrix.shape != (3, 3) or not np.all(np.isfinite(matrix)) + or matrix[0, 0] <= 0 or matrix[1, 1] <= 0 + or not np.array_equal(matrix[2], [0, 0, 1])): + raise ValueError("camera_matrix_invalid") + if not np.array_equal(matrix, first_matrix): + raise ValueError("camera_model_changed") + observations = {o.role: o for o in frame.observations} + candidates = {r.role: r for r in frame.evidence.roles} + if len(observations) != len(frame.observations) or any(role not in observations for role in roles): + raise ValueError("image_observation_missing_or_duplicate") + if any(role not in candidates or not candidates[role].frozen for role in roots): + raise ValueError("root_pose_not_frozen") + for role in roles: + observation = observations[role] + corners = np.asarray(observation.corners_xy) + size = observation.tag_size_m + if (corners.shape != (4, 2) or not np.all(np.isfinite(corners)) + or not np.isfinite(size) or size <= 0): + raise ValueError("tag_geometry_invalid") + if role in sizes and size != sizes[role]: + raise ValueError("tag_size_changed") + sizes[role] = size + paths, reason = _connect_paths([f.evidence for f in frames], roles) + if paths is None: + raise ValueError(reason) + return paths, roles + + +def _sample(indices, maximum): + if len(indices) <= maximum: + return list(indices) + return [indices[i] for i in np.linspace(0, len(indices) - 1, maximum, dtype=int)] + + +def _quality(residuals, roles): + norms = np.linalg.norm(np.asarray(residuals).reshape(-1, len(roles), 4, 2), axis=-1) + result = [] + for index, role in enumerate(roles): + rms = np.sqrt(np.mean(norms[:, index] ** 2, axis=1)) + result.append(ImageRoleQuality(role, len(rms), float(np.sqrt(np.mean(rms ** 2))), + float(np.percentile(rms, 95)), float(np.max(rms)))) + return tuple(result) + + +def _subset(poses, indices): + return {role: tuple(items[i] for i in indices) for role, items in poses.items()} + + +def _interval(name, frames, relations, poses, train, validation, limits): + train = _sample(train, limits.maximum_training_frames) + validation = _sample(validation, limits.maximum_validation_frames) + stamps = tuple(frames[i].evidence.stamp_ns for i in train) + heldout = tuple(frames[i].evidence.stamp_ns for i in validation) + if len(train) < limits.minimum_frames or not heldout: + return ImageIntervalDiagnostic(name, False, "insufficient_interval_frames", 0, stamps, heldout) + try: + bundle = ImageHingeBundle(tuple(frames[i] for i in train), relations, _subset(poses, train)) + fit = bundle.solve(limits) + residuals, valid = bundle.validate(fit.x, tuple(frames[i] for i in validation), + _subset(poses, validation), limits) + training = _quality(fit.fun, bundle.child_roles) + checked = _quality(residuals, bundle.child_roles) + converged = bool(fit.success and valid and np.all(np.isfinite(fit.fun)) + and np.all(np.isfinite(residuals))) + return ImageIntervalDiagnostic(name, converged, "" if converged else "optimizer_or_projection_unresolved", + int(fit.nfev), stamps, heldout, training, checked, bundle.geometry(fit.x), + tuple(bundle.motion_spans)) + except (ValueError, FloatingPointError, np.linalg.LinAlgError) as error: + return ImageIntervalDiagnostic(name, False, str(error), 0, stamps, heldout) + + +def _pnp_diagnostics(frames, relations, poses): + result = [] + for relation in relations: + parents, children = poses[relation.parent_role], poses[relation.child_role] + pairs = [_relative_pose(p, c) for p, c in zip(parents, children)] + rotations = Rotation.from_quat([r.as_quat() for r, _ in pairs]) + points = np.asarray([t for _, t in pairs]) + _, angles = _axis(rotations) + span = float(np.ptp(angles)) + if span < MotionEvidenceParameters().minimum_motion_rad: + raise ValueError("insufficient_rotation_evidence") + if span >= np.deg2rad(170): + raise ValueError("rotation_log_wrap_unsupported") + model, condition = _fit_constraint(relation, rotations, points, + tuple(f.evidence.stamp_ns for f in frames), MotionEvidenceParameters()) + if not np.isfinite(condition) or condition > MotionEvidenceParameters().maximum_pivot_condition: + raise ValueError("ill_conditioned_pivot") + angular, errors = _residuals(model, rotations, points) + difference = points - np.asarray(model.pivot_parent_xyz_m) - rotations.apply(model.mount_child_xyz_m) + camera_difference = Rotation.from_quat([p.quaternion_xyzw for p in parents]).apply(difference) + total_squared = float(np.sum(camera_difference ** 2)) + depth_fraction = float(np.sum(camera_difference[:, 2] ** 2) / total_squared) if total_squared > 1e-24 else 0.0 + result.append(PnpHingeDiagnostic(relation.joint, span, + float(np.sqrt(np.mean(errors ** 2))), float(np.percentile(errors, 95)), + depth_fraction, float(np.sqrt(np.mean(angular ** 2))))) + return tuple(result) + + +def _hypothesis(frames, relations, poses, branches, limits): + supported = [i for i in range(len(frames)) if all(items[i] is not None for items in poses.values())] + frames, poses = tuple(frames[i] for i in supported), _subset(poses, supported) + try: + pnp = _pnp_diagnostics(frames, relations, poses) if len(frames) >= limits.minimum_frames else () + if not pnp: + return ImageHypothesisDiagnostic(branches, "insufficient_candidate_support") + except (ValueError, FloatingPointError, np.linalg.LinAlgError) as error: + return ImageHypothesisDiagnostic(branches, str(error)) + full = list(range(len(frames))) + train = _sample(full[::2], limits.maximum_training_frames) + heldout = [i for i in full if i not in set(train)] + midpoint = len(frames) // 2 + intervals = ( + _interval("interleaved", frames, relations, poses, train, heldout, limits), + _interval("early_to_late", frames, relations, poses, full[:midpoint], full[midpoint:], limits), + _interval("late_to_early", frames, relations, poses, full[midpoint:], full[:midpoint], limits), + ) + repeatability = [] + early, late = intervals[1:] + if early.converged and late.converged: + for first, second in zip(early.geometry, late.geometry): + dot = abs(float(np.dot(first.axis_parent_xyz, second.axis_parent_xyz))) + repeatability.append(ImageAxisRepeatability(first.relation.joint, + float(np.arccos(np.clip(dot, -1, 1))), + float(np.linalg.norm(np.subtract(first.pivot_parent_xyz_m, second.pivot_parent_xyz_m))))) + reason = "" if all(interval.converged for interval in intervals) else "incomplete_interval_analysis" + return ImageHypothesisDiagnostic(branches, reason, pnp, intervals, tuple(repeatability)) + + +def analyze_motion_images(frames: Sequence[ImageMotionFrame], relations: Sequence[MotionRelation], *, + parameters: MotionImageDiagnosticParameters | None = None) -> MotionImageDiagnosticReport: + """Return diagnostic evidence only; never select a winning branch or pose. + + Maximum work is bounded by source frames, graph size, candidate families, + sampled image counts and optimizer evaluation limits. Roots must already be + frozen; calibration holdout images must never be passed as training frames. + """ + limits = parameters or MotionImageDiagnosticParameters() + frames, relations = tuple(frames), tuple(relations) + stamps = tuple(frame.evidence.stamp_ns for frame in frames) + try: + if len(relations) > limits.maximum_relations: + raise ValueError("motion_graph_complexity_limit") + relations = _ordered_relations(relations) + paths, roles = _validate_frames(frames, relations, limits) + family_count = int(np.prod([len(paths[role]) for role in roles])) + if family_count > limits.maximum_hypotheses: + raise ValueError("candidate_hypothesis_limit") + except (ValueError, TypeError) as error: + return MotionImageDiagnosticReport("not_available", str(error), stamps) + hypotheses = [] + for indices in product(*(range(len(paths[role])) for role in roles)): + branches = tuple(zip(roles, indices)) + poses = {role: paths[role][index] for role, index in branches} + hypotheses.append(_hypothesis(frames, relations, poses, branches, limits)) + complete = all(not h.reason for h in hypotheses) + status = "available" if complete else "partial" if any(h.intervals for h in hypotheses) else "not_available" + return MotionImageDiagnosticReport(status, "" if complete else "some_hypotheses_not_evaluable", + stamps, tuple(hypotheses)) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_image_solver.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_image_solver.py new file mode 100644 index 0000000..f60a4e9 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_image_solver.py @@ -0,0 +1,174 @@ +"""Bounded articulated image fit for preparation and offline diagnostics.""" +from __future__ import annotations + +import numpy as np +from scipy.optimize import least_squares +from scipy.sparse import lil_matrix +from scipy.spatial.transform import Rotation + +from .ippe import square_object_points +from .motion_evidence import MotionEvidenceParameters, _axis, _fit_constraint +from .relative import _relative_pose +from .motion_image_types import ImageHingeGeometry + + +def _basis(axis): + other = np.array([1., 0, 0]) if abs(axis[0]) < .9 else np.array([0., 1, 0]) + first = np.cross(axis, other) + first /= np.linalg.norm(first) + return np.stack((first, np.cross(axis, first)), axis=1) + + +class ImageHingeBundle: + """Shared hinge geometry and per-frame geometric angles; no SDK inputs. + + Relations are topologically sorted. Roots are actual, already frozen poses + for each image, so a previously frozen moving parent is supported too. + Every child has one parent. Each hinge removes the unobservable axial pivot + gauge explicitly. Validation may change angles only, never shared geometry. + """ + + def __init__(self, frames, relations, poses): + self.frames, self.relations, self.poses = frames, relations, poses + self.count, self.joints = len(frames), len(relations) + self.child_roles = tuple(r.child_role for r in relations) + self.root_roles = tuple(dict.fromkeys(r.parent_role for r in relations + if r.parent_role not in self.child_roles)) + self.axes, self.bases, self.references, angles, initial = [], [], [], [], [] + self.motion_spans = [] + for relation in relations: + pairs = [_relative_pose(parent, child) for parent, child in + zip(poses[relation.parent_role], poses[relation.child_role])] + rotations = Rotation.from_quat([r.as_quat() for r, _ in pairs]) + points = np.asarray([t for _, t in pairs]) + axis, theta = _axis(rotations) + # The full arc has passed the diagnostic minimum-span check. A + # half interval is only a counterfactual fit: retain its measured + # span in the report rather than treating it as new authorization. + span = float(np.ptp(theta)) + if span < 1e-6: + raise ValueError("insufficient_interval_rotation") + self.motion_spans.append((relation.joint, span)) + model, condition = _fit_constraint(relation, rotations, points, + tuple(f.evidence.stamp_ns for f in frames), MotionEvidenceParameters()) + if not np.isfinite(condition) or condition > MotionEvidenceParameters().maximum_pivot_condition: + raise ValueError("ill_conditioned_interval_pivot") + pivot, mount = np.array(model.pivot_parent_xyz_m), np.array(model.mount_child_xyz_m) + axial = float(pivot @ axis) + pivot -= axial * axis + mount += rotations[0].inv().apply(axis) * axial + basis = _basis(axis) + self.axes.append(axis) + self.bases.append(basis) + self.references.append(rotations[0]) + angles.append(theta) + initial.extend((0., 0., 0., 0., 0., *(basis.T @ pivot), *mount)) + self.initial = np.r_[initial, np.asarray(angles).T[1:].ravel()] + self.scale = np.r_[np.tile([1, 1, 1, 1, 1, .05, .05, .05, .05, .05], self.joints), + np.ones(self.joints * (self.count - 1))] + self.root_transforms, self.matrices, self.observed, self.objects = self._image_inputs(frames, poses) + + @property + def static_parameter_count(self): + return 10 * self.joints + + def geometry_parameter_indices(self, index): + return tuple(range(index * 10, (index + 1) * 10)) + + def geometry_dependency_indices(self, index): + return self.geometry_parameter_indices(index) + + def _image_inputs(self, frames, poses): + roots = {role: (Rotation.from_quat([p.quaternion_xyzw for p in poses[role]]), + np.asarray([p.translation_xyz_m for p in poses[role]])) for role in self.root_roles} + observations = [{o.role: o for o in frame.observations} for frame in frames] + observed = np.asarray([[o[role].corners_xy for role in self.child_roles] for o in observations]) + objects = {role: square_object_points(observations[0][role].tag_size_m) for role in self.child_roles} + return roots, np.asarray([f.camera_matrix for f in frames]), observed, objects + + def geometry_values(self, parameters, index): + values = parameters[index * 10:(index + 1) * 10] + change = Rotation.from_rotvec(self.bases[index] @ values[:2]) + axis = change.apply(self.axes[index]) + reference = Rotation.from_rotvec(values[2:5]) * self.references[index] + # Transport the initial tangent basis with the axis. Rebuilding + # _basis(axis) switches its helper at |axis.x|=.9, which changes the + # meaning of the same pivot coordinates discontinuously during a fit. + pivot = change.apply(self.bases[index] @ values[5:7]) + return axis, reference, pivot, values[7:10] + + def geometry(self, parameters): + result = [] + for index, relation in enumerate(self.relations): + axis, reference, pivot, mount = self.geometry_values(parameters, index) + result.append(ImageHingeGeometry(relation, tuple(axis), tuple(reference.as_quat()), + tuple(pivot), tuple(mount))) + return tuple(result) + + def project(self, parameters, angles, roots, matrices, objects): + transforms = dict(roots) + projected, valid = [], True + for index, relation in enumerate(self.relations): + axis, reference, pivot, mount = self.geometry_values(parameters, index) + relative = Rotation.from_rotvec(angles[:, index, None] * axis) * reference + parent_rotation, parent_translation = transforms[relation.parent_role] + translation = parent_translation + parent_rotation.apply(pivot + relative.apply(mount)) + rotation = parent_rotation * relative + transforms[relation.child_role] = (rotation, translation) + camera_points = np.einsum('nij,kj->nki', rotation.as_matrix(), objects[relation.child_role]) + camera_points += translation[:, None, :] + valid = valid and bool(np.all(camera_points[:, :, 2] > 1e-6)) + pixels = np.einsum('nij,nkj->nki', matrices, camera_points) + depth = np.maximum(pixels[:, :, 2:], 1e-6) + projected.append(pixels[:, :, :2] / depth) + return np.stack(projected, axis=1), valid + + def residual(self, parameters): + angles = np.vstack((np.zeros(self.joints), parameters[self.static_parameter_count:].reshape(-1, self.joints))) + pixels, _ = self.project(parameters, angles, self.root_transforms, self.matrices, self.objects) + return (pixels - self.observed).ravel() + + def solve(self, parameters): + # A child's image depends on its ancestors' parameters and angles only. + ancestry = {} + for index, relation in enumerate(self.relations): + ancestry[relation.child_role] = (*ancestry.get(relation.parent_role, ()), index) + width = 8 * self.joints + sparsity = lil_matrix((self.count * width, len(self.initial)), dtype=int) + for frame in range(self.count): + for child, role in enumerate(self.child_roles): + rows = slice(frame * width + child * 8, frame * width + (child + 1) * 8) + for ancestor in ancestry[role]: + sparsity[rows, self.geometry_parameter_indices(ancestor)] = 1 + if frame: + sparsity[rows, self.static_parameter_count + (frame - 1) * self.joints + ancestor] = 1 + return least_squares(self.residual, self.initial, jac_sparsity=sparsity.tocsr(), + x_scale=self.scale, max_nfev=parameters.maximum_solver_evaluations, + # LSMR's default 1e-6 inner tolerance leaves coupled hinge/angle + # steps too approximate near the image minimum. Tighten that + # linear solve so the unchanged outer convergence tolerances can + # be reached within the bounded evaluation budget. + tr_options={"atol": 1e-10, "btol": 1e-10}, + ftol=1e-9, xtol=1e-9, gtol=1e-7) + + def validate(self, parameters, frames, poses, limits): + roots, matrices, observed, objects = self._image_inputs(frames, poses) + residuals, converged = [], True + for frame in range(len(frames)): + guess = [] + for index, relation in enumerate(self.relations): + rotation, _ = _relative_pose(poses[relation.parent_role][frame], poses[relation.child_role][frame]) + axis, reference, _, _ = self.geometry_values(parameters, index) + guess.append(float((rotation * reference.inv()).as_rotvec() @ axis)) + frame_roots = {role: (r[frame:frame + 1], t[frame:frame + 1]) for role, (r, t) in roots.items()} + def residual(angles): + pixels, _ = self.project(parameters, angles.reshape(1, self.joints), frame_roots, + matrices[frame:frame + 1], objects) + return (pixels[0] - observed[frame]).ravel() + fit = least_squares(residual, guess, max_nfev=limits.maximum_frame_evaluations, + ftol=1e-9, xtol=1e-9, gtol=1e-7) + _, positive_depth = self.project(parameters, fit.x.reshape(1, self.joints), frame_roots, + matrices[frame:frame + 1], objects) + converged = converged and bool(fit.success and positive_depth) + residuals.append(fit.fun.reshape(self.joints, 4, 2)) + return np.asarray(residuals), converged diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_image_types.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_image_types.py new file mode 100644 index 0000000..fc5e79f --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/motion_image_types.py @@ -0,0 +1,121 @@ +"""Immutable inputs and reports for image-domain motion diagnostics. + +These types carry no branch authorization, zero reference or calibration map. +Image residuals and metric PnP residuals deliberately have different fields. +""" +from __future__ import annotations + +from dataclasses import dataclass, field + +from .motion_evidence import MotionEvidenceFrame, MotionRelation + + +@dataclass(frozen=True) +class ImageRoleObservation: + role: str + tag_size_m: float + corners_xy: tuple[tuple[float, float], ...] + + +@dataclass(frozen=True) +class ImageMotionFrame: + evidence: MotionEvidenceFrame + camera_matrix: tuple[tuple[float, float, float], ...] + observations: tuple[ImageRoleObservation, ...] + + +@dataclass(frozen=True) +class MotionImageDiagnosticParameters: + maximum_training_frames: int = 36 + maximum_validation_frames: int = 72 + maximum_solver_evaluations: int = 80 + maximum_frame_evaluations: int = 25 + maximum_relations: int = 3 + maximum_hypotheses: int = 8 + minimum_frames: int = 12 + + def __post_init__(self): + for name, value in vars(self).items(): + if type(value) is not int or value <= 0: + raise ValueError(f"{name} must be a positive integer") + if self.minimum_frames < 12 or self.maximum_training_frames < self.minimum_frames: + raise ValueError("image diagnostics require at least 12 training frames") + if (self.maximum_training_frames > 72 or self.maximum_validation_frames > 144 + or self.maximum_solver_evaluations > 150 or self.maximum_frame_evaluations > 50 + or self.maximum_relations > 3 or self.maximum_hypotheses > 8): + raise ValueError("image diagnostic computation limit exceeded") + + +@dataclass(frozen=True) +class ImageRoleQuality: + role: str + frames: int + rms_px: float + frame_rms_p95_px: float + maximum_frame_rms_px: float + + +@dataclass(frozen=True) +class ImageHingeGeometry: + relation: MotionRelation + axis_parent_xyz: tuple[float, float, float] + reference_quaternion_xyzw: tuple[float, float, float, float] + pivot_parent_xyz_m: tuple[float, float, float] + mount_child_xyz_m: tuple[float, float, float] + + +@dataclass(frozen=True) +class ImageIntervalDiagnostic: + name: str + converged: bool + reason: str + solver_evaluations: int + training_stamps_ns: tuple[int, ...] + validation_stamps_ns: tuple[int, ...] + training_quality: tuple[ImageRoleQuality, ...] = () + validation_quality: tuple[ImageRoleQuality, ...] = () + geometry: tuple[ImageHingeGeometry, ...] = () + training_motion_span_rad_by_joint: tuple[tuple[str, float], ...] = () + + +@dataclass(frozen=True) +class PnpHingeDiagnostic: + joint: str + motion_span_rad: float + translation_rms_m: float + translation_p95_m: float + camera_depth_squared_error_fraction: float + off_axis_rms_rad: float + + +@dataclass(frozen=True) +class ImageAxisRepeatability: + joint: str + early_late_axis_difference_rad: float + early_late_pivot_difference_m: float + + +@dataclass(frozen=True) +class ImageHypothesisDiagnostic: + branches: tuple[tuple[str, int], ...] + reason: str + pnp_geometry: tuple[PnpHingeDiagnostic, ...] = () + intervals: tuple[ImageIntervalDiagnostic, ...] = () + repeatability: tuple[ImageAxisRepeatability, ...] = () + + +@dataclass(frozen=True) +class MotionImageDiagnosticReport: + status: str + reason: str + source_stamps_ns: tuple[int, ...] + hypotheses: tuple[ImageHypothesisDiagnostic, ...] = () + scope: str = field(default="diagnostic_not_branch_authorization", init=False) + branch_authorization_allowed: bool = field(default=False, init=False) + calibration_accuracy_verified: bool = field(default=False, init=False) + interpretation: str = ( + "Pixel residuals test image consistency; PnP metric residuals test noisy pose estimates. " + "Neither lowest pixel score nor this diagnostic replaces the existing branch, " + "baseline, interval-repeatability or independent calibration holdout gates. " + "The first training angle is a solver gauge, not a joint zero reference." + ) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/parameters.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/parameters.py index 37177e1..dfa4d85 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/parameters.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/parameters.py @@ -5,6 +5,10 @@ from __future__ import annotations from dataclasses import dataclass import math + +BRANCH_TRANSLATION_EQUIVALENCE_M = 0.001 + + @dataclass(frozen=True) class PoseTrackingParameters: """One shared set of defaults, with angles stored in radians.""" @@ -15,7 +19,17 @@ class PoseTrackingParameters: maximum_translation_jump_m: float = 0.04 maximum_tag_tilt_rad: float = math.radians(75.0) reset_after_seconds: float = 5.0 + branch_confirmation_frames: int = 5 + branch_confirmation_minimum_seconds: float = 0.1 + branch_equivalence_rad: float = math.radians(1.0) DEFAULT_POSE_TRACKING_PARAMETERS = PoseTrackingParameters() DEFAULT_REPROJECTION_TIE_PX = DEFAULT_POSE_TRACKING_PARAMETERS.reprojection_tie_px +POSE_TRACKING_POLICY_VERSION = "confirmed_branch_v8_training_model_selection" +FROZEN_BRANCH_POLICY_VERSIONS = frozenset({ + "confirmed_branch_v1", "confirmed_branch_v2_fixed_reference", + "confirmed_branch_v3_recoverable_ambiguity", "confirmed_branch_v5_image_motion", + "confirmed_branch_v6_source_geometry", "confirmed_branch_v7_final_image_sampling", + POSE_TRACKING_POLICY_VERSION, +}) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/production_image_motion.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/production_image_motion.py new file mode 100644 index 0000000..8a5ce84 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/production_image_motion.py @@ -0,0 +1,230 @@ +"""Pre-zero motion authorization from images, followed by frozen image geometry. + +A hinge bundle minimizes measured corner errors, not the poorly conditioned +single-image PnP depth estimates. All IPPE families seed independent fits. A +model is authorized only with observable geometry, absolute image quality and +held-out, motion-stratified evidence separating every distinct alternative. +This preparation-image check does not replace final fourth-round acceptance. +""" +from dataclasses import dataclass, replace +from itertools import product + +import numpy as np + +from .image_motion_model import ( + IMAGE_MOTION_MODEL_POLICY, ImageMotionModel, ImageMotionResolution, + ImageMotionHypothesis, ImageMotionFrameSelection, image_motion_model_from_dict, +) +from .image_motion_projection import select_image_motion_frame +from .motion_evidence import MotionEvidenceParameters +from .image_model_selection import ( + compare_image_losses as _comparison, equivalent_frame_selections, + holm_adjusted_probabilities, training_candidate_index, +) +from .motion_image_diagnostics import ( + _ordered_relations, _validate_frames, _sample, _subset, _quality, _pnp_diagnostics, +) +from .motion_image_solver import ImageHingeBundle + +__all__ = ["ImageMotionModel", "ImageMotionResolution", "ImageMotionFrameSelection", + "resolve_image_motion", "select_image_motion_frame", "image_motion_model_from_dict", + "IMAGE_MOTION_MODEL_POLICY"] + + +@dataclass(frozen=True) +class ImageMotionParameters: + maximum_training_frames: int = 72 + maximum_validation_frames: int = 72 + maximum_solver_evaluations: int = 150 + maximum_frame_evaluations: int = 40 + minimum_frames: int = 24 + maximum_hypotheses: int = 64 + maximum_relations: int = 6 + familywise_error_probability: float = .01 + + def __post_init__(self): + for name in ("maximum_training_frames", "maximum_validation_frames", "maximum_solver_evaluations", + "maximum_frame_evaluations", "minimum_frames", "maximum_hypotheses", "maximum_relations"): + if type(getattr(self, name)) is not int or getattr(self, name) <= 0: + raise ValueError(f"{name} must be a positive integer") + if (self.minimum_frames < 24 or self.maximum_training_frames < 12 + or self.maximum_training_frames > 72 or self.maximum_validation_frames > 144 + or self.maximum_solver_evaluations > 500 or self.maximum_frame_evaluations > 100 + or self.maximum_hypotheses > 256 or self.maximum_relations > 8 + or not 0 < self.familywise_error_probability <= .01): + raise ValueError("image_motion_limits_invalid") + + +def _uncertainty(bundle, fit): + """Gaussian local confidence after estimating all per-image nuisance angles. + + The bundle already fixes angle and axial-pivot gauges. SVD of the full + Jacobian accounts for coupling to per-frame angles. Residual variance has a + 0.03 pixel floor, the existing image branch resolution, to avoid claiming + infinite precision on synthetic or rounded input. Three-sigma conservative + vector bounds cover the axis's two and pivot's three coordinates at >95%. + """ + jacobian = fit.jac.toarray() if hasattr(fit.jac, "toarray") else np.asarray(fit.jac) + scaled = jacobian * bundle.scale[None, :] + _, singular, vh = np.linalg.svd(scaled, full_matrices=False) + if singular[-1] <= singular[0] * np.finfo(float).eps * max(scaled.shape): + raise ValueError("image_motion_geometry_rank_deficient") + sigma_squared = max(.03 ** 2, float(np.sum(fit.fun ** 2) / max(1, len(fit.fun) - len(fit.x)))) + covariance_scaled = (vh.T / singular ** 2) @ vh * sigma_squared + covariance = covariance_scaled * bundle.scale[:, None] * bundle.scale[None, :] + axes, pivots = [], [] + for index, relation in enumerate(bundle.relations): + derivative = np.zeros((6, len(fit.x))) + for column in bundle.geometry_dependency_indices(index): + step = 1e-5 * bundle.scale[column] + upper, lower = fit.x.copy(), fit.x.copy() + upper[column] += step + lower[column] -= step + ua, _, up, _ = bundle.geometry_values(upper, index) + la, _, lp, _ = bundle.geometry_values(lower, index) + derivative[:, column] = np.r_[ua - la, up - lp] / (2 * step) + propagated = derivative @ covariance @ derivative.T + axes.append((relation.joint, float(3 * np.sqrt(max(0., np.linalg.eigvalsh(propagated[:3, :3])[-1]))))) + pivots.append((relation.joint, float(3 * np.sqrt(max(0., np.linalg.eigvalsh(propagated[3:, 3:])[-1]))))) + return tuple(axes), tuple(pivots) + + +def _model(bundle, fit, frames, branches, training, validation): + from .cad_hinge import CadImageHingeBundle + model_type, extra = ImageMotionModel, {} + if isinstance(bundle, CadImageHingeBundle): + from .cad_image_model import CadImageMotionModel + model_type = CadImageMotionModel + extra = {"constraints": bundle.constraints, "source_urdf_sha256": bundle.source_urdf_sha256} + return model_type(bundle.geometry(fit.x), tuple(tuple(v) for v in frames[0].camera_matrix), + tuple((role, next(o.tag_size_m for o in frames[0].observations if o.role == role)) + for role, _ in branches), tuple(f.evidence.stamp_ns for f in frames), + tuple(frames[i].evidence.stamp_ns for i in training), + tuple(frames[i].evidence.stamp_ns for i in validation), branches, **extra) + + +def _fit_hypothesis(frames, relations, poses, branches, training, validation, limits, + *, geometry_constraints=(), source_urdf_sha256=""): + if any(pose is None for role in poses.values() for pose in role): + return ImageMotionHypothesis(branches, False, "incomplete_candidate_family"), None, None + try: + pnp = _pnp_diagnostics(frames, relations, poses) + selected = tuple(frames[i] for i in training) + if geometry_constraints: + from .cad_hinge import CadImageHingeBundle + bundle = CadImageHingeBundle(selected, relations, _subset(poses, training), + constraints=geometry_constraints, source_urdf_sha256=source_urdf_sha256) + else: + bundle = ImageHingeBundle(selected, relations, _subset(poses, training)) + fit = bundle.solve(limits) + model = _model(bundle, fit, frames, branches, training, validation) + training_quality = _quality(fit.fun, bundle.child_roles) + training_converged = bool(fit.success and np.all(np.isfinite(fit.fun))) + axes, pivots = _uncertainty(bundle, fit) if training_converged else ((), ()) + p = MotionEvidenceParameters() + reason = "" if training_converged else "image_motion_optimizer_unresolved" + if not reason and any(q.maximum_frame_rms_px > model.maximum_reprojection_error_px for q in training_quality): + reason = "image_motion_reprojection_failed" + if not reason and (any(v > p.maximum_split_axis_difference_rad for _, v in axes) + or any(v > p.maximum_frame_pivot_error_m for _, v in pivots)): + reason = "image_motion_geometry_uncertain" + training_accepted = not reason + try: + residuals, valid = bundle.validate(fit.x, tuple(frames[i] for i in validation), + _subset(poses, validation), limits) + validation_quality = _quality(residuals, bundle.child_roles) + except (ValueError, FloatingPointError, np.linalg.LinAlgError) as error: + # Keep training eligibility: a failed holdout must veto the chosen + # candidate, never silently make the next candidate the winner. + residuals, valid, validation_quality = None, False, () + reason = reason or str(error) + converged = bool(training_converged and valid and np.all(np.isfinite(residuals))) + if not reason and not converged: + reason = "image_motion_optimizer_unresolved" + if not reason and any(q.maximum_frame_rms_px > model.maximum_reprojection_error_px for q in validation_quality): + reason = "image_motion_reprojection_failed" + hypothesis = ImageMotionHypothesis(branches, converged, reason, training_quality, validation_quality, + tuple((r.joint, r.motion_span_rad) for r in pnp), axes, pivots, + training_solver_status=int(fit.status), training_solver_evaluations=int(fit.nfev), + training_accepted=training_accepted) + rms = None if residuals is None else np.sqrt(np.mean(np.sum(residuals ** 2, axis=-1), axis=(1, 2))) + return hypothesis, model, rms + except (ValueError, FloatingPointError, np.linalg.LinAlgError) as error: + return ImageMotionHypothesis(branches, False, str(error)), None, None + + +def resolve_image_motion(frames, relations, *, parameters=None, geometry_constraints=(), source_urdf_sha256=""): + """Only pre-zero preparation frames may enter this expensive training API. + + Runtime owns session, motion and phase checks. This function knows no SDK + channels, baseline or round and cannot acquire extra training images itself. + Its output model retains the disjoint image identities needed for auditing. + """ + limits = parameters or ImageMotionParameters() + frames, relations = tuple(frames), tuple(relations) + try: + relations = _ordered_relations(relations) + if len(relations) > limits.maximum_relations: + raise ValueError("image_motion_graph_limit") + paths, roles = _validate_frames(frames, relations, limits) + if np.prod([len(paths[r]) for r in roles]) > limits.maximum_hypotheses: + raise ValueError("image_motion_hypothesis_limit") + training = _sample(list(range(0, len(frames), 2)), limits.maximum_training_frames) + validation = _sample([i for i in range(len(frames)) if i not in training], limits.maximum_validation_frames) + if min(len(training), len(validation)) < 12: + raise ValueError("image_motion_insufficient_independent_frames") + except (ValueError, TypeError) as error: + return ImageMotionResolution(False, str(error)) + hypotheses, models, residuals = [], [], [] + for indices in product(*(range(len(paths[r])) for r in roles)): + branches = tuple(zip(roles, indices)) + poses = {role: paths[role][index] for role, index in branches} + geometry = {"geometry_constraints": geometry_constraints, "source_urdf_sha256": source_urdf_sha256} + hypothesis, model, rms = _fit_hypothesis(frames, relations, poses, branches, training, validation, limits, + **(geometry if geometry_constraints else {})) + hypotheses.append(hypothesis) + models.append(model) + residuals.append(rms) + winner = training_candidate_index(hypotheses) + if winner is None or hypotheses[winner].reason: + return ImageMotionResolution(False, "image_motion_no_observable_image_model", hypotheses=tuple(hypotheses)) + model = models[winner] + checks = tuple(select_image_motion_frame(model, frames[i]) for i in validation) + if not all(c.resolved for c in checks): + return ImageMotionResolution(False, "image_motion_validation_frame_unresolved", hypotheses=tuple(hypotheses)) + angles = [[v for _, v in c.angles_rad] for c in checks] + winner_errors = np.array([np.sqrt(np.mean([q.rms_px ** 2 for q in c.quality])) for c in checks]) + comparisons = [] + for index, alternative in enumerate(models): + if index == winner: + continue + if alternative is None or residuals[index] is None or not hypotheses[index].converged: + # Missing candidate evidence is not evidence excluding a branch. + return ImageMotionResolution(False, "image_motion_alternative_not_evaluable", hypotheses=tuple(hypotheses)) + if hypotheses[index].reason == "image_motion_reprojection_failed": + # This converged model violates the unchanged absolute image gate + # on training/held-out images. It is already an inadmissible + # explanation, not a competing accepted family whose current-frame + # angle uncertainty should prevent the valid model being used. + continue + alternative_checks = tuple(select_image_motion_frame(alternative, frames[i]) for i in validation) + equivalent = all(equivalent_frame_selections(checked, other) + for checked, other in zip(checks, alternative_checks)) + if equivalent: + comparisons.append((index, 0.)) + continue + if not all(c.quality for c in alternative_checks): + return ImageMotionResolution(False, "image_motion_alternative_angles_unresolved", hypotheses=tuple(hypotheses)) + alternative_errors = np.array([np.sqrt(np.mean([q.rms_px ** 2 for q in c.quality])) + for c in alternative_checks]) + probability = _comparison(winner_errors, alternative_errors, angles, model.reprojection_tie_px) + comparisons.append((index, probability)) + adjusted = holm_adjusted_probabilities(comparisons) + hypotheses[winner] = replace(hypotheses[winner], comparison_p_values=tuple(comparisons), + comparison_adjusted_p_values=adjusted) + if any(probability > limits.familywise_error_probability for _, probability in adjusted): + return ImageMotionResolution(False, "image_motion_families_not_distinguishable", hypotheses=tuple(hypotheses)) + final = select_image_motion_frame(model, frames[-1]) + if not final.resolved: + return ImageMotionResolution(False, final.reason, hypotheses=tuple(hypotheses)) + return ImageMotionResolution(True, "", model, tuple(hypotheses), final.selections) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/tracking.py b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/tracking.py index 238463c..c863b2b 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/tracking.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/geometry/tag_pose/tracking.py @@ -2,7 +2,10 @@ from __future__ import annotations -from dataclasses import replace +from contextlib import ExitStack +from copy import deepcopy +from dataclasses import asdict, dataclass, field, replace +from threading import RLock from typing import Sequence import math @@ -10,9 +13,69 @@ from scipy.spatial.transform import Rotation import cv2 import numpy as np -from .ippe import rotation_distance_rad, solve_square_tag_ippe -from .parameters import DEFAULT_POSE_TRACKING_PARAMETERS, DEFAULT_REPROJECTION_TIE_PX +from .ippe import fixed_pose_reprojection_error, rotation_distance_rad, solve_square_tag_ippe +from .branch_confirmation import BranchConfirmation +from .fixed_reference import FixedTagReference +from .parameters import DEFAULT_POSE_TRACKING_PARAMETERS, DEFAULT_REPROJECTION_TIE_PX, PoseTrackingParameters from .types import SquareTagPose +from .image_motion_commit import (VerifiedImageMotionDecision, + verify_image_motion_frame, commit_image_motion_frame) + + +@dataclass(frozen=True) +class TrackerImageObservation: + """Exact immutable pixels and optics retained independently of callers.""" + + stamp_ns: int + corners_xy: tuple[tuple[float, float], ...] + camera_matrix: tuple[tuple[float, float, float], ...] + tag_size_m: float + + +@dataclass(frozen=True) +class MotionTrackerSnapshot: + """One image's immutable inputs for a lock-free geometric decision.""" + + tracker: SquareTagPoseTracker = field(repr=False) + role: str + epoch: int + stamp_ns: int + revision: int + frozen: bool + candidates: tuple[SquareTagPose, ...] + previous_pose: SquareTagPose | None + evidence_id: str | None + motion_managed: bool + image_observation: TrackerImageObservation | None = None + + +@dataclass(frozen=True) +class MotionPoseSelection: + """A candidate selected by a previously checked geometric constraint.""" + + snapshot: MotionTrackerSnapshot + pose: SquareTagPose + evidence_id: str + + +@dataclass(frozen=True) +class MotionFrameCommit: + accepted: bool + reason: str + selections: tuple[MotionPoseSelection, ...] = () + observation_snapshots: tuple[MotionObservationSnapshot, ...] = () + + +@dataclass(frozen=True) +class MotionObservationSnapshot: + """Detached evidence captured in the same critical section as selection.""" + + tracker: SquareTagPoseTracker = field(repr=False) + role: str + stamp_ns: int + candidates: tuple[SquareTagPose, ...] + diagnostics: dict[str, object] + corrections: int def _validate_reprojection_thresholds(maximum_error: float, tie_error: float) -> None: @@ -42,13 +105,9 @@ def select_continuous_pose( maximum_rotation = float(maximum_pose_jump_rad) maximum_translation = float(maximum_translation_jump_m) maximum_tilt = float(maximum_tag_tilt_rad) - if min( - maximum_error, - maximum_rotation, - maximum_translation, - maximum_tilt, - ) <= 0.0: - raise ValueError("PnP selection thresholds must be positive") + if any(not math.isfinite(value) or value <= 0 for value in ( + maximum_error, maximum_rotation, maximum_translation, maximum_tilt)): + raise ValueError("PnP selection thresholds must be finite and positive") eligible: list[SquareTagPose] = [] for candidate in candidates: @@ -67,13 +126,9 @@ def select_continuous_pose( if previous is None: return best, "" - # Temporal continuity must only break a genuine planar-PnP tie. The old - # implementation normalised reprojection error by the permissive 1.5 px - # rejection limit, which allowed a stale mirror branch at 0.25 px to beat - # the true branch at e.g. 0.05 px merely because it was closer to the - # preceding (already wrong) pose. Once one IPPE solution has a meaningful - # image-fit advantage, trust it and allow the tracker to leave the stale - # branch even if that correction is a large pose jump. + # Image fit identifies a proposed branch. A stateless selector cannot + # confirm a large correction; the tracker below owns that evidence and + # knows whether a reference has already been frozen. competitive = [ candidate for candidate in eligible @@ -81,6 +136,14 @@ def select_continuous_pose( <= best.reprojection_error_px + tie_error ] if len(competitive) == 1: + nearest = min(eligible, key=lambda candidate: + rotation_distance_rad(previous.quaternion_xyzw, candidate.quaternion_xyzw) / maximum_rotation + + np.linalg.norm(np.asarray(previous.translation_xyz_m) - candidate.translation_xyz_m) / maximum_translation) + if (rotation_distance_rad(previous.quaternion_xyzw, best.quaternion_xyzw) > maximum_rotation + or np.linalg.norm(np.asarray(previous.translation_xyz_m) - best.translation_xyz_m) > maximum_translation + or rotation_distance_rad(nearest.quaternion_xyzw, best.quaternion_xyzw) + > DEFAULT_POSE_TRACKING_PARAMETERS.branch_equivalence_rad): + return None, "pose_branch_unconfirmed" return best, "" previous_translation = np.asarray(previous.translation_xyz_m, dtype=float) @@ -139,6 +202,9 @@ class SquareTagPoseTracker: maximum_translation_jump_m: float = DEFAULT_POSE_TRACKING_PARAMETERS.maximum_translation_jump_m, maximum_tag_tilt_rad: float = DEFAULT_POSE_TRACKING_PARAMETERS.maximum_tag_tilt_rad, reset_after_seconds: float = DEFAULT_POSE_TRACKING_PARAMETERS.reset_after_seconds, + branch_confirmation_frames: int = DEFAULT_POSE_TRACKING_PARAMETERS.branch_confirmation_frames, + branch_confirmation_minimum_seconds: float = DEFAULT_POSE_TRACKING_PARAMETERS.branch_confirmation_minimum_seconds, + branch_equivalence_rad: float = DEFAULT_POSE_TRACKING_PARAMETERS.branch_equivalence_rad, ) -> None: self.maximum_reprojection_error_px = float( maximum_reprojection_error_px @@ -150,23 +216,230 @@ class SquareTagPoseTracker: self.maximum_pose_jump_rad = float(maximum_pose_jump_rad) self.maximum_translation_jump_m = float(maximum_translation_jump_m) self.maximum_tag_tilt_rad = float(maximum_tag_tilt_rad) + if any(not math.isfinite(v) or v <= 0 for v in ( + self.maximum_pose_jump_rad, self.maximum_translation_jump_m, + self.maximum_tag_tilt_rad, float(reset_after_seconds))): + raise ValueError("PnP tracking limits must be finite and positive") self.reset_after_ns = int(float(reset_after_seconds) * 1_000_000_000) if self.reset_after_ns <= 0: raise ValueError("reset_after_seconds must be positive") - self._previous: dict[str, tuple[int, SquareTagPose]] = {} + self.branch_confirmation_frames = branch_confirmation_frames + self.branch_confirmation_minimum_seconds = float(branch_confirmation_minimum_seconds) + self.branch_equivalence_rad = float(branch_equivalence_rad) + self._lock = RLock() + self._epoch = 0 + self._branches = BranchConfirmation(PoseTrackingParameters( + self.maximum_reprojection_error_px, self.reprojection_tie_px, + self.maximum_pose_jump_rad, self.maximum_translation_jump_m, + self.maximum_tag_tilt_rad, float(reset_after_seconds), + branch_confirmation_frames, self.branch_confirmation_minimum_seconds, self.branch_equivalence_rad)) self.last_candidates_by_role: dict[ str, tuple[SquareTagPose, ...] ] = {} self.last_candidate_diagnostics_by_role: dict[ - str, dict[str, float | int] + str, dict[str, object] ] = {} - self.branch_correction_counts: dict[str, int] = {} + self.branch_correction_counts = self._branches.corrections + self._motion_candidates_by_role: dict[str, tuple[SquareTagPose, ...]] = {} + self._image_observations_by_role: dict[str, TrackerImageObservation] = {} + self._image_model_sha256_by_role: dict[str, str] = {} def reset(self) -> None: - self._previous.clear() - self.last_candidates_by_role.clear() - self.last_candidate_diagnostics_by_role.clear() - self.branch_correction_counts.clear() + with self._lock: + self._epoch += 1 + self._branches.reset() + self.last_candidates_by_role.clear() + self.last_candidate_diagnostics_by_role.clear() + self._motion_candidates_by_role.clear() + self._image_observations_by_role.clear() + self._image_model_sha256_by_role.clear() + + def observation_snapshot(self, role, stamp_ns): + """Copy one image's evidence atomically, never a subsequent callback.""" + with self._lock: + diagnostics = self.last_candidate_diagnostics_by_role.get(role, {}) + if diagnostics.get("observation_stamp_ns") != stamp_ns: + return None + return (self.last_candidates_by_role.get(role, ()), deepcopy(diagnostics), + self.branch_correction_counts.get(role, 0)) + + def motion_snapshot(self, role: str, stamp_ns: int) -> MotionTrackerSnapshot | None: + """Copy candidates and version metadata; callers solve outside locks.""" + with self._lock: + state = self._branches.states.get(role) + diagnostics = self.last_candidate_diagnostics_by_role.get(role, {}) + if state is None or diagnostics.get("observation_stamp_ns") != stamp_ns: + return None + return MotionTrackerSnapshot(self, role, self._epoch, stamp_ns, + state.revision, state.frozen, self._motion_candidates_by_role.get(role, ()), + state.pose, state.motion_evidence_id, diagnostics.get("motion_managed") is True, + self._image_observations_by_role.get(role)) + + @staticmethod + def _motion_snapshot_is_current(snapshot: MotionTrackerSnapshot) -> bool: + """Check metadata while the caller holds the corresponding tracker lock.""" + tracker = snapshot.tracker + state = tracker._branches.states.get(snapshot.role) + diagnostics = tracker.last_candidate_diagnostics_by_role.get(snapshot.role, {}) + return (tracker._epoch == snapshot.epoch and state is not None + and state.observed_ns == snapshot.stamp_ns and state.revision == snapshot.revision + and state.frozen == snapshot.frozen and state.motion_evidence_id == snapshot.evidence_id + and diagnostics.get("motion_managed") is True and snapshot.motion_managed + and diagnostics.get("motion_selection_committed") is not True) + + @staticmethod + def _motion_observation_snapshots(snapshots: Sequence[MotionTrackerSnapshot] + ) -> tuple[MotionObservationSnapshot, ...]: + """Detach diagnostics under the same locks used for a frame decision.""" + return tuple(MotionObservationSnapshot(snapshot.tracker, snapshot.role, snapshot.stamp_ns, + snapshot.tracker.last_candidates_by_role[snapshot.role], + deepcopy(snapshot.tracker.last_candidate_diagnostics_by_role[snapshot.role]), + snapshot.tracker.branch_correction_counts.get(snapshot.role, 0)) for snapshot in snapshots) + + @staticmethod + def reject_motion_frame(snapshots: Sequence[MotionTrackerSnapshot], reason: str + ) -> MotionFrameCommit: + """Discard one unresolved image without changing a reference or model.""" + snapshots = tuple(snapshots) + if not snapshots or not isinstance(reason, str) or not reason: + return MotionFrameCommit(False, "pose_motion_selection_invalid") + keys = {(snapshot.tracker, snapshot.role) for snapshot in snapshots} + if len(keys) != len(snapshots): + return MotionFrameCommit(False, "pose_motion_selection_duplicate_role") + with ExitStack() as stack: + for tracker in sorted({tracker for tracker, _ in keys}, key=id): + stack.enter_context(tracker._lock) + if not all(SquareTagPoseTracker._motion_snapshot_is_current(snapshot) for snapshot in snapshots): + return MotionFrameCommit(False, "pose_observation_superseded") + result_reason = reason + for snapshot in snapshots: + tracker, role = snapshot.tracker, snapshot.role + state = tracker._branches.states[role] + state.pending = None + if state.conflict: + result_reason = "pose_branch_conflict" + tracker._branches.reject(state, result_reason, "frozen_conflict") + else: + tracker._branches.reject(state, reason, "motion_unavailable") + diagnostics = tracker.last_candidate_diagnostics_by_role[role] + diagnostics.update(tracker._branches.diagnostics(role)) + diagnostics["motion_selection_committed"] = True + diagnostics["pose_constraint"] = "motion_geometry" + return MotionFrameCommit(False, result_reason, observation_snapshots= + SquareTagPoseTracker._motion_observation_snapshots(snapshots)) + + @staticmethod + def commit_motion_frame(selections: Sequence[MotionPoseSelection], *, + establish: bool = False) -> MotionFrameCommit: + """Commit a joint geometric decision without interleaving image metadata. + + No candidate evaluation or model fitting happens here. Every candidate + must have passed this tracker's original image and tilt gates; every + snapshot must still identify the same image and reference revision. + Existing evidence identities, especially frozen ones, are immutable. + """ + selections = tuple(selections) + if not selections: + return MotionFrameCommit(False, "pose_motion_selection_missing") + keys = {(item.snapshot.tracker, item.snapshot.role) for item in selections} + if len(keys) != len(selections): + return MotionFrameCommit(False, "pose_motion_selection_duplicate_role") + with ExitStack() as stack: + for tracker in sorted({tracker for tracker, _ in keys}, key=id): + stack.enter_context(tracker._lock) + for item in selections: + snapshot = item.snapshot + tracker = snapshot.tracker + if not SquareTagPoseTracker._motion_snapshot_is_current(snapshot): + return MotionFrameCommit(False, "pose_observation_superseded") + if (not isinstance(item.evidence_id, str) or not item.evidence_id + or item.pose not in snapshot.candidates + or item.pose not in tracker._motion_candidates_by_role.get(snapshot.role, ())): + return MotionFrameCommit(False, "pose_motion_selection_invalid") + if any(item.snapshot.role in item.snapshot.tracker._image_model_sha256_by_role + for item in selections): + return MotionFrameCommit(False, "pose_motion_solver_changed") + return SquareTagPoseTracker._commit_motion_selections_locked(selections, establish=establish) + + @staticmethod + def _commit_motion_selections_locked(selections, *, establish, image_decision=None): + """Share the recovery/identity transaction across both trusted solvers.""" + trials = [] + for item in selections: + tracker, role = item.snapshot.tracker, item.snapshot.role + original = tracker._branches.states[role] + trial = replace(original, + pending=None if original.pending is None else replace(original.pending)) + pose, reason = tracker._branches.select_motion(trial, item.pose, + item.evidence_id, establish=establish) + trials.append((tracker, role, trial, pose, reason)) + failure = next((reason for _, _, _, pose, reason in trials if pose is None), "") + for tracker, role, trial, pose, _ in trials: + diagnostics = tracker.last_candidate_diagnostics_by_role[role] + diagnostics["motion_selection_committed"] = True + # Recovery needs several images, even if another role is not yet + # ready. A successful role cannot advance or establish a new + # identity when the joint decision as a whole was rejected. + if failure and pose is not None: + continue + original = tracker._branches.states[role] + if (not failure and original.motion_evidence_id is None and original.pose is not None + and not tracker._branches.continuous(original.pose, trial.pose)): + tracker.branch_correction_counts[role] = tracker.branch_correction_counts.get(role, 0) + 1 + tracker._branches.states[role] = trial + diagnostics.update(tracker._branches.diagnostics(role)) + diagnostics["pose_constraint"] = "motion_geometry" + if image_decision is not None and not failure: + diagnostics.update({ + "pose_constraint": "image_motion_geometry", + "pose_source": "image_constrained_current_image", + "frozen_image_model": asdict(image_decision.model), + "image_model_sha256": image_decision.model_sha256, + "image_motion_quality": [asdict(item) for item in image_decision.quality], + }) + tracker._image_model_sha256_by_role[role] = image_decision.model_sha256 + observations = SquareTagPoseTracker._motion_observation_snapshots( + tuple(item.snapshot for item in selections)) + if failure: + return MotionFrameCommit(False, failure, observation_snapshots=observations) + return MotionFrameCommit(True, "", selections, observations) + + verify_image_motion_frame = staticmethod(verify_image_motion_frame) + commit_image_motion_frame = staticmethod(commit_image_motion_frame) + + def freeze_roles(self, expected_revisions, *, require_motion_evidence=False): + return self.freeze_references([(self, role, revision) for role, revision in expected_revisions.items()], + require_motion_evidence=require_motion_evidence) + + @staticmethod + def freeze_references(references, *, require_motion_evidence=False): + """Atomically freeze metadata across views after evidence validation. + + Callers may hold the session lock. No PnP work takes place under these + locks, and tracker code never acquires the session lock in reverse. + """ + references = tuple(references) + if not references: + return False + roles = {(tracker, role) for tracker, role, _ in references} + required = (roles if require_motion_evidence is True else set() + if require_motion_evidence is False else set(require_motion_evidence)) + if not required.issubset(roles): + return False + with ExitStack() as stack: + for tracker in sorted({tracker for tracker, _, _ in references}, key=id): + stack.enter_context(tracker._lock) + for tracker, role, revision in references: + state = tracker._branches.states.get(role) + if (type(revision) is not int or state is None or state.revision != revision or state.status != "tracking" + or state.pending is not None or state.conflict or state.recovery_required): + return False + if (tracker, role) in required and state.motion_evidence_id is None: + return False + for tracker, role, _ in references: + tracker._branches.states[role].frozen = True + tracker.last_candidate_diagnostics_by_role[role]["branch_frozen"] = True + return True def estimate( self, @@ -177,7 +450,15 @@ class SquareTagPoseTracker: camera_matrix: Sequence[Sequence[float]], stamp_ns: int, reprojection_tie_px: float | None = None, + fixed_reference: FixedTagReference | None = None, + motion_managed: bool = False, ) -> tuple[SquareTagPose | None, str]: + with self._lock: + epoch = self._epoch + image_observation = TrackerImageObservation(int(stamp_ns), + tuple(tuple(float(v) for v in point) for point in corners_xy), + tuple(tuple(float(v) for v in row) for row in camera_matrix), float(tag_size_m)) + corners_xy, camera_matrix = image_observation.corners_xy, image_observation.camera_matrix try: candidates = solve_square_tag_ippe( corners_xy, @@ -185,33 +466,7 @@ class SquareTagPoseTracker: camera_matrix=camera_matrix, ) except (ValueError, cv2.error): - self.last_candidates_by_role[str(role)] = () - self.last_candidate_diagnostics_by_role[str(role)] = { - "solved_candidate_count": 0, - "reprojection_candidate_count": 0, - "independent_tilt_candidate_count": 0, - "maximum_reprojection_error_px": float( - self.maximum_reprojection_error_px - ), - "maximum_independent_tilt_deg": math.degrees( - self.maximum_tag_tilt_rad - ), - } - return None, "pnp_solve_failed" - if not candidates: - self.last_candidates_by_role[str(role)] = () - self.last_candidate_diagnostics_by_role[str(role)] = { - "solved_candidate_count": 0, - "reprojection_candidate_count": 0, - "independent_tilt_candidate_count": 0, - "maximum_reprojection_error_px": float( - self.maximum_reprojection_error_px - ), - "maximum_independent_tilt_deg": math.degrees( - self.maximum_tag_tilt_rad - ), - } - return None, "pnp_solve_failed" + candidates = [] reprojection_candidates = [ candidate for candidate in candidates @@ -242,19 +497,12 @@ class SquareTagPoseTracker: # image detections. Group tracking therefore receives every # reprojection-valid candidate; independent tracking below retains the # original tilt safety gate. - self.last_candidates_by_role[str(role)] = tuple( - reprojection_candidates - ) - diagnostics: dict[str, float | int] = { + diagnostics: dict[str, object] = { + "motion_managed": bool(motion_managed and fixed_reference is None), + "motion_selection_committed": False, "solved_candidate_count": len(candidates), "reprojection_candidate_count": len(reprojection_candidates), "independent_tilt_candidate_count": len(independent_candidates), - "minimum_reprojection_error_px": float( - min( - candidate.reprojection_error_px - for candidate in candidates - ) - ), "maximum_reprojection_error_px": float( self.maximum_reprojection_error_px ), @@ -262,6 +510,8 @@ class SquareTagPoseTracker: self.maximum_tag_tilt_rad ), } + if candidates: + diagnostics["minimum_reprojection_error_px"] = min(c.reprojection_error_px for c in candidates) if candidate_tilts_rad: diagnostics["minimum_candidate_tilt_deg"] = math.degrees( min(candidate_tilts_rad) @@ -269,56 +519,44 @@ class SquareTagPoseTracker: diagnostics["maximum_candidate_tilt_deg"] = math.degrees( max(candidate_tilts_rad) ) - self.last_candidate_diagnostics_by_role[str(role)] = diagnostics - if not independent_candidates: - return None, "no_pose_within_reprojection_or_tilt_limit" - - previous_record = self._previous.get(str(role)) - previous: SquareTagPose | None = None - if previous_record is not None: - previous_stamp, previous_pose = previous_record - elapsed = int(stamp_ns) - previous_stamp - if 0 <= elapsed <= self.reset_after_ns: - previous = previous_pose - - selected, reason = select_continuous_pose( - independent_candidates, - previous=previous, - maximum_reprojection_error_px=( - self.maximum_reprojection_error_px - ), - reprojection_tie_px=( - self.reprojection_tie_px - if reprojection_tie_px is None - else float(reprojection_tie_px) - ), - maximum_pose_jump_rad=self.maximum_pose_jump_rad, - maximum_translation_jump_m=self.maximum_translation_jump_m, - maximum_tag_tilt_rad=self.maximum_tag_tilt_rad, - ) - if selected is not None: - if previous is not None: - rotation_jump = rotation_distance_rad( - previous.quaternion_xyzw, - selected.quaternion_xyzw, - ) - translation_jump = float( - np.linalg.norm( - np.asarray(selected.translation_xyz_m, dtype=float) - - np.asarray( - previous.translation_xyz_m, - dtype=float, - ) - ) - ) - if ( - rotation_jump > self.maximum_pose_jump_rad - or translation_jump - > self.maximum_translation_jump_m - ): - key = str(role) - self.branch_correction_counts[key] = ( - self.branch_correction_counts.get(key, 0) + 1 - ) - self._previous[str(role)] = (int(stamp_ns), selected) + tie = self.reprojection_tie_px if reprojection_tie_px is None else float(reprojection_tie_px) + _validate_reprojection_thresholds(self.maximum_reprojection_error_px, tie) + checked_fixed_pose = None + if fixed_reference is not None: + # Projection is outside the metadata lock, just like IPPE. Keep + # all current candidates as evidence, even when their ranking + # changes while the frozen fixed pose still explains the image. + diagnostics["pose_constraint"] = "fixed_reference" + try: + error = fixed_pose_reprojection_error(fixed_reference.pose, corners_xy, + tag_size_m=tag_size_m, camera_matrix=camera_matrix) + normal = Rotation.from_quat(fixed_reference.pose.quaternion_xyzw).as_matrix()[:, 2] + tilt = math.acos(float(np.clip(abs(normal[2]), 0.0, 1.0))) + diagnostics["fixed_reference_reprojection_error_px"] = error + if (error <= self.maximum_reprojection_error_px + and tilt <= self.maximum_tag_tilt_rad and independent_candidates): + checked_fixed_pose = replace(fixed_reference.pose, reprojection_error_px=error) + except (ValueError, cv2.error): + pass + diagnostics["fixed_reference_verified"] = checked_fixed_pose is not None + with self._lock: + if epoch != self._epoch: + return None, "pose_observation_superseded" + if fixed_reference is None and motion_managed: + selected, reason = self._branches.observe_motion(str(role), independent_candidates, int(stamp_ns)) + elif fixed_reference is None: + selected, reason = self._branches.update(str(role), independent_candidates, int(stamp_ns), tie) + else: + selected, reason = self._branches.update_fixed(str(role), fixed_reference, + checked_fixed_pose, int(stamp_ns)) + if reason == "pose_observation_not_new": + return None, reason + if not candidates and reason == "no_pose_within_reprojection_or_tilt_limit": + reason = "pnp_solve_failed" + self._branches.states[str(role)].reason = reason + diagnostics.update(self._branches.diagnostics(str(role))) + self.last_candidates_by_role[str(role)] = tuple(reprojection_candidates) + self._motion_candidates_by_role[str(role)] = tuple(independent_candidates) + self._image_observations_by_role[str(role)] = image_observation + self.last_candidate_diagnostics_by_role[str(role)] = diagnostics return selected, reason diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/acceptance.py b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/acceptance.py index 10cb4ca..022e310 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/acceptance.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/acceptance.py @@ -16,6 +16,11 @@ from .patch import validate_urdf_mimic_ranges from .validate import validate_runtime_curve_limits, validate_structural_urdf_diff +ANGLE_MAE_LIMIT_DEG = 1.0 +ANGLE_P95_LIMIT_DEG = 2.0 +ANGLE_MAXIMUM_LIMIT_DEG = 3.0 + + @dataclass(frozen=True) class JointHoldout: """Independent observation in the corrected coordinate convention. @@ -45,7 +50,9 @@ class ErrorMetrics: @property def passed(self) -> bool: - return self.count > 0 and self.mae_deg <= 1.0 and self.p95_deg <= 2.0 and self.maximum_deg <= 3.0 + return (self.count > 0 and self.mae_deg <= ANGLE_MAE_LIMIT_DEG + and self.p95_deg <= ANGLE_P95_LIMIT_DEG + and self.maximum_deg <= ANGLE_MAXIMUM_LIMIT_DEG) def angular_metrics(errors_rad: Sequence[float]) -> ErrorMetrics: @@ -59,8 +66,9 @@ def angular_metrics(errors_rad: Sequence[float]) -> ErrorMetrics: class SerializedJointMapping: """Read the numeric legacy v4/v6/v7 curve shapes without model imports. - Byte payloads preserve their deployed nearest-integer lookup; continuous - payloads interpolate explicit knots. Neither path silently clamps inputs. + Legacy byte tables and byte commands retain nearest-integer lookup. + Unified feedback uses its declared piecewise-linear knots, including + feedback interpolated to image time. Neither path silently clamps inputs. """ def __init__(self, payload: Mapping[str, Any], *, input_kind=None) -> None: @@ -68,6 +76,14 @@ class SerializedJointMapping: self.measured = payload.get("format") in {"unified_calibration_v3", "unified_calibration_report_v3"} self.compact = payload.get("format") in {"unified_calibration_v2", "unified_calibration_v3"} input_kind = input_kind or ("command" if self.compact else "feedback") + declared_directions = payload.get("directional_command_joints", []) + if (not isinstance(declared_directions, list) or any(not isinstance(n, str) for n in declared_directions) + or len(set(declared_directions)) != len(declared_directions) + or not set(declared_directions) <= payload.get("joints", {}).keys() + or declared_directions and not self.measured): + raise ValueError("invalid directional command declaration") + self.directional_joints = frozenset(declared_directions) if input_kind == "command" else frozenset() + self.baseline = payload.get("baseline_command", ()) self.unified = payload.get("format") in {"unified_calibration_v1", "unified_calibration_report_v3"} if self.compact: if input_kind != "command" or payload.get("schema_version") != (3 if self.measured else 2): @@ -78,11 +94,15 @@ class SerializedJointMapping: joints = {} for name, row in payload.get("joints", {}).items(): expected = {"sdk_channel", "angle_rad"} | ({"input_values"} if unit == "rad" else set()) + if name in self.directional_joints: + expected |= {"increasing_rad", "decreasing_rad"} index = row.get("sdk_channel") if set(row) != expected or type(index) is not int or index < 0: raise ValueError(f"invalid compact joint/channel fields:{name}") joints[name] = {"motor_index": index, "angle_rad": row["angle_rad"], "curve_input_knots_rad": row.get("input_values", ())} + if name in self.directional_joints: + joints[name].update({key: row[key] for key in ("increasing_rad", "decreasing_rad")}) payload = {"joints": joints, "curve_input_domain": f"command_{unit}"} elif self.unified: if input_kind not in {"command", "feedback"} or payload.get("schema_version") != (3 if self.measured else 1): @@ -153,6 +173,9 @@ class SerializedJointMapping: selected = direction if directions_by_joint is None else directions_by_joint.get(name, "") if selected not in {"increasing", "decreasing", ""}: raise ValueError("invalid per-joint SDK direction") + if name in self.directional_joints and not selected: + if index >= len(self.baseline) or value != self.baseline[index]: + raise ValueError(f"directional_command_requires_direction:{name}") if self.domain.endswith("_rad") and selected: increasing_branch = str(row.get("raw_increasing_curve_branch", "increasing")) if increasing_branch not in {"increasing", "decreasing"}: @@ -165,10 +188,10 @@ class SerializedJointMapping: angle = float(values[int(math.floor(value + 0.5))]) else: knots = row["curve_input_knots_rad"] - if self.domain.endswith("_u8"): - value = float(math.floor(value+0.5)) if not knots[0] <= value <= knots[-1]: raise ValueError(f"holdout input is outside serialized knot domain: {name}") + if self.domain == "command_u8": + value = float(math.floor(value+0.5)) angle = float(np.interp(value, knots, values)) output[name] = angle return output @@ -246,15 +269,41 @@ def validate_standard_urdf_holdout( def validate_artifact_structure(*, source_urdf: str | Path, corrected_urdf: str | Path, source_sha256: str, authorized_fields: Mapping[str, Sequence[str]], - payload: Mapping[str, Any], zero_offsets_rad: Mapping[str, float]) -> tuple[str, ...]: - """Validate physical ranges in CAD coordinates, including coordinate shifts.""" + payload: Mapping[str, Any], zero_offsets_rad: Mapping[str, float], + range_evidence=None) -> tuple[str, ...]: + """Validate source/safety bounds or exact trusted training motion ranges.""" if len(source_sha256) != 64 or hashlib.sha256(Path(source_urdf).read_bytes()).hexdigest() != source_sha256: raise ValueError("protected source URDF SHA256 mismatch") + before, after = UrdfKinematicModel(source_urdf), UrdfKinematicModel(corrected_urdf) + from .joint_control import independent_joint_sources, independent_export_authorization, validate_independent_output, uses_source_mimic + mapping = SerializedJointMapping(payload) + sources = independent_joint_sources(payload) + if sources is not None: + channels = {name: row["motor_index"] for name, row in mapping.joints.items()} + authorized_fields = independent_export_authorization(before, channels, sources, authorized_fields) + validate_independent_output(after, channels, sources) changes = validate_structural_urdf_diff(source_urdf, corrected_urdf, authorized_fields=authorized_fields, expected_source_sha256=source_sha256) - before, after = UrdfKinematicModel(source_urdf), UrdfKinematicModel(corrected_urdf) + from .limits import validate_range_evidence + motion_ranges = dict(range_evidence or {}) + if not set(motion_ranges) <= before.joints.keys(): + raise ValueError("range evidence references unknown joints") if not set(zero_offsets_rad) <= before.joints.keys(): raise ValueError("declared coordinate zero targets missing joints") + envelopes = {} + if uses_source_mimic(payload): + from .source_mimic import compile_source_mimic_envelopes, validate_source_mimic_elements, validate_serialized_mimic_envelopes + validate_source_mimic_elements(source_urdf, corrected_urdf) + envelopes = compile_source_mimic_envelopes(before, + {name: (j.lower, j.upper) for name, j in after.joints.items() if j.lower is not None}, + zero_offsets_rad, motion_ranges) + validate_serialized_mimic_envelopes(payload, after) + from dataclasses import asdict + for name, envelope in envelopes.items(): + declared = payload["urdf_correction"]["mimic_envelopes"][name] + if any(not np.allclose(values, declared[key], atol=1e-10, rtol=0) + for key, values in asdict(envelope).items()): + raise ValueError(f"source_mimic_envelope_differs_from_trusted_ranges:{name}") # No model bypass, no inherited-CAD excess allowance at final acceptance. validate_urdf_mimic_ranges(corrected_urdf) for name, joint in after.joints.items(): @@ -263,12 +312,14 @@ def validate_artifact_structure(*, source_urdf: str | Path, corrected_urdf: str if not math.isfinite(delta): raise ValueError("nonfinite coordinate zero") if joint.lower is not None and original.lower is not None: - if joint.lower + delta < original.lower - 1e-8 or joint.upper + delta > original.upper + 1e-8: + if name in motion_ranges: + interval = envelopes[name].base_limits_rad if name in envelopes else (joint.lower, joint.upper) + validate_range_evidence(name, original, delta, interval, motion_ranges[name]) + elif joint.lower + delta < original.lower - 1e-8 or joint.upper + delta > original.upper + 1e-8: raise ValueError(f"corrected range exceeds source mechanical range: {name}") expected = original.origin @ before._motion(original, delta) if not np.allclose(expected, joint.origin, atol=1e-8, rtol=0): raise ValueError(f"URDF origin does not match declared coordinate zero: {name}") - mapping = SerializedJointMapping(payload) if mapping.compact: validate_compact_urdf_tables(payload, after) for branch in ("angle_rad", "increasing_rad", "decreasing_rad"): @@ -280,6 +331,13 @@ def validate_artifact_structure(*, source_urdf: str | Path, corrected_urdf: str validate_runtime_curve_limits(corrected_urdf, {name: row[branch] for name, row in commands.joints.items()}) for name, row in payload["joints"].items(): + if sources is not None: + if row.get("passive") != (name in sources): + raise ValueError(f"passive_provenance_differs_from_source_urdf:{name}") + if name in sources and (row.get("mimic_approximation", {}).get("joint") != sources[name] + or row["mimic_approximation"].get("usage") != "diagnostic_only_not_exported"): + raise ValueError(f"diagnostic_mimic_source_differs:{name}") + continue if row.get("passive"): joint = after.joints[name] expected = {"joint": joint.mimic_joint, "multiplier": joint.mimic_multiplier, @@ -293,20 +351,31 @@ def validate_artifact_structure(*, source_urdf: str | Path, corrected_urdf: str def validate_compact_urdf_tables(payload, model): - """Redundant passive lookup entries must equal the written URDF, recursively.""" + """Validate curve ranges and the declared URDF angle-provider contract.""" mapping = SerializedJointMapping(payload, input_kind="command") + from .joint_control import independent_joint_sources, validate_independent_output, uses_affine_mimic_approximation, uses_source_mimic + sources = independent_joint_sources(payload) + preserved = uses_source_mimic(payload) + affine = uses_affine_mimic_approximation(payload) or preserved + if preserved: + from .source_mimic import validate_serialized_mimic_envelopes + validate_serialized_mimic_envelopes(payload, model) + if sources is not None: + validate_independent_output(model, {name: row["motor_index"] for name, row in mapping.joints.items()}, sources) moving = {name for name, joint in model.joints.items() if joint.kind != "fixed"} if set(mapping.joints) != moving: raise ValueError("compact JSON joints differ from the exported URDF") for name, row in mapping.joints.items(): joint = model.joints[name] values = np.asarray(row["angle_rad"]) - if joint.lower is not None and (np.min(values) < joint.lower-1e-9 or np.max(values) > joint.upper+1e-9): - raise ValueError(f"compact table exceeds exported URDF range:{name}") + for branch in ("angle_rad", "increasing_rad", "decreasing_rad"): + branch_values = np.asarray(row.get(branch, row["angle_rad"])) + if joint.lower is not None and (np.min(branch_values) < joint.lower-1e-9 or np.max(branch_values) > joint.upper+1e-9): + raise ValueError(f"compact table exceeds exported URDF range:{name}:{branch}") if joint.mimic_joint: parent = mapping.joints[joint.mimic_joint] if mapping.measured: - if row["motor_index"] != parent["motor_index"] or abs(joint.mimic_offset) > 1e-10: + if row["motor_index"] != parent["motor_index"] or (not affine and abs(joint.mimic_offset) > 1e-10): raise ValueError(f"v3 mimic channel or shared zero differs:{name}") continue if (row["motor_index"] != parent["motor_index"] @@ -323,6 +392,8 @@ def validate_compact_urdf_tables(payload, model): index = row["motor_index"] if index >= len(baseline): raise ValueError("v3 baseline channel missing") - value = mapping.evaluate(baseline, "", active_joints=(name,))[name] - if abs(value) > 1e-10: - raise ValueError(f"v3 baseline is not zero:{name}") + branches = ("", "increasing", "decreasing") if name in mapping.directional_joints else ("",) + for direction in branches: + value = mapping.evaluate(baseline, direction, active_joints=(name,))[name] + if abs(value) > 1e-10: + raise ValueError(f"v3 baseline is not zero:{name}:{direction}") diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/image_acceptance.py b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/image_acceptance.py new file mode 100644 index 0000000..4f4fbc7 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/image_acceptance.py @@ -0,0 +1,165 @@ +"""Validate frozen final-file motion and its independent image evidence. + +Primary measured poses must explain their original pixels. Their difference +from final-file motion is judged in degrees and metres, not by reusing the PnP +pixel limit for the sum of measurement and motion error. Extra views provide a +separate consistency check, never a substitute for independent 3-D accuracy. +No shared calibration parameter is estimated or changed during acceptance. +""" + +from dataclasses import asdict, dataclass + +import numpy as np +from scipy.spatial.transform import Rotation + +from ..fitting.tag_installation import rigid_matrix +from ..geometry.tag_pose.ippe import square_object_points +from ..geometry.tag_pose.parameters import DEFAULT_POSE_TRACKING_PARAMETERS +from .acceptance import SerializedJointMapping, angular_metrics +from .image_consistency import find_projection_witness, projection_error +from .kinematics import UrdfKinematicModel +from .joint_control import independent_joint_sources +from .tag_acceptance import POSITION_P95_LIMIT_M + + +FINAL_IMAGE_ACCEPTANCE_POLICY = "independent_measurement_and_physical_consistency_v2" + + +@dataclass(frozen=True) +class TagImageObservation: + sample_id: str + role: str + cycle: int + sdk_values: tuple[float, ...] + directions: tuple[tuple[str, str], ...] + view: str + tag_size_m: float + corners_xy: tuple[tuple[float, float], ...] + camera_matrix: tuple[tuple[float, float, float], ...] + view_from_common: tuple[tuple[float, ...], ...] + task_name: str = "" + + +class ImageHoldoutFailure(ValueError): + def __init__(self, report, *, input_kind, holdout_cycle): + self.report = report + self.input_kind = input_kind + self.holdout_cycle = holdout_cycle + failed = [role for role, metrics in report.items() if not metrics["passed"]] + super().__init__("final_image_holdout_failed:" + input_kind + ":" + ",".join(failed)) + + +def _physical_metrics(values): + angles = angular_metrics([angle for angle, _ in values]) + position = float(np.percentile([distance for _, distance in values], 95)) + return {**asdict(angles), "translation_p95_m": position, + "passed": angles.passed and position <= POSITION_P95_LIMIT_M} + + +def _pixel_metrics(values): + return {"rms_px": float(np.sqrt(np.mean(np.square(values)))), + "p95_px": float(np.percentile(values, 95)), "maximum_px": max(values)} + + +def _image_geometry(row): + if not np.isfinite(row.tag_size_m) or row.tag_size_m <= 0: + raise ValueError("final_image_holdout_size_invalid") + matrix, measured = np.asarray(row.camera_matrix), np.asarray(row.corners_xy) + if (matrix.shape != (3, 3) or measured.shape != (4, 2) + or not np.all(np.isfinite(matrix)) or not np.all(np.isfinite(measured)) + or matrix[0, 0] <= 0 or matrix[1, 1] <= 0 or not np.array_equal(matrix[2], [0, 0, 1])): + raise ValueError("final_image_holdout_projection_invalid") + return matrix, measured, square_object_points(row.tag_size_m) + + +def validate_serialized_image_holdout(*, corrected_urdf, payload, common_from_base, + installations, observations, independent_observations, required_roles, input_kind, + holdout_cycle, minimum_samples): + """Keep 1.5 px measurement accuracy and the original 3-D motion limits. + + Auxiliary witnesses only prove image compatibility within those limits. + They are grouped by motion task so stationary images cannot dilute the + errors in a different moving task. Independent poses remain mandatory. + """ + roles = frozenset(required_roles) + if (holdout_cycle not in {3, 5} or not roles or not roles <= installations.keys() + or minimum_samples < 1): + raise ValueError("final_image_holdout_contract_invalid") + model = UrdfKinematicModel(corrected_urdf) + mapping = SerializedJointMapping(payload, input_kind=input_kind) + active = tuple(name for name, joint in model.joints.items() + if joint.kind != "fixed" and (mapping.measured or joint.mimic_joint is None)) + base = rigid_matrix(common_from_base) + training_ids = {sample for mount in installations.values() for sample in mount.training_sample_ids} + independent = {} + for pose in independent_observations: + identity = (pose.role, pose.sample_id) + if (identity in independent or pose.role not in roles or not pose.sample_id + or pose.cycle != holdout_cycle or pose.sample_id in training_ids): + raise ValueError("final_image_holdout_independent_identity_invalid") + independent[identity] = pose + errors = {role: [] for role in roles} + motion = {role: [] for role in roles} + auxiliary = {role: {} for role in roles} + seen = set() + limit = DEFAULT_POSE_TRACKING_PARAMETERS.maximum_reprojection_error_px + for row in observations: + identity = (row.role, row.sample_id) + if (row.cycle != holdout_cycle or row.sample_id in training_ids or not row.sample_id + or identity in seen or row.role not in roles): + raise ValueError("final_image_holdout_identity_invalid") + seen.add(identity) + mount = installations[row.role] + if mount.role != row.role or mount.link not in model.links: + raise ValueError("final_image_holdout_installation_invalid") + angles = mapping.evaluate(row.sdk_values, "", active_joints=active, + directions_by_joint=dict(row.directions)) + link = np.eye(4) if mount.link == model.root_link else model.link_transform( + model.parent_joint_by_child[mount.link], zero_offsets={}, joint_angles=angles, + independent_mimic_angles=mapping.measured and independent_joint_sources(payload) is None) + predicted = rigid_matrix(row.view_from_common) @ base @ link @ rigid_matrix(mount.link_from_tag) + camera, pixels, objects = _image_geometry(row) + nominal = projection_error(predicted, camera, objects, pixels) + measured = independent.get(identity) + if measured is not None: + if (tuple(measured.sdk_values) != row.sdk_values + or dict(measured.sdk_directions_by_joint) != dict(row.directions)): + raise ValueError("final_image_holdout_independent_input_changed") + observed = rigid_matrix(row.view_from_common) @ rigid_matrix(measured.common_from_tag) + residual = projection_error(observed, camera, objects, pixels) + angle = float(Rotation.from_matrix(predicted[:3, :3].T @ observed[:3, :3]).magnitude()) + distance = float(np.linalg.norm(predicted[:3, 3]-observed[:3, 3])) + motion[row.role].append((angle, distance)) + else: + if not row.task_name: + raise ValueError("final_image_holdout_auxiliary_task_missing") + witness = find_projection_witness(predicted, camera, objects, pixels, image_limit_px=limit) + residual = witness.residual_px + auxiliary[row.role].setdefault(row.task_name, []).append(witness) + errors[row.role].append((row, nominal, residual)) + results = {} + for role in sorted(roles): + if len(motion[role]) < minimum_samples: + raise ValueError(f"final_image_holdout_missing:{role}:{len(motion[role])}") + values = errors[role] + residuals = [value[2] for value in values] + physical = _physical_metrics(motion[role]) + tasks = {} + for task, witnesses in auxiliary[role].items(): + metrics = _physical_metrics([(row.angle_rad, row.position_m) for row in witnesses]) + tasks[task] = {**metrics, "interpretation": "feasible_pose_displacement_only", + "optimizer_not_converged_count": sum(not row.optimizer_converged for row in witnesses), + "maximum_px": max(row.residual_px for row in witnesses)} + tasks[task]["passed"] = metrics["passed"] and tasks[task]["maximum_px"] <= limit + worst = values[int(np.argmax(residuals))][0] + results[role] = {"count": len(values), **_pixel_metrics(residuals), "limit_px": limit, + "nominal_projection": _pixel_metrics([value[1] for value in values]), + "independent_motion": physical, "auxiliary_consistency_by_task": tasks, + "worst_sample_id": worst.sample_id, "view": worst.view, + "passed": (max(residuals) <= limit and physical["passed"] + and all(value["passed"] for value in tasks.values()))} + if not independent.keys() <= seen: + raise ValueError("final_image_holdout_independent_source_missing") + if not all(metrics["passed"] for metrics in results.values()): + raise ImageHoldoutFailure(results, input_kind=input_kind, holdout_cycle=holdout_cycle) + return results diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/image_consistency.py b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/image_consistency.py new file mode 100644 index 0000000..b0b0fd3 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/image_consistency.py @@ -0,0 +1,109 @@ +"""Check whether extra-view pixels admit a pose within a physical error budget. + +A local pose is an existence witness, not an independently measured pose. It +never changes the base, mounting, camera, joints or exported calibration. The +caller must separately validate independent 3-D observations and check the +witness displacement against the same physical accuracy limits. +""" + +from dataclasses import dataclass + +import numpy as np +from scipy.optimize import minimize +from scipy.spatial.transform import Rotation + +from .acceptance import ANGLE_MAXIMUM_LIMIT_DEG +from .tag_acceptance import POSITION_P95_LIMIT_M + + +@dataclass(frozen=True) +class ProjectionConsistency: + nominal_px: float + residual_px: float + angle_rad: float + position_m: float + iterations: int + optimizer_converged: bool + + +def _cross_matrix(vector): + a, b, c = vector + return np.array([[0., -c, b], [c, 0., -a], [-b, a, 0.]]) + + +def _rigid_projection(x, pose, camera, objects, measured, angle_scale, position_scale): + """Pixel residual and analytic Jacobian for a Tag-centred rigid displacement.""" + vector = x[:3] * angle_scale + theta = float(np.linalg.norm(vector)) + skew = _cross_matrix(vector) + if theta < 1e-4: + left = np.eye(3) + (.5-theta**2/24)*skew + (1/6-theta**2/120)*(skew @ skew) + else: + left = (np.eye(3) + (1-np.cos(theta))/theta**2*skew + + (theta-np.sin(theta))/theta**3*(skew @ skew)) + rotated = objects @ (Rotation.from_rotvec(vector).as_matrix() @ pose[:3, :3]).T + points = rotated + pose[:3, 3] + x[3:] * position_scale + if not np.all(np.isfinite(points)) or np.any(points[:, 2] <= 0): + raise ValueError("final_image_holdout_projection_invalid") + homogeneous = points @ camera.T + pixels = homogeneous[:, :2] / homogeneous[:, 2:] + point_jacobian = np.empty((4, 3, 6)) + point_jacobian[:, :, :3] = [-_cross_matrix(point) @ left * angle_scale for point in rotated] + point_jacobian[:, :, 3:] = np.eye(3) * position_scale + projection = ((camera[:2, :][None] - pixels[:, :, None]*camera[2, :][None, None]) + / homogeneous[:, 2, None, None]) + return pixels-measured, np.einsum("nki,nij->nkj", projection, point_jacobian) + + +def projection_error(pose, camera, objects, measured): + residual, _ = _rigid_projection(np.zeros(6), pose, camera, objects, measured, 1., 1.) + return float(np.sqrt(np.mean(np.sum(residual**2, axis=1)))) + + +def find_projection_witness(pose, camera, objects, measured, *, image_limit_px): + """Find a feasible pose; optimizer success alone never establishes acceptance. + + The objective favours a small displacement in physical units. Its returned + displacement is not claimed to be globally minimal. The caller checks both + the directly evaluated pixel residual and the physical displacement. + """ + angle_scale = np.deg2rad(ANGLE_MAXIMUM_LIMIT_DEG) + position_scale = POSITION_P95_LIMIT_M + + def evaluate(x): + return _rigid_projection(x, pose, camera, objects, measured, angle_scale, position_scale) + + def rms(x): + residual, _ = evaluate(x) + return float(np.sqrt(np.mean(np.sum(residual**2, axis=1)))) + + zero = np.zeros(6) + nominal = rms(zero) + if nominal <= image_limit_px: + return ProjectionConsistency(nominal, nominal, 0., 0., 0, True) + # A small inward margin avoids accepting a constraint rounded above its + # limit. It does not enlarge either image or physical tolerances. + target = image_limit_px * (1.-1e-6) + + def constraint_jacobian(x): + residual, jacobian = evaluate(x) + return -2*np.mean(np.einsum("nk,nkj->nj", residual, jacobian), axis=0) + + constraint = {"type": "ineq", "fun": lambda x: target**2-rms(x)**2, + "jac": constraint_jacobian} + # Bounds limit numerical exploration, not the permitted physical error. + # Ensure every explored point remains in front of the image plane. + depth = pose[2, 3] - float(np.max(np.linalg.norm(objects, axis=1))) + lower_z = max(-5., -depth / position_scale * (1.-1e-6)) + if lower_z >= 0: + raise ValueError("final_image_holdout_projection_invalid") + bounds = [(-5., 5.)]*5 + [(lower_z, 5.)] + result = minimize(lambda x: float(x @ x), zero, jac=lambda x: 2*x, + constraints=constraint, bounds=bounds, method="SLSQP", + options={"maxiter": 60, "ftol": 1e-10}) + if not np.all(np.isfinite(result.x)): + raise ValueError("final_image_holdout_witness_nonfinite") + return ProjectionConsistency(nominal, rms(result.x), + float(np.linalg.norm(result.x[:3])*angle_scale), + float(np.linalg.norm(result.x[3:])*position_scale), + int(result.nit), bool(result.success)) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/joint_control.py b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/joint_control.py new file mode 100644 index 0000000..8951e8e --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/joint_control.py @@ -0,0 +1,78 @@ +"""Versioned roles of measured JSON and standard URDF mimic approximations. + +Production keeps mimic for standalone linear motion and uses explicit JSON +angles for measured motion. Version 2's mimic-free export remains readable; +its removal rights never authorize extra geometry or SDK actuation changes. +""" + +JSON_JOINT_ANGLES = "calibration_json" +AFFINE_MIMIC_APPROXIMATION = "range_bounded_affine_approximation" +PRESERVE_SOURCE_MIMIC = "preserve_source_mimic" + + +def uses_source_mimic(payload): + correction = payload.get("urdf_correction", {}) + if correction.get("schema_version") != 4: + return False + if (payload.get("format") not in {"unified_calibration_v3", "unified_calibration_report_v3"} + or correction.get("joint_angle_source") != JSON_JOINT_ANGLES + or correction.get("mimic_policy") != PRESERVE_SOURCE_MIMIC): + raise ValueError("unsupported_source_mimic_contract") + return True + + +def uses_affine_mimic_approximation(payload): + correction = payload.get("urdf_correction", {}) + if correction.get("schema_version") != 3: + return False + if (payload.get("format") not in {"unified_calibration_v3", "unified_calibration_report_v3"} + or correction.get("joint_angle_source") != JSON_JOINT_ANGLES + or correction.get("mimic_policy") != AFFINE_MIMIC_APPROXIMATION): + raise ValueError("unsupported_urdf_mimic_approximation_contract") + return True + + +def independent_joint_sources(payload): + """None denotes a URDF retaining its linear mimic constraints.""" + correction = payload.get("urdf_correction", {}) + version = correction.get("schema_version", 1) + if version == 1 or uses_affine_mimic_approximation(payload) or uses_source_mimic(payload): + return None + if (version != 2 + or payload.get("format") not in {"unified_calibration_v3", "unified_calibration_report_v3"} + or correction.get("joint_angle_source") != JSON_JOINT_ANGLES): + raise ValueError("unsupported_urdf_joint_control_contract") + sources = correction.get("passive_joint_sources") + if not isinstance(sources, dict) or any( + not isinstance(name, str) or not isinstance(parent, str) or name == parent + for name, parent in sources.items() + ): + raise ValueError("invalid_independent_passive_joint_sources") + return sources + + +def validate_joint_channels(model, channels, sources): + moving = {name for name, joint in model.joints.items() if joint.kind != "fixed"} + if set(channels) != moving or any(type(index) is not int or index < 0 for index in channels.values()): + raise ValueError("independent_joint_channel_coverage_differs") + for name, parent in sources.items(): + if name not in moving or parent not in moving or channels[name] != channels[parent]: + raise ValueError(f"independent_passive_sdk_channel_differs:{name}") + + +def independent_export_authorization(model, channels, sources, authorized_fields): + """Authorize only the exact source mimic removals, after binding validation.""" + original = {name: joint.mimic_joint for name, joint in model.joints.items() if joint.mimic_joint} + if sources != original: + raise ValueError("independent_passive_sources_differ_from_source_urdf") + validate_joint_channels(model, channels, sources) + rights = {name: tuple(fields) for name, fields in authorized_fields.items()} + for name in sources: + rights[name] = tuple(sorted(set(rights.get(name, ())) | {"mimic.remove"})) + return rights + + +def validate_independent_output(model, channels, sources): + validate_joint_channels(model, channels, sources) + if any(joint.mimic_joint is not None for joint in model.joints.values()): + raise ValueError("json_joint_angles_must_not_be_overridden_by_urdf_mimic") diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/limits.py b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/limits.py new file mode 100644 index 0000000..a7402ad --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/limits.py @@ -0,0 +1,102 @@ +"""Training-supported URDF motion envelopes and independent safety bounds. + +CAD limits remain hard bounds unless the protected Profile explicitly declares +them to be nominal motion envelopes. This module never changes SDK commands, +adds tolerance padding, fits a curve, or consumes validation observations. +""" + +from dataclasses import dataclass +import math + + +@dataclass(frozen=True) +class MotionRangeEvidence: + joint: str + source_joint: str + independently_measured: bool + source_limits_cad_rad: tuple[float, float] + output_limits_rad: tuple[float, float] + feedback_bounds_rad: tuple[float, float] + command_bounds_rad: tuple[float, float] + zero_offset_rad: float + baseline_command: float + training_cycles: tuple[int, ...] + training_sample_ids: tuple[str, ...] + reference_sample_ids: tuple[str, ...] + source_limits: str + evidence_source: str + independent_safety_limits_cad_rad: tuple[float, float] | None + safety_evidence_source: str + command_training_cycles: tuple[int, ...] = (0, 1, 2) + command_training_sample_ids: tuple[str, ...] = () + + +def _interval(value): + return len(value) == 2 and all(math.isfinite(v) for v in value) and value[0] < value[1] + + +def validate_range_evidence(name, original, delta, interval, evidence): + """Validate trusted fit evidence at both planning and final read-back.""" + if (not isinstance(evidence, MotionRangeEvidence) or evidence.joint != name + or evidence.source_limits != "calibrated_motion_envelope" or not evidence.evidence_source.strip() + or evidence.training_cycles != (0, 1, 2) or not evidence.training_sample_ids + or evidence.command_training_cycles not in {(0, 1, 2), (4,)} + or (evidence.command_training_cycles == (4,) and not evidence.command_training_sample_ids) + or not evidence.reference_sample_ids or not evidence.source_joint + or evidence.independently_measured != (evidence.source_joint == name) + or evidence.source_limits_cad_rad != (original.lower, original.upper) + or evidence.zero_offset_rad != delta + or not _interval(evidence.feedback_bounds_rad) or not _interval(evidence.command_bounds_rad)): + raise ValueError(f"invalid_training_motion_range_evidence:{name}") + expected = (min(0.0, evidence.feedback_bounds_rad[0], evidence.command_bounds_rad[0]), + max(0.0, evidence.feedback_bounds_rad[1], evidence.command_bounds_rad[1])) + if evidence.output_limits_rad != expected or not _interval(interval) \ + or any(abs(a-b) > 1e-10 for a, b in zip(interval, expected)): + raise ValueError(f"motion_range_differs_from_training_support:{name}") + safety = evidence.independent_safety_limits_cad_rad + if safety is None: + if evidence.safety_evidence_source: + raise ValueError(f"independent_safety_evidence_without_bounds:{name}") + elif (not _interval(safety) or not evidence.safety_evidence_source.strip() + or interval[0]+delta < safety[0]-1e-9 or interval[1]+delta > safety[1]+1e-9): + raise ValueError(f"measured_motion_exceeds_independent_safety_range:{name}") + + +def compile_motion_ranges(profile, model, fit): + """Freeze bounds from existing 3-cycle fitted mappings, without a refit. + + Copied curves retain donor image identities and never become independent + recipient measurements. All angular values already share the frozen zero. + """ + result = {} + from ..domain.sampling import command_training_cycles + command_cycles = command_training_cycles(profile) + for name, policy in profile.urdf_limit_policies.items(): + source = profile.measurement.transferred_motion_sources.get(name, name) + curve = fit.curves[source] + reference = fit.joint_zero_references[source] + command_evidence = fit.command_holdout_metrics[source] + if (tuple(curve.circle.get("training_cycles", ())) != (0, 1, 2) + or tuple(command_evidence.get("training_cycles", ())) != command_cycles + or not curve.circle.get("training_sample_ids") + or not reference.samples): + raise ValueError(f"motion_range_requires_training_provenance:{name}") + feedback, command = fit.output_mappings[name], fit.command_mappings[name] + if (feedback.transferred_from != (source if source != name else None) + or command.transferred_from != feedback.transferred_from + or abs(command.evaluate(reference.baseline_command)) > 1e-10): + raise ValueError(f"motion_range_requires_shared_baseline_and_donor:{name}") + bounds = (min(0.0, feedback.bounds_rad[0], command.bounds_rad[0]), + max(0.0, feedback.bounds_rad[1], command.bounds_rad[1])) + original = model.joints[name] + evidence = MotionRangeEvidence(name, source, source == name, + (original.lower, original.upper), bounds, feedback.bounds_rad, command.bounds_rad, + fit.zero_offsets_rad[name], reference.baseline_command, (0, 1, 2), + tuple(sorted(curve.circle["training_sample_ids"])), + tuple(sorted(f"{sample.view}:{sample.image_stamp_ns}" for sample in reference.samples)), + policy.source_limits, policy.evidence_source, policy.independent_safety_limits_cad_rad, + policy.safety_evidence_source, command_cycles, + tuple(command_evidence.get("training_sample_ids", ()))) + validate_range_evidence(name, original, fit.zero_offsets_rad[name], bounds, evidence) + result[name] = evidence + return result diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/measured_acceptance.py b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/measured_acceptance.py index 929b316..f79eb5c 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/measured_acceptance.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/measured_acceptance.py @@ -21,9 +21,15 @@ def validate_transferred_joint_tables(payload, measured_joints, transferred_moti or recipient["curve_input_knots_rad"] != donor["curve_input_knots_rad"] or baseline[recipient["motor_index"]] != baseline[donor["motor_index"]]): raise ValueError(f"transferred_final_table_differs_from_source:{target}") + for branch in ("increasing_rad", "decreasing_rad"): + if recipient.get(branch) != donor.get(branch): + raise ValueError(f"transferred_final_table_differs_from_source:{target}:{branch}") -def validate_measured_joint_holdout(payload, curves, observations, *, transferred_motion_sources=None): +def validate_measured_joint_holdout(payload, curves, observations, *, transferred_motion_sources=None, + holdout_cycle=3): + if holdout_cycle not in {3, 5}: + raise ValueError("command validation requires a declared independent partition") validate_transferred_joint_tables(payload, curves, transferred_motion_sources or {}) mapping = SerializedJointMapping(payload, input_kind="command") errors = {name: {d: [] for d in ("increasing", "decreasing")} for name in curves} @@ -31,13 +37,14 @@ def validate_measured_joint_holdout(payload, curves, observations, *, transferre unit = payload["input_unit"] for row in observations: name, identity = row["joint"], row["sample_id"] - if name not in curves or row["cycle"] != 3 or row.get("sample_phase") != "steady": + if name not in curves or row["cycle"] != holdout_cycle or row.get("sample_phase") != "steady": raise ValueError("v3 angle holdout has wrong partition or joint") if (name, identity) in seen or identity in curves[name].circle["training_sample_ids"]: raise ValueError("v3 angle holdout duplicates or overlaps training") seen.add((name, identity)) measured = measure_joint_curve_observation(curves[name], quaternion_xyzw=row["relative_quaternion_xyzw"]) - predicted = mapping.evaluate(row[f"command_vector_{unit}"], "", active_joints=(name,))[name] + predicted = mapping.evaluate(row[f"command_vector_{unit}"], + row["direction"] if name in mapping.directional_joints else "", active_joints=(name,))[name] errors[name][row["direction"]].append(predicted-measured) results = {} for name, branches in errors.items(): diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/patch.py b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/patch.py index 6189b7b..edeabcf 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/patch.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/patch.py @@ -2,7 +2,7 @@ Model profiles decide *what* values are authorized. This module owns the shared mechanics of locating those fields in the original XML text, changing -only the declared attributes, materializing mesh resources and atomically +only the declared attributes or explicit mimic removals, materializing mesh resources and atomically publishing a new file. """ @@ -16,7 +16,7 @@ from pathlib import Path import re import shutil import tempfile -from typing import Mapping, Sequence +from typing import Callable, Mapping, Sequence import xml.etree.ElementTree as ET import numpy as np @@ -25,13 +25,24 @@ from scipy.spatial.transform import Rotation @dataclass(frozen=True) class UrdfJointPatch: - """Authorized attribute replacements inside one top-level URDF joint.""" + """Attribute edits or explicit removal of one joint's linear mimic.""" origin_rpy: str | None = None limit_lower: str | None = None limit_upper: str | None = None mimic_multiplier: str | None = None mimic_offset: str | None = None + remove_mimic: bool = False + + def __post_init__(self) -> None: + if type(self.remove_mimic) is not bool or (self.remove_mimic and ( + self.mimic_multiplier is not None or self.mimic_offset is not None + )): + raise ValueError("mimic removal cannot also replace its coefficients") + + def requested_fields(self) -> tuple[str, ...]: + return tuple(f"{element}.{attribute}" for element, attribute, _ in self.replacements()) + ( + ("mimic.remove",) if self.remove_mimic else ()) def replacements(self) -> tuple[tuple[str, str, str], ...]: values = ( @@ -69,7 +80,7 @@ class UrdfPatchSet: def __post_init__(self) -> None: empty = [ - name for name, patch in self.joints.items() if not patch.replacements() + name for name, patch in self.joints.items() if not patch.requested_fields() ] if empty: raise ValueError( @@ -193,6 +204,12 @@ def apply_urdf_patch_text( block = match.group(0) for element, attribute, value in patch.replacements(): block = _replace_attribute(block, element, attribute, value) + if patch.remove_mimic: + if len(top_level_joints[name].findall("mimic")) != 1: + raise ValueError(f"mimic removal requires one original constraint:{name}") + block, count = re.subn(r"]*(?:/>|>\s*)", "", block) + if count != 1: + raise ValueError(f"could not uniquely remove mimic constraint:{name}") applied_joints.add(name) return block @@ -411,6 +428,7 @@ def write_urdf_patches( authorized_fields: Mapping[str, Sequence[str]] | None = None, validate_mimic_ranges: bool = True, expected_source_sha256: str | None = None, + validate_output: Callable[[Path], None] | None = None, ) -> Path: """Validate and atomically materialize one patched URDF.""" source = Path(source_urdf).expanduser().resolve() @@ -446,10 +464,7 @@ def write_urdf_patches( from .validate import validate_structural_urdf_diff requested_authorization = { - name: tuple( - f"{element}.{attribute}" - for element, attribute, _value in patch.replacements() - ) + name: patch.requested_fields() for name, patch in patches.joints.items() } authorization = ( @@ -481,6 +496,8 @@ def write_urdf_patches( # when calibration does not make it worse. if validate_mimic_ranges: validate_urdf_mimic_ranges(temporary, reference_urdf=source) + if validate_output is not None: + validate_output(temporary) materialize_relative_mesh_assets( source=source, output=destination.parent, urdf_root=root ) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/plan.py b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/plan.py index 7aefc5b..d945ce9 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/plan.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/plan.py @@ -13,6 +13,9 @@ from ..domain import CalibrationProfile from ..fitting.coupling import LinearMimicFit from .kinematics import UrdfKinematicModel, corrected_mimic_offset from .patch import UrdfJointPatch, UrdfPatchSet, corrected_origin_rpy, write_urdf_patches +from .limits import MotionRangeEvidence, validate_range_evidence +from .joint_control import independent_export_authorization, AFFINE_MIMIC_APPROXIMATION, PRESERVE_SOURCE_MIMIC +from .source_mimic import MimicEnvelope, compile_source_mimic_envelopes @dataclass(frozen=True) @@ -21,7 +24,8 @@ class StandardUrdfPlan: Limits are expressed in output coordinates. Mimic fits enter in CAD coordinates and are transformed exactly once here, including chains whose - parent is a passive joint. Nothing is inferred from JSON runtime curves. + parent is a passive joint. Current measured exports preserve source mimic + parameters as a standalone fallback; their JSON curves remain independent. """ source_sha256: str @@ -30,6 +34,10 @@ class StandardUrdfPlan: mimic_output: Mapping[str, tuple[str, float, float]] patches: UrdfPatchSet authorized_fields: Mapping[str, Sequence[str]] + range_evidence: Mapping[str, MotionRangeEvidence] = field(default_factory=dict) + independent_joint_sources: Mapping[str, str] | None = None + mimic_policy: str | None = None + mimic_envelopes: Mapping[str, MimicEnvelope] = field(default_factory=dict) def write(self, source_urdf: str | Path, destination_urdf: str | Path) -> Path: source = Path(source_urdf) @@ -50,22 +58,50 @@ def build_standard_correction_plan( fitted_mimics_cad: Mapping[str, LinearMimicFit] | None = None, transferred_zero_sources: Mapping[str, str] | None = None, require_shared_mimic_zero: bool = False, + range_evidence: Mapping[str, MotionRangeEvidence] | None = None, + independent_joint_channels: Mapping[str, int] | None = None, + fitted_mimics_output: Mapping[str, LinearMimicFit] | None = None, + preserve_source_mimic: bool = False, ) -> StandardUrdfPlan: """Generate authorized standard-URDF edits, never clamp an invalid fit. q_CAD = q_output + delta. Consequently both bounds shift by -delta and b_output = b_CAD + a*delta_parent - delta_child. Unobserved joints retain their own CAD geometry and physical range, including transferred joints. + An explicit training-supported motion envelope can replace nominal CAD + limits; separately declared hardware safety bounds remain mandatory. + With preserve_source_mimic, the original coefficients are kept verbatim + as an approximation in output coordinates, not rebased as a physical law. """ source = Path(source_urdf) if hashlib.sha256(source.read_bytes()).hexdigest() != source_sha256: raise ValueError("protected source URDF SHA256 mismatch") model = UrdfKinematicModel(source) + if preserve_source_mimic and (fitted_mimics_cad is not None or fitted_mimics_output is not None + or require_shared_mimic_zero or independent_joint_channels is not None): + raise ValueError("preserved_source_mimic_cannot_be_fitted_rebased_or_removed") + approximations = dict(fitted_mimics_output or {}) + if fitted_mimics_output is not None: + if fitted_mimics_cad or require_shared_mimic_zero or independent_joint_channels is not None: + raise ValueError("affine_mimic_export_requires_output_coordinate_approximations") + if set(approximations) != {name for name, joint in model.joints.items() if joint.mimic_joint}: + raise ValueError("affine_mimic_export_requires_all_source_couplings") + independent_sources = None + if independent_joint_channels is not None: + if fitted_mimics_cad or require_shared_mimic_zero: + raise ValueError("independent_joint_export_cannot_apply_linear_mimic_fits") + independent_sources = {name: joint.mimic_joint for name, joint in model.joints.items() + if joint.mimic_joint is not None} + authorized_fields = independent_export_authorization( + model, independent_joint_channels, independent_sources, authorized_fields) nodes = {str(node.get("name")): node for node in ET.parse(source).getroot().findall("joint")} offsets = {str(name): float(value) for name, value in zero_offsets_rad.items()} ranges = dict(measured_ranges_output_rad or {}) mimics = dict(fitted_mimics_cad or {}) - requested = set(offsets) | set(ranges) | set(mimics) + motion_ranges = dict(range_evidence or {}) + if not motion_ranges.keys() <= ranges.keys(): + raise ValueError("motion range evidence requires explicit output ranges") + requested = set(offsets) | set(ranges) | set(mimics) | set(approximations) if not requested <= model.joints.keys(): raise ValueError("correction references a missing URDF joint") for target, donor in (transferred_zero_sources or {}).items(): @@ -78,12 +114,17 @@ def build_standard_correction_plan( for name, value in offsets.items(): if not math.isfinite(value) or model.joints[name].kind not in {"revolute", "continuous"}: raise ValueError("zero corrections require finite revolute coordinates") + envelopes = {} + if preserve_source_mimic: + base_ranges = {name: tuple(ranges.get(name, (joint.lower-offsets.get(name, 0), + joint.upper-offsets.get(name, 0)))) for name, joint in model.joints.items() if joint.lower is not None} + envelopes = compile_source_mimic_envelopes(model, base_ranges, offsets, motion_ranges) edits: dict[str, UrdfJointPatch] = {} output_ranges: dict[str, tuple[float, float]] = {} output_mimics: dict[str, tuple[str, float, float]] = {} for name, joint in model.joints.items(): delta = offsets.get(name, 0.0) - fields: dict[str, str] = {} + fields: dict[str, str | bool] = {} if delta != 0: fields["origin_rpy"] = corrected_origin_rpy(nodes[name], delta) if joint.lower is not None: @@ -91,8 +132,14 @@ def build_standard_correction_plan( interval = tuple(float(v) for v in ranges.get(name, mechanical)) if len(interval) != 2 or not all(math.isfinite(v) for v in interval) or interval[0] >= interval[1]: raise ValueError(f"invalid corrected interval: {name}") - if interval[0] < mechanical[0] - 1e-9 or interval[1] > mechanical[1] + 1e-9: + if name in motion_ranges: + validate_range_evidence(name, joint, delta, interval, motion_ranges[name]) + elif interval[0] < mechanical[0] - 1e-9 or interval[1] > mechanical[1] + 1e-9: raise ValueError(f"corrected range exceeds source mechanical range: {name}") + if name in envelopes: + if interval != envelopes[name].base_limits_rad: + raise ValueError(f"source_mimic_base_range_differs_from_policy:{name}") + interval = envelopes[name].output_limits_rad output_ranges[name] = interval if interval[0] != joint.lower: fields["limit_lower"] = f"{interval[0]:.15g}" @@ -102,21 +149,24 @@ def build_standard_correction_plan( raise ValueError(f"cannot bound an unbounded joint without changing its contract: {name}") if name in mimics and joint.mimic_joint is None: raise ValueError(f"cannot create a new mimic topology: {name}") - if joint.mimic_joint is not None: - fit = mimics.get(name) + if joint.mimic_joint is not None and independent_sources is not None: + fields["remove_mimic"] = True + elif joint.mimic_joint is not None: + fit = approximations.get(name, mimics.get(name)) if fit is not None: if not isinstance(fit, LinearMimicFit) or fit.target_joint != name or fit.source_joint != joint.mimic_joint: raise ValueError(f"mimic fit does not match source URDF topology: {name}") if fit.training_cycles != (0, 1, 2) or not fit.residuals_rad or not all(math.isfinite(v) for v in fit.residuals_rad): raise ValueError(f"mimic fit lacks training-only evidence: {name}") - if not fit.offset_observed and not math.isclose(fit.offset_rad, joint.mimic_offset, rel_tol=0, abs_tol=1e-12): + if name not in approximations and not fit.offset_observed and not math.isclose(fit.offset_rad, joint.mimic_offset, rel_tol=0, abs_tol=1e-12): raise ValueError(f"unobservable passive static offset must retain CAD: {name}") multiplier, offset = fit.multiplier, fit.offset_rad else: multiplier, offset = joint.mimic_multiplier, joint.mimic_offset if not math.isfinite(multiplier) or not math.isfinite(offset): raise ValueError(f"nonfinite mimic fit: {name}") - shifted = corrected_mimic_offset(multiplier, offset, offsets.get(joint.mimic_joint, 0), delta) + shifted = offset if preserve_source_mimic or name in approximations else corrected_mimic_offset( + multiplier, offset, offsets.get(joint.mimic_joint, 0), delta) if require_shared_mimic_zero: if fit is None or not fit.offset_observed or abs(shifted) > 1e-12: raise ValueError(f"mimic lacks two supported baseline datums:{name}") @@ -129,7 +179,7 @@ def build_standard_correction_plan( if fields: patch = UrdfJointPatch(**fields) rights = set(authorized_fields.get(name, ())) - if any(f"{element}.{attribute}" not in rights for element, attribute, _ in patch.replacements()): + if not set(patch.requested_fields()) <= rights: raise ValueError(f"correction requests a field absent from Profile authorization: {name}") edits[name] = patch # Reject inconsistent full mimic ranges before creating any artifacts. @@ -155,7 +205,10 @@ def build_standard_correction_plan( for name in output_mimics: resolve(name) return StandardUrdfPlan(source_sha256, offsets, output_ranges, output_mimics, - UrdfPatchSet(edits), {name: tuple(fields) for name, fields in authorized_fields.items()}) + UrdfPatchSet(edits), {name: tuple(fields) for name, fields in authorized_fields.items()}, + motion_ranges, independent_sources, + PRESERVE_SOURCE_MIMIC if preserve_source_mimic else ( + AFFINE_MIMIC_APPROXIMATION if fitted_mimics_output is not None else None), envelopes) @dataclass(frozen=True) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/result_plan.py b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/result_plan.py index c523a65..4ee6d75 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/result_plan.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/result_plan.py @@ -71,19 +71,17 @@ def prepare_measured_result(profile, model, source_sha256, fit): validate_mapping_provenance(profile, fit) validate_zero_provenance(profile, fit) passive = {name for name, joint in model.joints.items() if joint.mimic_joint} - if set(fit.mimic_approximations) != passive: - raise ValueError("independent_release_missing_paired_mimic_observations") - mimics = {name: evidence.in_cad_coordinates(fit.zero_offsets_rad) - for name, evidence in fit.mimic_approximations.items()} - # Keep legal mechanical limits for passive approximations; do not expand - # them to disguise an invalid linear reachable range. + # Measured support and the original linear fallback have separate envelopes. + # Only explicit nominal-limit policies permit expansion beyond CAD bounds. ranges = {name: (min(fit.output_mappings[name].bounds_rad[0], fit.command_mappings[name].bounds_rad[0], 0), max(fit.output_mappings[name].bounds_rad[1], fit.command_mappings[name].bounds_rad[1], 0)) for name in moving - passive} + from .limits import compile_motion_ranges + range_evidence = compile_motion_ranges(profile, model, fit) + ranges.update({name: evidence.output_limits_rad for name, evidence in range_evidence.items()}) plan = build_standard_correction_plan(source_urdf=model.source, source_sha256=source_sha256, zero_offsets_rad=fit.zero_offsets_rad, measured_ranges_output_rad=ranges, - fitted_mimics_cad=mimics, authorized_fields=profile.urdf_authorized_fields, require_shared_mimic_zero=True) - for name, (_, _, offset) in plan.mimic_output.items(): - if abs(offset) > 1e-12: - raise ValueError(f"mimic_does_not_pass_shared_zero:{name}") + authorized_fields=profile.urdf_authorized_fields, + preserve_source_mimic=True, + range_evidence=range_evidence) return PreparedCalibration(plan, dict(fit.output_mappings)) diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/source_mimic.py b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/source_mimic.py new file mode 100644 index 0000000..395371e --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/source_mimic.py @@ -0,0 +1,120 @@ +"""Original CAD mimic fallback and its minimal representation envelopes. + +The measured JSON curves own actual joint motion. A retained CAD mimic is a +standalone approximation, including its original offset in output coordinates. +Its envelope is recorded separately from training evidence, never as additional +measured travel. Declared hardware bounds and default hard CAD bounds still apply. +""" + +from dataclasses import asdict, dataclass +import math +import xml.etree.ElementTree as ET + + +@dataclass(frozen=True) +class MimicEnvelope: + base_limits_rad: tuple[float, float] + mimic_reachable_limits_rad: tuple[float, float] + output_limits_rad: tuple[float, float] + + +def _resolve_envelopes(model, ranges, bases): + reachable, envelopes, visiting = {}, {}, set() + + def resolve(name): + if name in reachable: + return reachable[name] + if name in visiting: + raise ValueError(f"source_mimic_cycle:{name}") + if name not in model.joints: + raise ValueError(f"source_mimic_parent_missing:{name}") + visiting.add(name) + joint = model.joints[name] + if joint.mimic_joint is None: + if name not in ranges: + raise ValueError(f"source_mimic_requires_bounded_parent:{name}") + interval = ranges[name] + if (len(interval) != 2 or any(v is None or not math.isfinite(v) for v in interval) + or interval[0] >= interval[1]): + raise ValueError(f"invalid_source_mimic_parent_range:{name}") + else: + if not all(math.isfinite(v) for v in (joint.mimic_multiplier, joint.mimic_offset)): + raise ValueError(f"invalid_source_mimic_coefficients:{name}") + parent = resolve(joint.mimic_joint) + endpoints = [joint.mimic_multiplier*v+joint.mimic_offset for v in parent] + interval = (min(endpoints), max(endpoints)) + base = bases[name] + if len(base) != 2 or not all(math.isfinite(v) for v in base) or base[0] >= base[1]: + raise ValueError(f"invalid_source_mimic_base_range:{name}") + output = (min(base[0], interval[0]), max(base[1], interval[1])) + envelopes[name] = MimicEnvelope(base, interval, output) + visiting.remove(name) + reachable[name] = interval + return interval + + for name in bases: + resolve(name) + return envelopes + + +def compile_source_mimic_envelopes(model, ranges, offsets, motion_ranges): + """Compute from source coefficients and trusted ranges; never refit or pad.""" + bases = {} + for name, joint in model.joints.items(): + if joint.mimic_joint is None: + continue + if joint.lower is None: + raise ValueError(f"source_mimic_requires_bounded_child:{name}") + delta = offsets.get(name, 0.0) + bases[name] = (motion_ranges[name].output_limits_rad if name in motion_ranges else + (joint.lower-delta, joint.upper-delta)) + envelopes = _resolve_envelopes(model, ranges, bases) + for name, envelope in envelopes.items(): + evidence = motion_ranges.get(name) + delta = offsets.get(name, 0.0) + safety = (evidence.independent_safety_limits_cad_rad if evidence is not None else + (model.joints[name].lower, model.joints[name].upper)) + if safety is not None: + lo, hi = envelope.output_limits_rad + if lo+delta < safety[0]-1e-9 or hi+delta > safety[1]+1e-9: + raise ValueError(f"source_mimic_exceeds_safety_range:{name}:" + f"required_cad_rad=({lo+delta:.9g},{hi+delta:.9g}):" + f"allowed_cad_rad=({safety[0]:.9g},{safety[1]:.9g})") + return envelopes + + +def validate_source_mimic_elements(source, output): + """Even omitted coefficient attributes must stay omitted in new exports.""" + def elements(path): + return {node.get("name"): [ET.tostring(item, encoding="unicode").strip() + for item in node.findall("mimic")] + for node in ET.parse(path).getroot().findall("joint")} + if elements(source) != elements(output): + raise ValueError("exported_mimic_differs_from_source_urdf") + + +def validate_serialized_mimic_envelopes(payload, model): + """Check declared minimal envelopes against the actual exported model.""" + declared = payload["urdf_correction"].get("mimic_envelopes") + passive = {name for name, joint in model.joints.items() if joint.mimic_joint is not None} + if not isinstance(declared, dict) or set(declared) != passive: + raise ValueError("source_mimic_envelope_coverage_differs") + keys = {"base_limits_rad", "mimic_reachable_limits_rad", "output_limits_rad"} + for name, row in declared.items(): + if not isinstance(row, dict) or set(row) != keys: + raise ValueError(f"invalid_source_mimic_envelope:{name}") + for key, values in row.items(): + if (not isinstance(values, (list, tuple)) or len(values) != 2 + or any(type(v) not in (float, int) or not math.isfinite(v) for v in values) + or values[0] > values[1] + or (key != "mimic_reachable_limits_rad" and values[0] == values[1])): + raise ValueError(f"invalid_source_mimic_envelope:{name}:{key}") + ranges = {name: (j.lower, j.upper) for name, j in model.joints.items() if j.lower is not None} + expected = _resolve_envelopes(model, ranges, + {name: tuple(row["base_limits_rad"]) for name, row in declared.items()}) + for name, envelope in expected.items(): + for key, values in asdict(envelope).items(): + if any(abs(a-b) > 1e-10 for a, b in zip(values, declared[name][key])): + raise ValueError(f"source_mimic_envelope_differs:{name}:{key}") + if any(abs(a-b) > 1e-10 for a, b in zip(envelope.output_limits_rad, ranges[name])): + raise ValueError(f"source_mimic_envelope_differs_from_urdf:{name}") diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/tag_acceptance.py b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/tag_acceptance.py index 3dc72af..ca4add6 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/tag_acceptance.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/tag_acceptance.py @@ -1,7 +1,8 @@ """Replay raw fourth-cycle Tag poses with the *written* URDF and JSON. -Mounting transforms and base registration are frozen from training. Passive -poses are resolved only through the exported standard URDF mimic graph. +Mounting transforms and base registration are frozen from training. Measured +v3 motion uses the complete JSON mapping, including nonlinear passive joints; +standard URDF mimic is evaluated separately as an approximation. """ from __future__ import annotations @@ -16,6 +17,10 @@ from scipy.spatial.transform import Rotation from ..fitting.tag_installation import FrozenTagInstallation, rigid_matrix from .acceptance import SerializedJointMapping, angular_metrics from .kinematics import UrdfKinematicModel +from .joint_control import independent_joint_sources + + +POSITION_P95_LIMIT_M = 0.003 @dataclass(frozen=True) @@ -36,7 +41,10 @@ def validate_serialized_tag_holdout( input_kind: str = "feedback", mimic_approximation: bool = False, enforce_precision: bool = True, + holdout_cycle: int = 3, ) -> dict[str, dict[str, Any]]: + if holdout_cycle not in {3, 5}: + raise ValueError("Tag validation requires a declared independent partition") model = UrdfKinematicModel(corrected_urdf) mapping = SerializedJointMapping(payload, input_kind=input_kind) base = rigid_matrix(common_from_base) @@ -45,14 +53,16 @@ def validate_serialized_tag_holdout( raise ValueError("final Tag holdout requires declared frozen installations") active = tuple(name for name, joint in model.joints.items() if joint.kind != "fixed" and joint.mimic_joint is None) - independent = mapping.measured and not mimic_approximation + # Full measured motion must use the JSON angles rather than the approximate + # mimic. Correction version 2 is the historical mimic-free export. + independent = mapping.measured and not mimic_approximation and independent_joint_sources(payload) is None if independent: active = tuple(name for name, joint in model.joints.items() if joint.kind != "fixed") training_ids = {sample for mount in installations.values() for sample in mount.training_sample_ids} seen = set() errors: dict[str, list[tuple[float, float]]] = {role: [] for role in roles} for row in observations: - if row.cycle != 3 or not row.sample_id or row.sample_id in training_ids: + if row.cycle != holdout_cycle or not row.sample_id or row.sample_id in training_ids: raise ValueError("final Tag holdout overlaps training or has no image identity") if row.role not in roles: raise ValueError("final Tag holdout contains an undeclared Tag") @@ -83,7 +93,7 @@ def validate_serialized_tag_holdout( raise ValueError(f"missing independent Tag holdout samples:{role}:{len(values)}") metrics = angular_metrics([value[0] for value in values]) translation_p95 = float(np.percentile([value[1] for value in values], 95)) - if enforce_precision and (not metrics.passed or translation_p95 > 0.003): + if enforce_precision and (not metrics.passed or translation_p95 > POSITION_P95_LIMIT_M): raise ValueError(f"serialized_urdf_tag_holdout_failed:{role}:mae_deg={metrics.mae_deg}:p95_deg={metrics.p95_deg}:max_deg={metrics.maximum_deg}:translation_p95_m={translation_p95}") results[role] = {"count": metrics.count, "mae_deg": metrics.mae_deg, "p95_deg": metrics.p95_deg, "maximum_deg": metrics.maximum_deg, diff --git a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/validate.py b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/validate.py index 0aedf5a..987c127 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/core/urdf/validate.py +++ b/src/linkerhand_calibration/linkerhand_calibration/core/urdf/validate.py @@ -47,7 +47,7 @@ def validate_structural_urdf_diff( field for fields in allowed.values() for field in fields - if field not in URDF_FIELD_TO_XML + if field not in URDF_FIELD_TO_XML and field != "mimic.remove" } if unsupported: raise ValueError( @@ -118,6 +118,11 @@ def validate_structural_urdf_diff( changes.append(f"{current_joint}.{field}") left_children = list(left) right_children = list(right) + if (left.tag == "joint" and path.count("/") == 2 + and "mimic.remove" in allowed.get(current_joint, ()) + and len(left.findall("mimic")) == 1 and not right.findall("mimic")): + left_children = [child for child in left_children if child.tag != "mimic"] + changes.append(f"{current_joint}.mimic.remove") if len(left_children) != len(right_children): raise ValueError(f"URDF topology changed at {path}") for index, (left_child, right_child) in enumerate( diff --git a/src/linkerhand_calibration/linkerhand_calibration/hikrobot_camera.py b/src/linkerhand_calibration/linkerhand_calibration/hikrobot_camera.py index 225b523..1cd7262 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/hikrobot_camera.py +++ b/src/linkerhand_calibration/linkerhand_calibration/hikrobot_camera.py @@ -231,6 +231,7 @@ class HikrobotCameraNode(Node): self._closed = False self._frame_count = 0 self._timeout_count = 0 + self._camera_timing = None self.selector = str(self.get_parameter("serial_number").value) self.expected_model = str(self.get_parameter("expected_model").value) @@ -286,6 +287,7 @@ class HikrobotCameraNode(Node): self.declare_parameter("mvs_python_path", DEFAULT_MVS_PYTHON_PATH) self.declare_parameter("mvs_runtime_path", DEFAULT_MVS_RUNTIME_PATH) self.declare_parameter("grab_timeout_ms", 1000) + self.declare_parameter("timestamp_journal_path", "") def _load_calibration(self) -> CameraCalibration | None: if self.camera_info_path is None or not self.camera_info_path.exists(): @@ -407,6 +409,13 @@ class HikrobotCameraNode(Node): "set latest-frame grab strategy", camera.MV_CC_SetGrabStrategy(mvs.MV_GrabStrategy_LatestImagesOnly), ) + from .camera_timing import MvsCameraTiming + self._camera_timing = MvsCameraTiming(camera, mvs, + lambda: int(self.get_clock().now().nanoseconds), + exposure_us=0. if bool(self.get_parameter("auto_exposure").value) + else float(self.get_parameter("exposure_time_us").value), + journal_path=str(self.get_parameter("timestamp_journal_path").value)) + self._camera_timing.start() self._require_ok("start image acquisition", camera.MV_CC_StartGrabbing()) self._grabbing = True self.get_logger().info( @@ -464,6 +473,11 @@ class HikrobotCameraNode(Node): assert self._mvs is not None timeout_ms = int(self.get_parameter("grab_timeout_ms").value) while not self._stop_event.is_set(): + try: + self._camera_timing.refresh_if_due() + except ValueError as error: + self.get_logger().error(f"Camera timestamp source failed: {error}") + return frame = self._mvs.MV_FRAME_OUT() memset(byref(frame), 0, sizeof(frame)) result = self._camera.MV_CC_GetImageBuffer(frame, timeout_ms) @@ -506,7 +520,9 @@ class HikrobotCameraNode(Node): raise RuntimeError( f"frame contains {info.nFrameLen} bytes, expected {expected_size}" ) - stamp = self.get_clock().now().to_msg() + from rclpy.time import Time + received_ns = int(self.get_clock().now().nanoseconds) + stamp = Time(nanoseconds=self._camera_timing.timestamp(info, received_ns)).to_msg() image = Image() image.header.stamp = stamp image.header.frame_id = self.frame_id @@ -596,6 +612,8 @@ class HikrobotCameraNode(Node): self._stop_event.set() if self._grab_thread is not None: self._grab_thread.join(timeout=2.0) + if self._camera_timing is not None: + self._camera_timing.close() if self._camera is not None and self._camera_opened: if self._grabbing: result = self._camera.MV_CC_StopGrabbing() diff --git a/src/linkerhand_calibration/linkerhand_calibration/profiles/diagnostics.py b/src/linkerhand_calibration/linkerhand_calibration/profiles/diagnostics.py new file mode 100644 index 0000000..635e824 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/profiles/diagnostics.py @@ -0,0 +1,31 @@ +"""Temporary diagnostic task selections, declared beside hand Profiles.""" + +from dataclasses import dataclass + +from ..core.domain.profile import ProfileKey + + +@dataclass(frozen=True) +class DiagnosticCapturePreset: + profile_key: ProfileKey + tasks: tuple[tuple[str, tuple[str, ...]], ...] + label_zh: str = "诊断采集" + hold_command_values: tuple[float, ...] = () + hold_seconds: float = 0.0 + + +DIAGNOSTIC_CAPTURE_PRESETS = { + "o6_thumb": DiagnosticCapturePreset( + ProfileKey("O6", "right", "o6_right_8", 1), + (("thumb_yaw_top", ("rh_thumb_cmc_yaw",)), + ("thumb_pitch_ip_front", ("rh_thumb_cmc_pitch", "rh_thumb_ip"))), + label_zh="拇指短程诊断", + ), + "o6_yaw_hold": DiagnosticCapturePreset( + ProfileKey("O6", "right", "o6_right_8", 1), + (("thumb_yaw_top", ("rh_thumb_cmc_yaw",)),), + label_zh="拇指yaw方向差保持诊断", + hold_command_values=(64.0, 128.0, 192.0), + hold_seconds=10.0, + ), +} diff --git a/src/linkerhand_calibration/linkerhand_calibration/profiles/export.py b/src/linkerhand_calibration/linkerhand_calibration/profiles/export.py index cbdabe9..85b4674 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/profiles/export.py +++ b/src/linkerhand_calibration/linkerhand_calibration/profiles/export.py @@ -51,6 +51,9 @@ def hand_profile_payload(profile: CalibrationProfile) -> dict[str, Any]: "acquisition": _plain(asdict(profile.acquisition)), "urdf": { "authorized_fields": _plain(profile.urdf_authorized_fields), + **({"limit_policies": {name: _plain(asdict(policy)) + for name, policy in profile.urdf_limit_policies.items()}} + if profile.urdf_limit_policies else {}), }, "joint_coverage": _plain(profile.joint_coverage), } diff --git a/src/linkerhand_calibration/linkerhand_calibration/profiles/loader.py b/src/linkerhand_calibration/linkerhand_calibration/profiles/loader.py index d331ca3..29c4e05 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/profiles/loader.py +++ b/src/linkerhand_calibration/linkerhand_calibration/profiles/loader.py @@ -8,7 +8,7 @@ import re from typing import Any, Mapping import yaml -from ..core.domain.profile import ReferenceWaypoint, JointZeroSpec, KnownBaselineGeometry +from ..core.domain.profile import ReferenceWaypoint, JointZeroSpec, KnownBaselineGeometry, JointLimitPolicy from ..core import ( AcquisitionPolicy, @@ -56,7 +56,9 @@ def _validate_keys(payload): ("quality", QualityPolicy), ("scope", ScopePolicy), ("artifacts", ArtifactPolicy), ("acquisition", AcquisitionPolicy)): _keys(payload.get(name, {}), {f.name for f in fields(contract)}, name) - _keys(payload.get("urdf", {}), {"authorized_fields"}, "urdf") + _keys(payload.get("urdf", {}), {"authorized_fields", "limit_policies"}, "urdf") + for policy in payload.get("urdf", {}).get("limit_policies", {}).values(): + _keys(policy, {f.name for f in fields(JointLimitPolicy)}, "URDF limit policy") for view in payload.get("vision", {}).get("views", ()): _keys(view, {"name", "tags"}, "view") for tag in view.get("tags", ()): @@ -318,6 +320,7 @@ def load_hand_profile(path: str | Path) -> CalibrationProfile: str(artifacts.get("publication_pointer", "latest_passed")), _set(artifacts.get("session_compatibility_tokens")), bool(artifacts.get("publish_corrected_urdf", False)), + _set(artifacts.get("directional_command_joints")), ), acquisition=AcquisitionPolicy(**dict(acquisition)), sdk_adapter=str(payload["sdk_adapter"]), @@ -333,6 +336,12 @@ def load_hand_profile(path: str | Path) -> CalibrationProfile: payload.get("joint_coverage", {}), "joint_coverage" ).items() }, + urdf_limit_policies={str(name): JointLimitPolicy( + source_limits=str(row["source_limits"]), evidence_source=str(row["evidence_source"]), + independent_safety_limits_cad_rad=(None if row.get("independent_safety_limits_cad_rad") is None + else tuple(float(v) for v in row["independent_safety_limits_cad_rad"])), + safety_evidence_source=str(row.get("safety_evidence_source", ""))) + for name, row in _map(urdf.get("limit_policies", {}), "urdf.limit_policies").items()}, ) validate_profile(profile) return profile diff --git a/src/linkerhand_calibration/linkerhand_calibration/profiles/observations.py b/src/linkerhand_calibration/linkerhand_calibration/profiles/observations.py index cccb03e..5d2934c 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/profiles/observations.py +++ b/src/linkerhand_calibration/linkerhand_calibration/profiles/observations.py @@ -42,3 +42,38 @@ def compile_tag_links(profile: CalibrationProfile, model: UrdfKinematicModel) -> raise ValueError(f"Tag is assigned to different kinematic branches:{tag.role}") bind(tag.role, model.joints[descendants[0]].child) return result + + +def compile_parallel_axis_geometry(profile, model, relations): + """Read invariant geometry only when both Tags identify adjacent links.""" + import numpy as np + from ..core.geometry.tag_pose.cad_hinge import ParallelAxisGeometry + + links = compile_tag_links(profile, model) + by_child_role = {relation.child_role: relation for relation in relations} + result = [] + for child in relations: + parent = by_child_role.get(child.parent_role) + if parent is None: + continue + parent_name = profile.command.urdf_joint_by_joint.get(parent.joint, parent.joint) + child_name = profile.command.urdf_joint_by_joint.get(child.joint, child.joint) + first, second = model.joints[parent_name], model.joints[child_name] + if (first.kind not in {"revolute", "continuous"} or second.kind not in {"revolute", "continuous"} + or links[parent.child_role] != first.child or links[child.child_role] != second.child): + continue + chain = model._chain(child_name) + positions = [i for i, joint in enumerate(chain) if joint.name == parent_name] + if not positions or any(joint.kind != "fixed" for joint in chain[positions[0]+1:-1]): + continue + a, b = (model.link_transform(name, zero_offsets={}, joint_angles={}, independent_mimic_angles=True) + for name in (parent_name, child_name)) + axis_a, axis_b = a[:3, :3] @ first.axis, b[:3, :3] @ second.axis + if np.linalg.norm(np.cross(axis_a, axis_b)) > 1e-8: + continue + displacement = b[:3, 3] - a[:3, 3] + distance = float(np.linalg.norm(displacement - axis_a * (axis_a @ displacement))) + if distance > 1e-6: + result.append(ParallelAxisGeometry(parent.joint, child.joint, + 1 if axis_a @ axis_b > 0 else -1, distance)) + return tuple(result) diff --git a/src/linkerhand_calibration/linkerhand_calibration/profiles/validator.py b/src/linkerhand_calibration/linkerhand_calibration/profiles/validator.py index 5a6adb1..758bef8 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/profiles/validator.py +++ b/src/linkerhand_calibration/linkerhand_calibration/profiles/validator.py @@ -6,7 +6,18 @@ from ..core.urdf.kinematics import UrdfKinematicModel from .observations import compile_tag_links +def require_release_geometry(report): + """Reject structurally impossible releases before acquiring any images.""" + coverage = report["independent_measurement_coverage"] + missing_motion = coverage["unresolved_motion_joints"] + missing_zero = coverage["missing_baseline_geometry"] + if missing_motion or missing_zero: + raise ValueError(f"release_geometry_incomplete:motion={missing_motion}:baseline={missing_zero}") + + def validate_executable_profile(profile, source_urdf): + if profile.artifacts.output_schema_version not in {2, 3}: + raise ValueError("production calibration requires unified output_schema_version: 2 or 3; legacy formats are read-only") validate_profile(profile) if not profile.command.sdk_to_joint_direction: raise ValueError("executable Profile must declare fixed SDK-to-joint directions") @@ -26,8 +37,6 @@ def validate_executable_profile(profile, source_urdf): raise ValueError(f"unsupported measurement primitive:{name}:{spec.kind}") if not set(profile.zero.axis_joints) <= measured: raise ValueError("spatial observer axes require declared measured tasks") - if profile.artifacts.output_schema_version not in {2, 3}: - raise ValueError("production calibration requires unified output_schema_version: 2 or 3; legacy formats are read-only") if profile.artifacts.output_schema_version == 3: required = {name for task in profile.motion.tasks for name in task.joints} if required != set(profile.motion.joint_zero_references): @@ -39,6 +48,9 @@ def validate_executable_profile(profile, source_urdf): _validate_transfer_geometry(profile, model) if not set(profile.urdf_authorized_fields) <= model.joints.keys(): raise ValueError("URDF authorization references a missing joint") + for name in profile.urdf_limit_policies: + if name not in model.joints or model.joints[name].lower is None: + raise ValueError(f"calibrated motion envelope requires an existing bounded CAD joint:{name}") conflicts = [] for name, joint in model.joints.items(): if joint.mimic_joint is None or joint.lower is None: @@ -71,6 +83,9 @@ def validate_executable_profile(profile, source_urdf): "cad_zero_assumptions": dict(profile.zero.cad_zero_assumptions), "transferred": dict(profile.zero.transferred_zero_sources), "cad_constraint_conflicts": conflicts, + "calibrated_motion_envelope_joints": sorted(profile.urdf_limit_policies), + "independent_angular_safety_limits_declared": sorted(name for name, policy + in profile.urdf_limit_policies.items() if policy.independent_safety_limits_cad_rad is not None), "observability": "structural_checks_only; numerical_rank_and_holdout_required", "arbitrary_multiaxis_validated": False} diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/adapters/base.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/adapters/base.py index 107aecc..8a8ece7 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/adapters/base.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/adapters/base.py @@ -14,6 +14,23 @@ from typing import Protocol, Sequence, runtime_checkable from ...core import CommandLayout +def ordered_feedback_values(layout, names, values): + """Apply the same channel order to positions and their measurement times.""" + if len(values) != layout.command_count: + return None + if not names or layout.feedback_by_index: + return tuple(values) + if len(names) != len(values) or len(set(names)) != len(names): + return None + by_name = dict(zip((str(name) for name in names), values)) + for alias, canonical in layout.feedback_name_aliases.items(): + if alias in by_name and canonical not in by_name: + by_name[canonical] = by_name[alias] + if any(name not in by_name for name in layout.names): + return None + return tuple(by_name[name] for name in layout.names) + + @dataclass(frozen=True) class HardwareHealth: connected: bool @@ -77,29 +94,13 @@ class ProfileSdkAdapter: def parse_feedback( self, names: Sequence[str], values: Sequence[float] ) -> tuple[float, ...] | None: - layout = self.command_layout - if len(values) != layout.command_count: + ordered = ordered_feedback_values(self.command_layout, names, values) + if ordered is None: return None - if names and not layout.feedback_by_index and ( - len(names) != len(values) or len(set(names)) != len(names) - ): + try: + parsed = tuple(float(value) for value in ordered) + except (ValueError, TypeError, OverflowError): return None - if names and not layout.feedback_by_index: - by_name = dict(zip((str(name) for name in names), values)) - for alias, canonical in layout.feedback_name_aliases.items(): - if alias in by_name and canonical not in by_name: - by_name[canonical] = by_name[alias] - if any(name not in by_name for name in layout.names): - return None - try: - parsed = tuple(float(by_name[name]) for name in layout.names) - except (ValueError, TypeError, OverflowError): - return None - else: - try: - parsed = tuple(float(value) for value in values) - except (ValueError, TypeError, OverflowError): - return None return parsed if all(math.isfinite(value) for value in parsed) else None def validate_command(self, values: Sequence[float]) -> tuple[float, ...]: diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/controller.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/controller.py index 0d82938..9e83166 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/controller.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/controller.py @@ -25,6 +25,10 @@ class FinalizationInputs: source_urdf: Path protected_inputs: Mapping[str, str] records: tuple[dict[str, Any], ...] + require_motion_evidence: bool = False + journal_path: Path | None = None + journal_size: int | None = None + camera_extrinsics_file: Path | None = None class FinalizationProtocolError(RuntimeError): @@ -32,17 +36,29 @@ class FinalizationProtocolError(RuntimeError): class FinalizationController: - def __init__(self, profile, session_dir: Path, *, finalizer=finalize_profile_session): + def __init__(self, profile, session_dir: Path, *, finalizer=finalize_profile_session, isolated=False): self.worker = FinalizationWorker() self._events: SimpleQueue[str] = SimpleQueue() + self._isolated = isolated + if isolated: + from .process_finalizer import isolated_finalize_profile_session + finalizer = partial(isolated_finalize_profile_session, finalizer=finalizer) self._finalizer = partial(finalizer, profile=profile, phase_changed=self._events.put) self._publisher = ArtifactPublisher(session_dir.parent, profile.artifacts.publication_pointer) self.started = False def start(self, inputs: FinalizationInputs) -> None: + transport = {} + if inputs.camera_extrinsics_file is not None: + transport["camera_extrinsics_file"] = inputs.camera_extrinsics_file + if self._isolated: + if inputs.journal_path is None or inputs.journal_size is None: + raise ValueError("isolated_finalization_requires_synced_journal") + transport.update(journal_path=inputs.journal_path, journal_size=inputs.journal_size) self.worker.start(self._finalizer, session_dir=inputs.session_dir, serial_number=inputs.serial_number, source_urdf=inputs.source_urdf, - protected_inputs=dict(inputs.protected_inputs), records=inputs.records) + protected_inputs=dict(inputs.protected_inputs), records=inputs.records, + require_motion_evidence=inputs.require_motion_evidence, **transport) self.started = True def drain_events(self, on_event: Callable[[str], None]) -> None: diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/direction.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/direction.py new file mode 100644 index 0000000..b2e807d --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/direction.py @@ -0,0 +1,38 @@ +"""Direction state for tables measured on complete endpoint-to-endpoint sweeps.""" + +from math import floor + + +def next_input_directions(values, previous, directions, *, resolution, channels, byte_input=False): + """Pure transition: callers commit it only after every mapping check passes. + + A hold keeps its arrival direction. Initial state requires the recorded + baseline; an interior reversal needs unmeasured minor-loop hysteresis and + is rejected. An endpoint reset is the scope actually covered by calibration. + """ + result = list(directions) + for index, (baseline, lower, upper) in channels.items(): + if previous is None: + if values[index] != baseline: + raise ValueError(f"directional_mapping_requires_baseline_initialization:channel={index}") + if baseline not in (lower, upper): + raise ValueError("directional_mapping_requires_endpoint_baseline") + result[index] = "increasing" if baseline == upper else "decreasing" + if previous is not None: + for index, (old, new) in enumerate(zip(previous, values)): + tolerance = resolution + if byte_input and index in channels: + # The serialized byte table uses floor(v + .5). Compare the + # same effective inputs so small float steps cannot accumulate + # into a changed table index with a stale travel direction. + old, new = floor(old+.5), floor(new+.5) + tolerance = 0 + if abs(new-old) <= tolerance: + continue + direction = "increasing" if new > old else "decreasing" + if index in channels and direction != result[index]: + _, lower, upper = channels[index] + if min(abs(old-lower), abs(old-upper)) > tolerance: + raise ValueError(f"directional_mapping_interior_reversal_unvalidated:channel={index}") + result[index] = direction + return result diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/evidence.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/evidence.py index 5f73dab..9b7aaac 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/evidence.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/evidence.py @@ -32,13 +32,42 @@ def _pose(value): return matrix_tuple(value) +def _replay_directions(mappings, row, task, *, directions_are_task_relative=False): + """A held ancestor retains its arrival branch in both replay paths.""" + direction = row["direction"] + if directions_are_task_relative and task.end_value > task.start_value: + direction = {"increasing": "decreasing", "decreasing": "increasing"}[direction] + if direction not in {"increasing", "decreasing"}: + raise ValueError("Tag replay requires an explicit scan direction") + # Older CaptureFrame records encode unavailable history as an empty + # vector. Preserve that explicit unknown state; a nonempty partial vector + # is damaged evidence and must not silently change only some branches. + history = row.get("command_direction_by_index") + if history is not None and not isinstance(history, (list, tuple)): + raise ValueError("Tag replay has an invalid SDK direction history") + if history: + covered = len(history) > max(m.motor_index for m in mappings.values()) + valid = all(value in {"", "increasing", "decreasing"} for value in history) + if not covered or not valid: + raise ValueError("Tag replay has an invalid SDK direction history") + directions = {} + for joint, mapping in mappings.items(): + if mapping.motor_index == task.command_index: + directions[joint] = direction + else: + directions[joint] = history[mapping.motor_index] if history else "" + return directions + + def prepare_command_holdout(profile, mappings, records): - """Raw steady fourth-cycle poses; no registration or mounting fit here.""" + """Raw independent command-validation poses; no registration or fit here.""" + from ...core.domain.sampling import command_holdout_cycle + holdout_cycle = command_holdout_cycle(profile) specs = profile.measurement.measurements tasks = {task.key: task for task in profile.motion.tasks} seen, result = set(), [] for row in records: - if row["cycle"] != 3: + if row["cycle"] != holdout_cycle: continue if row.get("sample_phase") != "steady": raise ValueError("command holdout requires stable command observations") @@ -48,13 +77,8 @@ def prepare_command_holdout(profile, mappings, records): if identity in seen: continue seen.add(identity) - directions = {joint: row["direction"] for joint, m in mappings.items() - if m.motor_index == task.command_index} - for joint, mapping in mappings.items(): - previous = row.get("command_direction_by_index", ()) - if mapping.motor_index != task.command_index and len(previous) > mapping.motor_index: - directions[joint] = previous[mapping.motor_index] - result.append(TagHoldout(row["sample_id"], spec.child_role, 3, + directions = _replay_directions(mappings, row, task) + result.append(TagHoldout(row["sample_id"], spec.child_role, holdout_cycle, tuple(row[f"command_vector_{profile.command.unit}"]), directions, _pose(row["child_pose_common"]))) return tuple(result) @@ -105,11 +129,8 @@ def prepare_tag_replay( input_domain = next(iter(domain)) field = f"state_{profile.command.unit}" if input_domain.startswith("feedback_") else f"command_vector_{profile.command.unit}" sdk = tuple(float(value) for value in row[field]) - direction = str(row["direction"]) - if directions_are_task_relative and task.end_value > task.start_value: - direction = {"increasing": "decreasing", "decreasing": "increasing"}[direction] - directions = {joint: direction for joint, mapping in output_mappings.items() - if mapping.motor_index == task.command_index} + directions = _replay_directions(output_mappings, row, task, + directions_are_task_relative=directions_are_task_relative) pose = _pose(row["child_pose_common"]) cycle = int(row["cycle"]) key = (role, identity) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/finalization.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/finalization.py index b5fceac..3941ebb 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/finalization.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/finalization.py @@ -10,11 +10,13 @@ from ...core.artifacts.storage import atomic_write_json from ...core.fitting.session import fit_profile_calibration from ...core.fitting.command_mapping import fit_command_mappings from ...core.fitting.motion_fit import channel_for_joint +from ...core.fitting.rotation_diagnostics import RotationHoldoutFailure from ...core.geometry.candidate_selection import resolve_chain_observations from ...core.geometry.extrinsics import transform_matrix from ...core.urdf.kinematics import UrdfKinematicModel from ...core.urdf.result_plan import prepare_standard_result from ..acquisition import accepted_joint_records, accepted_secondary_records +from ..diagnostic_capture import reject_diagnostic_capture from .evidence import prepare_tag_replay from .publisher import ArtifactPublisher, FrozenTagArtifactValidator, StagedRelease from .serializers.compact_v2 import from_report, REPORT_FILENAME @@ -28,47 +30,96 @@ class CorrectedArtifact: staged_release: StagedRelease +def _write_artifact_pair(profile, fit, prepared, directory, source, serial_number, protected_inputs): + """Save the calibration first, then rebuild its URDF from those bytes. + + These are reviewable candidate files. Only independent final-file + acceptance and the publication controller can create a valid release. + """ + from .urdf_from_json import serialize_urdf_correction, rebuild_urdf_from_json + report_writer, command_writer = serialize_report, from_report + if profile.artifacts.output_schema_version >= 3: + from .serializers.measured_v3 import serialize_report as report_writer, from_report as command_writer + report = report_writer(profile, fit, prepared, serial_number=serial_number, protected_inputs=protected_inputs) + report["urdf_correction"] = serialize_urdf_correction(prepared.plan) + if prepared.plan.range_evidence: + report["motion_range_evidence"] = {name: asdict(item) for name, item in prepared.plan.range_evidence.items()} + payload = command_writer(report) + naming = {"serial_number": serial_number, "side": profile.key.side, "model": profile.key.model.lower()} + json_path = directory / profile.artifacts.calibration_filename.format(**naming) + urdf_path = directory / profile.artifacts.corrected_urdf_filename.format(**naming) + atomic_write_json(json_path, payload) + atomic_write_json(directory / REPORT_FILENAME, report) + rebuild_urdf_from_json(json_path, source, urdf_path, + authorized_fields=profile.urdf_authorized_fields, expected_profile_id=profile.key.profile_id) + return payload, json_path, urdf_path + + def finalize_profile_session(*, profile, session_dir, serial_number, source_urdf, protected_inputs, records, publish=True, timestamp=None, standard_loader: Callable = validate_with_robot_state_publisher, cancelled: Callable[[], bool] = lambda: False, directions_are_task_relative: bool = False, + require_motion_evidence: bool = False, + camera_extrinsics_file=None, phase_changed: Callable[[str], None] = lambda _phase: None): """No worker may publish after an operator abort. The runtime owns cancellation synchronization. This function also checks immediately before publication and never returns the hand to a pose. """ + reject_diagnostic_capture(records) del timestamp if profile.artifacts.output_schema_version not in {2, 3}: raise ValueError("production calibration requires unified output_schema_version: 2 or 3; legacy formats are read-only") + if profile.artifacts.output_schema_version >= 3: + from ...profiles.validator import validate_executable_profile, require_release_geometry + require_release_geometry(validate_executable_profile(profile, source_urdf)) directory, source = Path(session_dir).resolve(), Path(source_urdf).resolve() directory.mkdir(parents=True, exist_ok=True) def check_cancelled(): if cancelled(): raise ValueError("operator_abort:no_artifact_publication") check_cancelled() + from ...core.fitting.command_sampling import validate_sampling_plans + sampling_plans = validate_sampling_plans(profile, records) + if sampling_plans: + atomic_write_json(directory / "command_sampling_plans.json", sampling_plans) + from ..motion_provenance import validate_source_geometry + validate_source_geometry(profile, source, records) if any(row.get("projection_reprocessing_scope") for row in records): raise ValueError("partial external projection is diagnostic-only; cannot certify a whole session") pose_reports = {} for task in profile.motion.tasks: if task.key not in profile.measurement.candidate_selection_tasks: continue - records, report = resolve_chain_observations(records, task_key=task.key, - fixed_roles={tag.role for view in profile.vision.views for tag in view.tags if tag.fixed_reference}, - role_pairs={name: (profile.measurement.measurements[name].parent_role, - profile.measurement.measurements[name].child_role) for name in task.joints}) + try: + records, report = resolve_chain_observations(records, task_key=task.key, + require_frozen_branches=True, + fixed_roles={tag.role for view in profile.vision.views for tag in view.tags if tag.fixed_reference}, + role_pairs={name: (profile.measurement.measurements[name].parent_role, + profile.measurement.measurements[name].child_role) for name in task.joints}) + except ValueError as error: + pose_reports[task.key] = {'status': 'failed', 'reason': str(error), 'publication_allowed': False} + atomic_write_json(directory / "pose_selection_diagnostics.json", pose_reports) + raise pose_reports[task.key] = report atomic_write_json(directory / "pose_selection_diagnostics.json", pose_reports) if report["status"] == "legacy_projection_unverified": raise ValueError("camera_projection_unverified:no_artifact_publication") + from ..motion_provenance import validate_motion_provenance + try: + motion_provenance = validate_motion_provenance(profile, records, + required=require_motion_evidence, require_complete=True) + except ValueError as error: + atomic_write_json(directory / "motion_branch_provenance.json", { + "status": "failed", "reason": str(error), "publication_allowed": False}) + raise + atomic_write_json(directory / "motion_branch_provenance.json", motion_provenance) accepted = accepted_joint_records(profile, records, directions_are_task_relative=directions_are_task_relative) secondary = accepted_secondary_records(profile, records, directions_are_task_relative=directions_are_task_relative) try: from ...core.domain.reference import read_joint_zero_references - if profile.artifacts.output_schema_version >= 3: - from ...profiles.validator import validate_executable_profile - validate_executable_profile(profile, source) references = read_joint_zero_references(profile, records) if profile.artifacts.output_schema_version >= 3 else None def save_relative_motion(motion): if profile.artifacts.output_schema_version >= 3: @@ -85,8 +136,11 @@ def finalize_profile_session(*, profile, session_dir, serial_number, source_urdf from ...core.fitting.motion_transfer import expand_profile_transfers fit = expand_profile_transfers(profile, fit) except ValueError as error: - atomic_write_json(directory / "fit_diagnostics.json", { - "passed": False, "reason": str(error), "publication_allowed": False}) + diagnostic = {"passed": False, "reason": str(error), "publication_allowed": False} + if isinstance(error, RotationHoldoutFailure): + diagnostic.update(stage="relative_rotation_holdout", + rotation_holdout=asdict(error.diagnostics)) + atomic_write_json(directory / "fit_diagnostics.json", diagnostic) raise check_cancelled() phase_changed("fit_complete") @@ -96,53 +150,80 @@ def finalize_profile_session(*, profile, session_dir, serial_number, source_urdf atomic_write_json(directory / "fit_diagnostics.json", {"passed": False, "reason": str(error), "publication_allowed": False, "relative_motion": {name: asdict(curve) for name, curve in fit.curves.items()}}) raise + check_cancelled() + phase_changed("holdout_complete") + payload, json_path, urdf_path = _write_artifact_pair( + profile, fit, prepared, directory, source, serial_number, protected_inputs) + phase_changed("artifacts_built") zero = fit.spatial_zero replay_records = [row for rows in accepted.values() for row in rows] replay_records.extend(dict(row, joint=profile.measurement.cross_view_sources[name]) for name, rows in secondary.items() for row in rows) - replay = prepare_tag_replay(profile=profile, source_model=UrdfKinematicModel(source), - plan=prepared.plan, output_mappings=fit.output_mappings, - common_from_base=transform_matrix(zero.base_translation_xyz_m, zero.base_quaternion_xyzw), - records=replay_records) + from ...core.fitting.tag_installation import TagInstallationFailure + try: + replay = prepare_tag_replay(profile=profile, source_model=UrdfKinematicModel(source), + plan=prepared.plan, output_mappings=fit.output_mappings, + common_from_base=transform_matrix(zero.base_translation_xyz_m, zero.base_quaternion_xyzw), + records=replay_records) + except TagInstallationFailure as error: + atomic_write_json(directory / "tag_installation_diagnostics.json", { + "passed": False, "publication_allowed": False, + "stage": "training_rigid_tag_installation", + "rotation_p95_limit_deg": 2.0, "translation_p95_limit_m": 0.003, + "roles": error.diagnostics}) + raise from .evidence import prepare_command_holdout + from ...core.domain.sampling import command_holdout_cycle + command_validation_cycle = command_holdout_cycle(profile) steady_secondary = accepted_secondary_records(profile, records, sample_phase="steady", directions_are_task_relative=directions_are_task_relative) steady_rows = [r for rows in steady.values() for r in rows] steady_rows.extend(dict(r, joint=profile.measurement.cross_view_sources[name]) for name, rows in steady_secondary.items() for r in rows) command_holdout = prepare_command_holdout(profile, fit.command_mappings, steady_rows) + from ..motion_provenance import uses_motion_evidence + image_inputs = {} + if require_motion_evidence or uses_motion_evidence(records): + if camera_extrinsics_file is None: + raise ValueError("final_image_holdout_requires_protected_camera_file") + from .image_evidence import ImageEvidence + image_evidence = ImageEvidence(profile, records, camera_extrinsics_file=camera_extrinsics_file, + protected_inputs=protected_inputs) + image_inputs = { + "image_observations": image_evidence.all_view_holdout(replay.holdout, + mappings=fit.output_mappings, input_kind="feedback", holdout_cycle=profile.quality.holdout_cycle), + "command_image_observations": image_evidence.all_view_holdout(command_holdout, + mappings=fit.command_mappings, input_kind="command", holdout_cycle=command_validation_cycle), + "require_image_holdout": True, + } validator = FrozenTagArtifactValidator(profile.key.profile_id, source, protected_inputs["source_urdf_sha256"], profile.urdf_authorized_fields, fit.zero_offsets_rad, replay.common_from_base, replay.installations, replay.holdout, replay.required_roles, standard_loader, protected_inputs=dict(protected_inputs), command_observations=command_holdout, - joint_observations=tuple(r for rows in steady.values() for r in rows if r["cycle"] == 3), + joint_observations=tuple(r for rows in steady.values() for r in rows if r["cycle"] == command_validation_cycle), + command_holdout_cycle=command_validation_cycle, joint_curves=fit.curves, joint_channels={name: channel_for_joint(profile, name) for name in fit.output_mappings}, transferred_motion_sources=profile.measurement.transferred_motion_sources, cad_zero_assumptions=profile.zero.cad_zero_assumptions, transferred_zero_sources=profile.zero.transferred_zero_sources, - joint_zero_methods=fit.zero_method_by_joint) - report_writer, command_writer = serialize_report, from_report - if profile.artifacts.output_schema_version >= 3: - from .serializers.measured_v3 import serialize_report as report_writer, from_report as command_writer - report = report_writer(profile, fit, prepared, serial_number=serial_number, protected_inputs=protected_inputs) - payload = command_writer(report) - naming = {"serial_number": serial_number, "side": profile.key.side, "model": profile.key.model.lower()} - json_path = directory / profile.artifacts.calibration_filename.format(**naming) - urdf_path = directory / profile.artifacts.corrected_urdf_filename.format(**naming) + joint_zero_methods=fit.zero_method_by_joint, range_evidence=prepared.plan.range_evidence, + directional_command_joints=profile.artifacts.directional_command_joints, **image_inputs) check_cancelled() - phase_changed("holdout_complete") - prepared.plan.write(source, urdf_path) - atomic_write_json(json_path, payload) - atomic_write_json(directory / REPORT_FILENAME, report) atomic_write_json(directory / "frozen_tag_installations.json", { "common_from_base": replay.common_from_base, "installations": {name: asdict(mount) for name, mount in replay.installations.items()}}) - phase_changed("artifacts_built") publisher = ArtifactPublisher(directory.parent, profile.artifacts.publication_pointer) - staged = publisher.prepare(session_directory=directory, calibration_json=json_path, - corrected_urdf=urdf_path, validate=validator, cancelled=cancelled) + from ...core.urdf.image_acceptance import ImageHoldoutFailure + try: + staged = publisher.prepare(session_directory=directory, calibration_json=json_path, + corrected_urdf=urdf_path, validate=validator, cancelled=cancelled) + except ImageHoldoutFailure as error: + atomic_write_json(directory / "final_image_diagnostics.json", { + "passed": False, "publication_allowed": False, "reason": str(error), + "input_kind": error.input_kind, "holdout_cycle": error.holdout_cycle, "roles": error.report}) + raise import json acceptance = json.loads(staged.validation_json) atomic_write_json(directory / "standard_urdf_acceptance.json", acceptance) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/image_evidence.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/image_evidence.py new file mode 100644 index 0000000..c07fdd9 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/image_evidence.py @@ -0,0 +1,153 @@ +"""Bind final-file image checks to raw pixels and protected camera identities.""" + +import hashlib +import json +from numbers import Real +from pathlib import Path + +import numpy as np + +from ...core.geometry.extrinsics import camera_info_fingerprint, load_camera_extrinsics +from ...core.urdf.image_acceptance import TagImageObservation +from ..image_capture import IMAGE_OBSERVATION_POLICY +from .evidence import _replay_directions + + +class ImageEvidence: + """Validate source identities once, then prepare disjoint input holdouts. + + Primary observations retain their existing provenance checks. Additional + views contribute original pixels and synchronized SDK values directly, + without manufacturing a PnP pose or fitting against a holdout image. + """ + + def __init__(self, profile, records, *, camera_extrinsics_file, protected_inputs): + path = Path(camera_extrinsics_file) + if hashlib.sha256(path.read_bytes()).hexdigest() != protected_inputs["camera_extrinsics_sha256"]: + raise ValueError("final_image_camera_extrinsics_changed") + self.profile = profile + self.extrinsics = load_camera_extrinsics(path, required_views=profile.vision.view_names, + reference_view=profile.vision.extrinsic_reference_view, + quality_limits=profile.vision.extrinsics_quality_limits, + minimum_capture_counts=profile.vision.minimum_capture_counts) + self.models, self.frames, self.raw_frames, self.completed = {}, {}, {}, {} + self.tags = {tag.role: (view.name, tag) for view in profile.vision.views for tag in view.tags} + self.tasks = {task.key: task for task in profile.motion.tasks} + for row in records: + kind = row.get("kind") + if kind == "rectified_camera_model": + self._bind(self.models, row["view"], row) + elif kind in {"pnp_candidate_frame", "image_observation_frame"}: + target = self.frames if kind == "pnp_candidate_frame" else self.raw_frames + self._bind(target, f"{row['view']}:{row['image_stamp_ns']}", row) + elif kind == "scan_unit_complete": + key = (row["task_name"], row["cycle"], row["direction"]) + self.completed[key] = row + for view, model in self.models.items(): + identity = self.extrinsics.cameras[view] + fingerprint = camera_info_fingerprint(width=model["width"], height=model["height"], + camera_matrix=model["raw_k"], distortion=model["raw_d"], + rectification=model["rectification_r"], projection=model["projection_p"]) + if (fingerprint != identity.intrinsics_sha256 + or model.get("matrix_source") != "CameraInfo.P[:3,:3]" + or model.get("input_is_rectified") is not True): + raise ValueError("final_image_camera_intrinsics_changed") + if not np.array_equal(np.asarray(model["projection_p"]).reshape(3, 4)[:, :3], model["camera_matrix"]): + raise ValueError("final_image_camera_projection_changed") + + @staticmethod + def _bind(mapping, key, row): + if key in mapping and json.dumps(mapping[key], sort_keys=True) != json.dumps(row, sort_keys=True): + raise ValueError("final_image_source_identity_reused") + mapping[key] = row + + def _image(self, frame, *, sample_id, role, cycle, sdk_values, directions): + view, tag = self.tags[role] + model = self.models.get(view) + if (model is None or frame["view"] != view or frame["cycle"] != cycle + or frame.get("input_is_rectified") is not True + or frame.get("camera_matrix_source") != "CameraInfo.P[:3,:3]" + or not np.array_equal(frame["camera_matrix"], model["camera_matrix"])): + raise ValueError("final_image_holdout_source_missing_or_changed") + field = "tags" if frame["kind"] == "image_observation_frame" else "roles" + pixel = frame[field].get(role, {}) + if pixel.get("tag_size_m") != tag.size_m or pixel.get("tag_id") != tag.tag_id: + raise ValueError("final_image_holdout_tag_changed") + corners = np.asarray(pixel.get("corners_xy", ())) + if corners.shape != (4, 2) or not np.all(np.isfinite(corners)): + raise ValueError("final_image_holdout_corners_missing") + return TagImageObservation(sample_id, role, cycle, tuple(sdk_values), tuple(sorted(directions.items())), + view, tag.size_m, tuple(map(tuple, corners)), tuple(map(tuple, frame["camera_matrix"])), + tuple(map(tuple, np.linalg.inv(self.extrinsics.transform(view)))), + task_name=frame.get("task_name", "")) + + def primary_holdout(self, observations): + result = [] + for observation in observations: + frame = self.frames.get(observation.sample_id) + if frame is None: + raise ValueError("final_image_holdout_source_missing_or_changed") + result.append(self._image(frame, sample_id=observation.sample_id, role=observation.role, + cycle=observation.cycle, sdk_values=observation.sdk_values, + directions=observation.sdk_directions_by_joint)) + return tuple(result) + + def all_view_holdout(self, observations, *, mappings, input_kind, holdout_cycle): + """Use every synchronized image in the accepted independent scan units. + + Duplicate primary/raw identities must agree in both pixels and SDK + conditions. Missing feedback is explicit unavailable evidence; it is + never replaced with a command value or a fitted angle. + """ + if input_kind not in {"feedback", "command"}: + raise ValueError("final_image_input_kind_invalid") + primary = self.primary_holdout(observations) + expected = {(row.role, row.sample_id): row for row in primary} + roles = {row.role for row in primary} + phase = "sweep" if input_kind == "feedback" else "steady" + result = {} + count = self.profile.command.command_count + for sample_id, frame in self.raw_frames.items(): + if frame.get("cycle") != holdout_cycle or frame.get("sample_phase") != phase: + continue + unit = self.completed.get((frame["task_name"], holdout_cycle, frame["direction"])) + if unit is None or not unit["passed"] or unit["attempt"] != frame["attempt"]: + continue + if frame.get("policy") != IMAGE_OBSERVATION_POLICY or frame.get("command_unit") != self.profile.command.unit: + raise ValueError("final_image_raw_policy_or_unit_changed") + vectors = [frame.get("command_vector"), frame.get("feedback_vector")] + skew = frame.get("state_image_sync_error_ns") + if any(values is None for values in vectors) or skew is None: + continue + if (not isinstance(skew, Real) or isinstance(skew, bool) + or not np.isfinite(skew) or skew < 0): + raise ValueError("final_image_raw_sync_error_invalid") + arrays = [np.asarray(values) for values in vectors] + if any(values.shape != (count,) or values.dtype.kind not in "fiu" + or not np.all(np.isfinite(values)) for values in arrays): + raise ValueError("final_image_raw_sdk_vector_invalid") + history = frame.get("command_direction_by_index") + if (not isinstance(history, (list, tuple)) or len(history) != count + or any(value not in {"", "increasing", "decreasing"} for value in history)): + raise ValueError("final_image_raw_direction_history_invalid") + task = self.tasks.get(frame["task_name"]) + if task is None: + raise ValueError("final_image_raw_task_unknown") + directions = _replay_directions(mappings, frame, task) + values = frame["feedback_vector" if input_kind == "feedback" else "command_vector"] + for role in sorted(roles.intersection(frame["tags"])): + image = self._image(frame, sample_id=sample_id, role=role, cycle=holdout_cycle, + sdk_values=values, directions=directions) + identity = (role, sample_id) + if identity in expected and image != expected[identity]: + raise ValueError("final_image_primary_raw_disagree") + result[identity] = image + if not expected or not expected.keys() <= result.keys(): + raise ValueError("final_image_all_view_source_missing") + return tuple(result.values()) + + +def prepare_image_holdout(profile, records, observations, *, camera_extrinsics_file, protected_inputs): + """Historical primary-image binding; no new evidence is imputed to old files.""" + return ImageEvidence(profile, records, camera_extrinsics_file=camera_extrinsics_file, + protected_inputs=protected_inputs).primary_holdout(observations) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/process_finalizer.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/process_finalizer.py new file mode 100644 index 0000000..ba37f90 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/process_finalizer.py @@ -0,0 +1,102 @@ +"""Isolate CPU-heavy finalization from live ROS callbacks. + +The parent thread transports events/results and owns cancellation. Only the +controller may publish. A bounded, synced journal prefix avoids pickling the +entire capture while holding the control lock. +""" + +import json +import multiprocessing +from pathlib import Path +import time +import traceback + +from .finalization import finalize_profile_session + + +def read_journal_prefix(path, size): + """Decode the frozen prefix without retaining copies of the raw journal. + + Captures can contain hundreds of megabytes of JSONL. Reading the whole + prefix and then splitting it keeps two additional copies alive while the + decoded records accumulate, alongside the live camera processes. + """ + if not isinstance(size, int) or isinstance(size, bool) or size <= 0: + raise ValueError("invalid_finalization_journal_size") + rows = [] + remaining = size + with Path(path).open("rb") as stream: + while remaining: + line = stream.readline(remaining) + if not line or not line.endswith(b"\n"): + raise ValueError("incomplete_finalization_journal_prefix") + remaining -= len(line) + row = json.loads(line) + if not isinstance(row, dict): + raise ValueError("invalid_finalization_journal_rows") + rows.append(row) + return tuple(rows) + + +def _prepare(connection, finalizer, journal_path, journal_size, kwargs): + started = time.monotonic() + try: + records = read_journal_prefix(journal_path, journal_size) + result = finalizer(**kwargs, records=records, publish=False, + phase_changed=lambda phase: connection.send(("phase", phase))) + connection.send(("result", result)) + except Exception: + connection.send(("error", traceback.format_exc())) + finally: + from ...core.artifacts.storage import atomic_write_json + atomic_write_json(Path(kwargs["session_dir"]) / "finalization_timing.json", { + "backend": "isolated_process", "elapsed_seconds": time.monotonic()-started, + "journal_prefix_bytes": journal_size}) + connection.close() + + +def isolated_finalize_profile_session(*, journal_path, journal_size, records, + phase_changed, cancelled, publish=False, finalizer=finalize_profile_session, + timeout_seconds=600., **kwargs): + """Same production finalizer, with bounded cancellation and no publication.""" + del records # The synced journal is the immutable capture transport. + if publish: + raise ValueError("isolated_worker_cannot_publish") + if cancelled(): + raise ValueError("operator_abort:no_artifact_publication") + context = multiprocessing.get_context("spawn") + receive, send = context.Pipe(duplex=False) + process = context.Process(target=_prepare, + args=(send, finalizer, journal_path, journal_size, kwargs), daemon=True) + started = time.monotonic() + try: + process.start() + send.close() + while not cancelled(): + if receive.poll(.05): + kind, value = receive.recv() + if kind == "phase": + phase_changed(value) + elif kind == "result": + return value + elif kind == "error": + raise RuntimeError(f"finalization_process_failed:\n{value}") + else: + raise RuntimeError(f"invalid_finalization_process_event:{kind}") + elif not process.is_alive(): + raise RuntimeError(f"finalization_process_exited:{process.exitcode}") + if time.monotonic()-started >= timeout_seconds: + raise TimeoutError("finalization_process_timeout:no_artifact_publication") + raise ValueError("operator_abort:no_artifact_publication") + finally: + receive.close() + send.close() + if process.pid is not None: + process.join(timeout=.2) + if process.is_alive(): + process.terminate() + process.join(timeout=1.) + if process.is_alive(): + process.kill() + process.join(timeout=1.) + process.close() diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/publisher.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/publisher.py index 8ec10af..4702409 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/publisher.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/publisher.py @@ -109,6 +109,12 @@ class FrozenTagArtifactValidator: cad_zero_assumptions: Mapping[str, str] = field(default_factory=dict) transferred_zero_sources: Mapping[str, str] = field(default_factory=dict) joint_zero_methods: Mapping[str, str] = field(default_factory=dict) + range_evidence: Mapping = field(default_factory=dict) + directional_command_joints: frozenset[str] = frozenset() + command_holdout_cycle: int = 3 + image_observations: tuple = () + command_image_observations: tuple = () + require_image_holdout: bool = False def __call__(self, json_path: Path, urdf_path: Path) -> Mapping[str, Any]: payload = json.loads(json_path.read_text(encoding="utf-8")) @@ -121,6 +127,8 @@ class FrozenTagArtifactValidator: if (measured_from_report if measured else from_report)(feedback_payload) != payload: raise ValueError("compact JSON differs from the frozen calibration report") if measured: + if payload.get("directional_command_joints", []) != sorted(self.directional_command_joints): + raise ValueError("directional command declaration differs from the Profile") if feedback_payload.get("protected_inputs") != dict(self.protected_inputs): raise ValueError("v3 report protected inputs differ from the session") if {name: row["sdk_channel"] for name, row in payload["joints"].items()} != dict(self.joint_channels): @@ -129,11 +137,16 @@ class FrozenTagArtifactValidator: validate_artifact_structure(source_urdf=self.source_urdf, corrected_urdf=urdf_path, source_sha256=self.source_sha256, authorized_fields=self.authorized_fields, payload=feedback_payload, - zero_offsets_rad=self.zero_offsets_rad) + zero_offsets_rad=self.zero_offsets_rad, range_evidence=self.range_evidence) changes = validate_artifact_structure(source_urdf=self.source_urdf, corrected_urdf=urdf_path, source_sha256=self.source_sha256, authorized_fields=self.authorized_fields, payload=payload, - zero_offsets_rad=self.zero_offsets_rad) + zero_offsets_rad=self.zero_offsets_rad, range_evidence=self.range_evidence) + rebuilt_from_json = "urdf_correction" in payload + if rebuilt_from_json: + from .urdf_from_json import validate_json_urdf_correspondence + validate_json_urdf_correspondence(payload, self.source_urdf, urdf_path, + expected_profile_id=self.profile_id) metrics = validate_serialized_tag_holdout(corrected_urdf=urdf_path, payload=feedback_payload, common_from_base=self.common_from_base, installations=self.installations, observations=self.observations, @@ -144,32 +157,64 @@ class FrozenTagArtifactValidator: command_metrics = validate_serialized_tag_holdout(corrected_urdf=urdf_path, payload=payload, common_from_base=self.common_from_base, installations=self.installations, observations=self.command_observations, - required_roles=self.required_roles, minimum_samples=3, input_kind="command") + required_roles=self.required_roles, minimum_samples=3, input_kind="command", + holdout_cycle=self.command_holdout_cycle) except ValueError as error: if payload.get("format") == COMPACT_FORMAT: raise ValueError(f"compact_command_table_holdout_failed:{error}") from error raise + image_metrics = {} + image_policy = None + if self.require_image_holdout: + from ...core.urdf.image_acceptance import FINAL_IMAGE_ACCEPTANCE_POLICY, validate_serialized_image_holdout + image_policy = FINAL_IMAGE_ACCEPTANCE_POLICY + common = dict(corrected_urdf=urdf_path, common_from_base=self.common_from_base, + installations=self.installations, required_roles=self.required_roles) + image_metrics["feedback"] = validate_serialized_image_holdout(**common, + payload=feedback_payload, observations=self.image_observations, + independent_observations=self.observations, + input_kind="feedback", holdout_cycle=3, minimum_samples=self.minimum_samples) + image_metrics["command"] = validate_serialized_image_holdout(**common, + payload=payload, observations=self.command_image_observations, + independent_observations=self.command_observations, + input_kind="command", holdout_cycle=self.command_holdout_cycle, minimum_samples=3) self.standard_loader(urdf_path) evidence = {"base": self.common_from_base, "installations": {k: asdict(v) for k, v in self.installations.items()}, "observations": [asdict(row) for row in self.observations]} evidence["steady_command_observations"] = [asdict(row) for row in self.command_observations] + evidence["image_observations"] = [asdict(row) for row in self.image_observations] + evidence["command_image_observations"] = [asdict(row) for row in self.command_image_observations] + evidence["motion_range_evidence"] = {name: asdict(item) for name, item in self.range_evidence.items()} extra = {} if measured: + from ...core.urdf.joint_control import ( + JSON_JOINT_ANGLES, independent_joint_sources, uses_affine_mimic_approximation, + uses_source_mimic, + ) from ...core.urdf.measured_acceptance import validate_measured_joint_holdout extra = {"independent_mapping_holdout": validate_measured_joint_holdout(payload, self.joint_curves, self.joint_observations, - transferred_motion_sources=self.transferred_motion_sources), + transferred_motion_sources=self.transferred_motion_sources, + holdout_cycle=self.command_holdout_cycle), "full_json_urdf_holdout": command_metrics, "independently_measured_joints": sorted(self.joint_curves), "transferred_joints": sorted(self.transferred_motion_sources), "transferred_motion_sources": dict(self.transferred_motion_sources), "cad_zero_assumptions": dict(self.cad_zero_assumptions), - "precision_scope": "observed_joints_travel_directions_and_held_poses", - "urdf_mimic_approximation": validate_serialized_tag_holdout(corrected_urdf=urdf_path, + "directional_command_joints": sorted(self.directional_command_joints), + "precision_scope": "observed_joints_travel_directions_and_held_poses"} + if independent_joint_sources(payload) is not None: + extra["urdf_joint_angle_source"] = JSON_JOINT_ANGLES + else: + extra["urdf_mimic_approximation"] = validate_serialized_tag_holdout(corrected_urdf=urdf_path, payload=payload, common_from_base=self.common_from_base, installations=self.installations, observations=self.command_observations, required_roles=self.required_roles, minimum_samples=3, - input_kind="command", mimic_approximation=True, enforce_precision=False)} + input_kind="command", mimic_approximation=True, enforce_precision=False, + holdout_cycle=self.command_holdout_cycle) + if uses_affine_mimic_approximation(payload) or uses_source_mimic(payload): + extra["urdf_joint_angle_source"] = JSON_JOINT_ANGLES + extra["urdf_mimic_policy"] = payload["urdf_correction"]["mimic_policy"] evidence["joint_observations"] = self.joint_observations evidence["frozen_joint_curves"] = {key: asdict(value) for key, value in self.joint_curves.items()} evidence["transferred_motion_sources"] = dict(self.transferred_motion_sources) @@ -177,10 +222,16 @@ class FrozenTagArtifactValidator: acceptance_kind = ("measured_and_transferred_json_urdf_v3" if self.transferred_motion_sources else "independently_measured_json_urdf_v3") if measured else "serialized_standard_urdf_tag_replay_v1" return {"acceptance": acceptance_kind, + "final_file_image_holdout": image_metrics, + "final_file_image_holdout_policy": image_policy, + "final_file_image_holdout_verified": self.require_image_holdout, **extra, + "urdf_rebuilt_from_json": rebuilt_from_json, "profile_id": self.profile_id, "source_urdf_sha256": self.source_sha256, "protected_inputs": dict(self.protected_inputs), + "motion_range_evidence": evidence["motion_range_evidence"], "holdout_cycle": 3, "tag_holdout": metrics, + "command_holdout_cycle": self.command_holdout_cycle, "steady_command_tag_holdout": command_metrics, "final_file_spatial_replay": "passed", "holdout_evidence_sha256": hashlib.sha256(json.dumps(evidence, @@ -189,6 +240,11 @@ class FrozenTagArtifactValidator: def _validate_report_provenance(self, report): """The report cannot grant itself new transfer or CAD authorizations.""" + if report.get("quality", {}).get("command_holdout_cycle", 3) != self.command_holdout_cycle: + raise ValueError("v3 command validation partition differs from the Profile") + ranges = json.loads(json.dumps({name: asdict(item) for name, item in self.range_evidence.items()})) + if report.get("motion_range_evidence", {}) != ranges: + raise ValueError("v3 motion range evidence differs from the frozen training fit") if report.get("transferred_motion_sources", {}) != dict(self.transferred_motion_sources): raise ValueError("v3 transfer sources differ from the measured Profile") rows = report["joints"] diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/reader.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/reader.py index 1c2f30a..91a18ee 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/reader.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/reader.py @@ -7,13 +7,19 @@ from pathlib import Path from ...core.urdf.acceptance import SerializedJointMapping, validate_compact_urdf_tables from ...core.urdf.kinematics import UrdfKinematicModel +from ...core.urdf.joint_control import ( + JSON_JOINT_ANGLES, independent_joint_sources, uses_affine_mimic_approximation, + uses_source_mimic, +) from .serializers.compact_v2 import FORMAT as COMPACT_FORMAT, from_report from .serializers.measured_v3 import FORMAT as MEASURED_FORMAT, from_report as measured_from_report +from .direction import next_input_directions class UnifiedCommandMapper: def __init__(self, payload, corrected_urdf, *, input_kind="command", expected_side=None, report=None): self.measured = payload.get("format") == MEASURED_FORMAT + self.json_mimic_override = self.measured and independent_joint_sources(payload) is None compact = payload.get("format") in {COMPACT_FORMAT, MEASURED_FORMAT} if payload.get("format") not in {"unified_calibration_v1", COMPACT_FORMAT, MEASURED_FORMAT}: raise ValueError("requires a unified calibration artifact") @@ -45,6 +51,13 @@ class UnifiedCommandMapper: self.feedback_name_aliases = dict(metadata.get("feedback_name_aliases", {})) self.previous = None self.directions = [""]*len(self.command_names) + self.directional_channels = {} + for name in self.mapping.directional_joints: + row = self.mapping.joints[name] + knots = row["curve_input_knots_rad"] + lower, upper = (knots[0], knots[-1]) if knots else (0., 255.) + index = row["motor_index"] + self.directional_channels[index] = (metadata["baseline_command"][index], lower, upper) def map_positions(self, positions, names=()): values = tuple(float(v) for v in positions) @@ -56,14 +69,12 @@ class UnifiedCommandMapper: raise ValueError("feedback names differ from the serialized SDK layout") named = dict(zip(names, values)) values = tuple(named[n] for n in self.command_names) - directions = self.directions.copy() - if self.previous is not None: - for i, (old, new) in enumerate(zip(self.previous, values)): - if abs(new-old) > (0.5 if self.input_domain.endswith("u8") else 1e-9): - directions[i] = "increasing" if new > old else "decreasing" + directions = next_input_directions(values, self.previous, self.directions, + resolution=0.5 if self.input_domain.endswith("u8") else 1e-9, + channels=self.directional_channels, byte_input=self.input_domain.endswith("u8")) active = self.mapping.evaluate(values, "", active_joints=self.active, directions_by_joint={name: directions[row["motor_index"]] for name, row in self.mapping.joints.items()}) - result = self.urdf.resolve_angles(active, independent_mimic_angles=self.measured) + result = self.urdf.resolve_angles(active, independent_mimic_angles=self.json_mimic_override) for name, angle in result.items(): joint = self.urdf.joints[name] if joint.lower is not None and not joint.lower-1e-9 <= angle <= joint.upper+1e-9: @@ -91,8 +102,20 @@ def load_unified_mapper(calibration_file, *, expected_side=None, input_kind="com if validation.get("final_file_spatial_replay") != "passed" or not validation.get("steady_command_tag_holdout"): raise ValueError("release lacks independent command and feedback URDF evidence") payload = json.loads(json_path.read_text()) + if "urdf_correction" in payload and validation.get("urdf_rebuilt_from_json") is not True: + raise ValueError("release lacks JSON-to-URDF correspondence validation") report = json.loads(artifact("calibration_report").read_text()) if payload.get("format") in {COMPACT_FORMAT, MEASURED_FORMAT} else None if payload.get("format") == MEASURED_FORMAT: + if (independent_joint_sources(payload) is not None + and validation.get("urdf_joint_angle_source") != JSON_JOINT_ANGLES): + raise ValueError("release lacks explicit JSON joint angle acceptance") + if (uses_affine_mimic_approximation(payload) or uses_source_mimic(payload)) and ( + validation.get("urdf_mimic_policy") != payload["urdf_correction"]["mimic_policy"] + or validation.get("urdf_joint_angle_source") != JSON_JOINT_ANGLES + or not validation.get("urdf_mimic_approximation")): + raise ValueError("release lacks separate mimic approximation acceptance") + if validation.get("directional_command_joints", []) != payload.get("directional_command_joints", []): + raise ValueError("v3 release directional mapping differs from its acceptance") transfers = report.get("transferred_motion_sources", {}) expected_kind = "measured_and_transferred_json_urdf_v3" if transfers else "independently_measured_json_urdf_v3" observed = {name for name, row in report["joints"].items() if row.get("independently_measured") is True} @@ -109,5 +132,7 @@ def load_unified_mapper(calibration_file, *, expected_side=None, input_kind="com if "cad_zero_assumption" in row} if validation.get("cad_zero_assumptions", {}) != assumptions: raise ValueError("v3 release CAD assumptions differ from its report") + if validation.get("motion_range_evidence", {}) != report.get("motion_range_evidence", {}): + raise ValueError("v3 release motion range evidence differs from its report") return UnifiedCommandMapper(payload, urdf_path, input_kind=input_kind, expected_side=expected_side, report=report) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/replay.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/replay.py index c039cd2..cf9b3ea 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/replay.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/replay.py @@ -5,6 +5,7 @@ import hashlib import json from ..acquisition import load_capture +from ..diagnostic_capture import reject_diagnostic_capture from ..engine import CalibrationEngine from ..resume import ResumeVerifier, fingerprint_from_mapping from ..scan_quality import evaluate_capture_unit @@ -13,13 +14,14 @@ from .finalization import finalize_profile_session def validate_capture_provenance(profile, serial_number, hashes, records): """An offline journal cannot substitute today's hashes for old evidence.""" + reject_diagnostic_capture(records) headers = [row for row in records if row.get("kind") == "session_start"] references = [row for row in records if row.get("kind") == "fixed_base_reference_locked"] if len(headers) != 1 or len(references) != 1: raise ValueError("capture_provenance:requires_unique_session_and_locked_reference") header, reference = headers[0], references[0] engine = CalibrationEngine(profile) - if (not engine.resume_compatible(header) or header.get("serial_number") != serial_number + if (not engine.capture_compatible(header) or header.get("serial_number") != serial_number or header.get("curve_input_domain") != profile.curve_input_domain): raise ValueError("capture_provenance:obsolete_policy_or_coordinate_identity; fresh capture required") for key, value in hashes.items(): @@ -48,6 +50,8 @@ def validate_capture_provenance(profile, serial_number, hashes, records): def validate_capture_units(profile, records): """Offline and resumed data must satisfy the actual post-sweep policy.""" + from ...core.fitting.command_sampling import validate_sampling_plans + validate_sampling_plans(profile, records) first_spans = {} for unit in CalibrationEngine(profile).scan_units(): key = (unit.task_key, unit.cycle, unit.direction) @@ -79,5 +83,6 @@ def replay_capture(config, raw_path: Path, *, output: Path | None, publish: bool payload, _fit, correction = finalize_profile_session( profile=profile, session_dir=output, serial_number=config.serial_number, source_urdf=config.source_urdf, - protected_inputs=hashes, records=records, publish=publish) + protected_inputs=hashes, records=records, publish=publish, + camera_extrinsics_file=config.camera_extrinsics) print(f"离线回放完成:schema {payload['schema_version']},URDF {correction.path}") diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/serializers/compact_v2.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/serializers/compact_v2.py index 750610b..399add9 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/serializers/compact_v2.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/serializers/compact_v2.py @@ -53,6 +53,7 @@ def from_report(report): for name in sorted(report["joints"]): resolve(name) return {"format": FORMAT, "schema_version": 2, + **({"urdf_correction": report["urdf_correction"]} if "urdf_correction" in report else {}), **{key: report[key] for key in ("profile_id", "model", "side", "serial_number")}, "input_unit": unit, "joints": {name: rows[name] for name in sorted(rows)}} diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/serializers/measured_v3.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/serializers/measured_v3.py index 89c54a1..5c543f8 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/serializers/measured_v3.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/serializers/measured_v3.py @@ -4,6 +4,7 @@ from dataclasses import asdict import numpy as np from .unified_v1 import _mapping +from ....core.urdf.joint_control import PRESERVE_SOURCE_MIMIC, uses_source_mimic FORMAT = "unified_calibration_v3" REPORT_FORMAT = "unified_calibration_report_v3" @@ -11,6 +12,7 @@ ZERO_CONVENTION = "q_CAD=q_output+delta; joint_baseline:q_output=0" def serialize_report(profile, fit, prepared, *, serial_number, protected_inputs): + from ....core.domain.sampling import command_training_cycles, command_holdout_cycle expected = set(prepared.mappings) transfers = dict(profile.measurement.transferred_motion_sources) observed = expected - transfers.keys() @@ -20,6 +22,9 @@ def serialize_report(profile, fit, prepared, *, serial_number, protected_inputs) if set(fit.joint_zero_references) != observed or set(fit.curves) != observed: raise ValueError("independent_measurement_or_reference_coverage_differs") rows = {} + independent_sources = getattr(prepared.plan, "independent_joint_sources", None) + preserved = getattr(prepared.plan, "mimic_policy", None) == PRESERVE_SOURCE_MIMIC + passive_sources = prepared.plan.mimic_output if independent_sources is None else independent_sources for name in sorted(expected): donor = transfers.get(name) if any(mapping.transferred_from != donor for mapping in @@ -29,7 +34,7 @@ def serialize_report(profile, fit, prepared, *, serial_number, protected_inputs) "zero_offset_rad": fit.zero_offsets_rad[name], "zero_method": fit.zero_method_by_joint[name], "command_to_rad": _mapping(fit.command_mappings[name]), "feedback_to_rad": _mapping(fit.output_mappings[name]), - "passive": name in prepared.plan.mimic_output} + "passive": name in passive_sources} if donor is None: row.update(zero_reference=fit.joint_zero_references[name].as_record(), applicability=fit.command_applicability[name], @@ -40,13 +45,26 @@ def serialize_report(profile, fit, prepared, *, serial_number, protected_inputs) baseline_command=profile.command.baseline_values[fit.command_mappings[name].motor_index], applicability={"scope": "transferred_from_declared_joint", "source_joint": donor, "recipient_independently_validated": False, "arbitrary_multiaxis_validated": False}) - if row["passive"]: + if row["passive"] and preserved: + parent, multiplier, offset = prepared.plan.mimic_output[name] + row["mimic_approximation"] = {"joint": parent, "multiplier": multiplier, "offset_rad": offset, + "usage": "source_urdf_linear_fallback", "physical_zero_correction": False} + elif row["passive"]: approximation = fit.mimic_approximations[name] if approximation.transferred_from_joint != donor: raise ValueError(f"mimic_transfer_differs_from_profile:{name}") - parent, multiplier, offset = prepared.plan.mimic_output[name] + if independent_sources is None: + parent, multiplier, offset = prepared.plan.mimic_output[name] + else: + parent = independent_sources[name] + multiplier, offset = approximation.output_fit.multiplier, approximation.output_fit.offset_rad row["mimic_approximation"] = {"joint": parent, "multiplier": multiplier, "offset_rad": offset, "evidence": asdict(approximation)} + if independent_sources is not None: + row["mimic_approximation"]["usage"] = "diagnostic_only_not_exported" + elif getattr(prepared.plan, "mimic_policy", None) is not None: + row["mimic_approximation"].update(usage="urdf_linear_approximation", + baseline_error_rad=offset, physical_zero_correction=False) if name in profile.zero.known_baseline_geometry: row["known_baseline_geometry"] = asdict(profile.zero.known_baseline_geometry[name]) if name in profile.zero.cad_zero_assumptions: @@ -55,6 +73,8 @@ def serialize_report(profile, fit, prepared, *, serial_number, protected_inputs) row["zero_transferred_from_joint"] = profile.zero.transferred_zero_sources[name] rows[name] = row return {"format": REPORT_FORMAT, "schema_version": 3, + **({"directional_command_joints": sorted(profile.artifacts.directional_command_joints)} + if profile.artifacts.directional_command_joints else {}), "profile_id": profile.key.profile_id, "model": profile.key.model, "side": profile.key.side, "serial_number": serial_number, "command_unit": profile.command.unit, "command_names": list(profile.command.names), "baseline_command": list(profile.command.baseline_values), @@ -63,6 +83,9 @@ def serialize_report(profile, fit, prepared, *, serial_number, protected_inputs) "coordinate_convention": ZERO_CONVENTION, "protected_inputs": dict(protected_inputs), "joints": rows, **({"transferred_motion_sources": transfers} if transfers else {}), "quality": {"training_cycles": [0, 1, 2], "holdout_cycle": 3, + "command_capture_mode": profile.acquisition.command_capture_mode, + "command_training_cycles": list(command_training_cycles(profile)), + "command_holdout_cycle": command_holdout_cycle(profile), "precision_scope": "observed_travel_directions_and_held_poses", "independently_measured_joints": sorted(observed), "transferred_joints": sorted(transfers), "final_file_acceptance": "release_manifest.json", "arbitrary_multiaxis_validated": False}} @@ -75,6 +98,9 @@ def from_report(report): mapping = SerializedJointMapping(report, input_kind="command") feedback = SerializedJointMapping(report, input_kind="feedback") unit, baseline, rows = report["command_unit"], report["baseline_command"], {} + directional = set(report.get("directional_command_joints", ())) + if not directional <= mapping.joints.keys(): + raise ValueError("directional_command_joint_missing") if len(baseline) != len(report["command_names"]) or not np.all(np.isfinite(baseline)): raise ValueError("joint_baseline_vector_differs_from_sdk_layout") transfers = report.get("transferred_motion_sources", {}) @@ -129,7 +155,7 @@ def from_report(report): right = {key: value for key, value in source[kind].items() if key != "channel_index"} if left != right: raise ValueError(f"transferred_curve_differs_from_source:{name}:{kind}") - if evidence.get("passive"): + if evidence.get("passive") and not uses_source_mimic(report): approximation = evidence.get("mimic_approximation", {}) source_approximation = source.get("mimic_approximation", {}).get("evidence", {}) expected_approximation = {**source_approximation, @@ -149,8 +175,17 @@ def from_report(report): row["angle_rad"] = np.interp(np.arange(256), knots, angles).tolist() else: row.update(input_values=knots.tolist(), angle_rad=angles.tolist()) + if name in directional: + for direction in ("increasing", "decreasing"): + values = np.asarray(data[direction + "_rad"]) + if abs(np.interp(baseline[index], knots, values)) > 1e-10: + raise ValueError(f"directional_joint_baseline_is_not_zero:{name}:{direction}") + row[direction + "_rad"] = (np.interp(np.arange(256), knots, values).tolist() + if unit == "u8" else values.tolist()) rows[name] = row return {"format": FORMAT, "schema_version": 3, + **({"urdf_correction": report["urdf_correction"]} if "urdf_correction" in report else {}), + **({"directional_command_joints": sorted(directional)} if directional else {}), **{key: report[key] for key in ("profile_id", "model", "side", "serial_number")}, "input_unit": unit, "baseline_command": baseline, "coordinate_convention": ZERO_CONVENTION, "joints": rows} diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/urdf_from_json.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/urdf_from_json.py new file mode 100644 index 0000000..4fbc727 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/artifacts/urdf_from_json.py @@ -0,0 +1,150 @@ +"""Persisted calibration JSON -> immutable-source URDF, without fitting.""" + +from dataclasses import asdict, fields +import hashlib +import json +import math +from pathlib import Path +import xml.etree.ElementTree as ET + +from ...core.urdf.acceptance import SerializedJointMapping, validate_compact_urdf_tables +from ...core.urdf.joint_control import ( + JSON_JOINT_ANGLES, independent_joint_sources, independent_export_authorization, + AFFINE_MIMIC_APPROXIMATION, uses_affine_mimic_approximation, + PRESERVE_SOURCE_MIMIC, uses_source_mimic, +) +from ...core.urdf.source_mimic import validate_source_mimic_elements +from ...core.urdf.kinematics import UrdfKinematicModel +from ...core.urdf.patch import ( + UrdfJointPatch, UrdfPatchSet, apply_urdf_patch_text, + write_urdf_patches, validate_urdf_mimic_ranges, +) + + +def serialize_urdf_correction(plan): + """Persist numeric edits and the explicit JSON angle-provider contract.""" + if plan.patches.mujoco_equalities: + raise ValueError("standard_calibration_cannot_rebuild_mujoco_extensions") + sources = plan.independent_joint_sources + affine = plan.mimic_policy == AFFINE_MIMIC_APPROXIMATION + preserved = plan.mimic_policy == PRESERVE_SOURCE_MIMIC + return {"schema_version": 4 if preserved else (3 if affine else (1 if sources is None else 2)), + "source_urdf_sha256": plan.source_sha256, + **({"joint_angle_source": JSON_JOINT_ANGLES, "mimic_policy": plan.mimic_policy} if affine or preserved else {}), + **({"mimic_envelopes": {name: {key: list(value) for key, value in asdict(item).items()} + for name, item in plan.mimic_envelopes.items()}} if preserved else {}), + **({"joint_angle_source": JSON_JOINT_ANGLES, "passive_joint_sources": dict(sources)} + if sources is not None else {}), + "joint_patches": {name: {key: value for key, value in asdict(patch).items() + if value is not None and not (key == "remove_mimic" and value is False)} + for name, patch in sorted(plan.patches.joints.items())}} + + +def _read_correction(payload, expected_profile_id): + if (not isinstance(payload, dict) + or payload.get("format") not in {"unified_calibration_v2", "unified_calibration_v3"} + or payload.get("profile_id") != expected_profile_id): + raise ValueError("calibration_json_profile_or_format_mismatch") + correction = payload.get("urdf_correction") + if not isinstance(correction, dict): + raise ValueError("calibration_json_missing_supported_urdf_correction") + sources = independent_joint_sources(payload) + keys = {"schema_version", "source_urdf_sha256", "joint_patches"} + if sources is not None: + keys.update({"joint_angle_source", "passive_joint_sources"}) + preserved = uses_source_mimic(payload) + if uses_affine_mimic_approximation(payload) or preserved: + keys.update({"joint_angle_source", "mimic_policy"}) + if preserved: + keys.add("mimic_envelopes") + if set(correction) != keys: + raise ValueError("calibration_json_missing_supported_urdf_correction") + digest = correction["source_urdf_sha256"] + if not isinstance(digest, str) or len(digest) != 64 or any(c not in "0123456789abcdef" for c in digest): + raise ValueError("invalid_calibration_source_sha256") + declared = correction["joint_patches"] + allowed = {field.name for field in fields(UrdfJointPatch)} + if sources is None: + allowed.remove("remove_mimic") + if preserved: + allowed -= {"mimic_multiplier", "mimic_offset"} + if not isinstance(declared, dict): + raise ValueError("invalid_calibration_joint_edits") + patches = {} + for name, values in declared.items(): + if not isinstance(values, dict) or not values or not set(values) <= allowed: + raise ValueError(f"invalid_calibration_joint_edits:{name}") + for key, value in values.items(): + if key == "remove_mimic": + if value is not True: + raise ValueError(f"invalid_calibration_mimic_removal:{name}") + continue + if not isinstance(value, str): + raise ValueError(f"calibration_edit_requires_numeric_text:{name}:{key}") + numbers = [float(item) for item in value.split()] + if len(numbers) != (3 if key == "origin_rpy" else 1) or not all(math.isfinite(v) for v in numbers): + raise ValueError(f"invalid_calibration_numeric_edit:{name}:{key}") + patches[name] = UrdfJointPatch(**values) + if sources is not None and {name for name, patch in patches.items() if patch.remove_mimic} != set(sources): + raise ValueError("calibration_mimic_removals_differ_from_joint_control") + return digest, UrdfPatchSet(patches) + + +def _export_authorization(payload, source_urdf, authorized_fields): + sources = independent_joint_sources(payload) + if sources is None: + return authorized_fields + mapping = SerializedJointMapping(payload, input_kind="command") + channels = {name: row["motor_index"] for name, row in mapping.joints.items()} + return independent_export_authorization(UrdfKinematicModel(source_urdf), channels, sources, authorized_fields) + + +def validate_json_urdf_correspondence(payload, source_urdf, corrected_urdf, *, expected_profile_id): + """Prove that the saved correction describes the exact final URDF bytes.""" + digest, patches = _read_correction(payload, expected_profile_id) + source_bytes = Path(source_urdf).read_bytes() + if hashlib.sha256(source_bytes).hexdigest() != digest: + raise ValueError("calibration_json_source_sha256_mismatch") + _export_authorization(payload, source_urdf, {}) + rebuilt = apply_urdf_patch_text(source_bytes.decode("utf-8"), ET.fromstring(source_bytes), patches) + if rebuilt.encode("utf-8") != Path(corrected_urdf).read_bytes(): + raise ValueError("calibration_json_correction_differs_from_urdf") + if uses_source_mimic(payload): + validate_source_mimic_elements(source_urdf, corrected_urdf) + validate_compact_urdf_tables(payload, UrdfKinematicModel(corrected_urdf)) + + +def rebuild_urdf_from_json(calibration_json, source_urdf, destination_urdf, *, + authorized_fields, expected_profile_id): + """Validate and atomically write only edits read from the saved JSON. + + Rebuilding checks source identity, declared field rights, topology, full + mimic ranges and JSON tables. It does not certify measurement precision or + publish a release; the normal final-file validator still owns those checks. + """ + payload = json.loads(Path(calibration_json).read_text(encoding="utf-8")) + digest, patches = _read_correction(payload, expected_profile_id) + authorization = _export_authorization(payload, source_urdf, authorized_fields) + def validate(path): + if uses_source_mimic(payload): + validate_source_mimic_elements(source_urdf, path) + validate_urdf_mimic_ranges(path) + validate_compact_urdf_tables(payload, UrdfKinematicModel(path)) + return write_urdf_patches(source_urdf=source_urdf, destination_urdf=destination_urdf, + patches=patches, authorized_fields=authorization, + expected_source_sha256=digest, validate_output=validate) + + +def main(args=None): + import argparse + from ...product import load_product_config + parser = argparse.ArgumentParser(description="读取标定 JSON,从受保护的原始 URDF 重建修正文件") + parser.add_argument("--config", required=True) + parser.add_argument("--calibration-json", required=True) + parser.add_argument("--output", required=True) + selected = parser.parse_args(args) + config = load_product_config(selected.config, check_can=False) + profile = config.calibration_contract.typed_profile + output = rebuild_urdf_from_json(selected.calibration_json, config.source_urdf, selected.output, + authorized_fields=profile.urdf_authorized_fields, expected_profile_id=profile.key.profile_id) + print(f"已按标定 JSON 重建:{output}。精度结论以原会话验收为准。") diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/branch_diagnostics.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/branch_diagnostics.py new file mode 100644 index 0000000..59f6368 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/branch_diagnostics.py @@ -0,0 +1,32 @@ +"""Small solver lifecycle records, separate from immutable measurement evidence.""" + +import hashlib +import json +import logging + +from ..core.artifacts.storage import append_jsonl_many + + +def record_solver_event(path, request, event, *, stamp_ns, monotonic_time, **details): + """Record even a discarded result without changing a finalized raw journal. + + These events diagnose wall time and commit gates. They never authorize a + branch, a zero or publication, and cannot substitute for source images. + """ + sources = json.dumps(dict(request.source_frame_hashes), sort_keys=True, + separators=(",", ":")).encode() + record = { + "event": event, "stamp_ns": stamp_ns, "monotonic_time": monotonic_time, + "task_name": request.task_name, "view": request.view, + "zero_joints": list(request.zero_joints), "session_epoch": request.session_epoch, + "motion_version": request.motion_version, "generation": request.generation, + "source_frames": len(request.frames), "image_geometry": request.image_geometry, + "source_hashes_sha256": hashlib.sha256(sources).hexdigest(), + "source_first_stamp_ns": request.frames[0].stamp_ns if request.frames else None, + "source_last_stamp_ns": request.frames[-1].stamp_ns if request.frames else None, + **details, + } + try: + append_jsonl_many(path, (record,), durable=False) + except OSError: + logging.getLogger(__name__).exception("Could not record motion solver lifecycle") diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/branch_initialization.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/branch_initialization.py new file mode 100644 index 0000000..2d083fe --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/branch_initialization.py @@ -0,0 +1,301 @@ +"""Bounded evidence from declared zero-arrival motion, owned by the coordinator. + +Mutation is performed under the session lock after its observation-version +gate. A request is an immutable snapshot; the expensive geometry solver runs +outside that lock. Nothing here moves hardware, fits a control curve, or uses +training/holdout observations to revise an existing reference. +""" + +from dataclasses import asdict, dataclass +import hashlib +import json +import math + +from ..core.geometry.tag_pose.motion_evidence import ( + FrozenMotionConstraint, MotionBranchResolution, MotionEvidenceFrame, + MotionRelation, RoleCandidates, resolve_motion_branches, +) +from ..core.geometry.tag_pose.types import SquareTagPose +from ..core.geometry.tag_pose.motion_image_types import ImageMotionFrame, ImageRoleObservation +from ..core.geometry.tag_pose.cad_hinge import ParallelAxisGeometry +from ..core.geometry.tag_pose.production_image_motion import ( + ImageMotionModel, ImageMotionResolution, resolve_image_motion, +) +from .scan_quality import observation_streams +from .motion_provenance import source_frame_sha256 +from ..core.geometry.tag_pose.image_model_selection import IMAGE_MODEL_SELECTION_POLICY + + +@dataclass(frozen=True) +class MotionBranchModel: + task_name: str + view: str + constraints: tuple[FrozenMotionConstraint, ...] + evidence_ids: tuple[tuple[str, str], ...] + image_model: ImageMotionModel | None = None + + +@dataclass(frozen=True) +class BranchInitializationRequest: + task_name: str + view: str + zero_joints: tuple[str, ...] + session_epoch: int + motion_version: int + generation: int + frames: tuple[MotionEvidenceFrame, ...] + relations: tuple[MotionRelation, ...] + existing_evidence_ids: tuple[tuple[str, str], ...] + moving_roles: tuple[str, ...] + source_frame_hashes: tuple[tuple[str, str], ...] = () + invalid_reason: str = "" + image_frames: tuple[ImageMotionFrame, ...] = () + image_geometry: bool = False + geometry_constraints: tuple[ParallelAxisGeometry, ...] = () + source_urdf_sha256: str = "" + + +def _pose(payload): + return SquareTagPose(tuple(payload["quaternion_xyzw"]), + tuple(payload["translation_xyz_m"]), float(payload["reprojection_error_px"])) + + +def _finite_report(value): + if isinstance(value, float) and not math.isfinite(value): + return None + if isinstance(value, dict): + return {key: _finite_report(item) for key, item in value.items()} + if isinstance(value, (list, tuple)): + return [_finite_report(item) for item in value] + return value + + +def solve_initialization(request: BranchInitializationRequest): + """Pure expensive operation; the caller must check request identity again.""" + if request.invalid_reason: + return MotionBranchResolution(False, request.invalid_reason) + if request.image_geometry: + return resolve_image_motion(request.image_frames, request.relations, + geometry_constraints=request.geometry_constraints, source_urdf_sha256=request.source_urdf_sha256) + return resolve_motion_branches(request.frames, request.relations) + + +def initialization_artifacts(request, resolution): + """Bind each newly established physical-Tag branch to its actual evidence.""" + image_model = resolution.model if isinstance(resolution, ImageMotionResolution) else None + constraints = () if isinstance(resolution, ImageMotionResolution) else resolution.constraints + record = { + "kind": "motion_branch_initialization", "schema_version": 1, + "status": "resolved" if resolution.resolved else "unresolved", + "task_name": request.task_name, "view": request.view, + "zero_joints": list(request.zero_joints), + "session_epoch": request.session_epoch, "motion_version": request.motion_version, + "source_image_stamps": [frame.stamp_ns for frame in request.frames], + "source_frame_hashes": dict(request.source_frame_hashes), + "reason": resolution.reason, + "constraints": [asdict(model) for model in constraints], + "hypotheses": _finite_report([asdict(item) for item in resolution.hypotheses]), + "evidence_ids": {}, "evidence_payloads": {}, + } + if image_model is not None: + payload = asdict(image_model) + record["frozen_image_model"] = payload + record["image_model_sha256"] = hashlib.sha256(json.dumps( + payload, sort_keys=True, separators=(",", ":"), allow_nan=False).encode()).hexdigest() + if request.image_geometry: + record["image_model_selection_policy"] = IMAGE_MODEL_SELECTION_POLICY + record["image_geometry_constraints"] = [asdict(item) for item in request.geometry_constraints] + record["source_urdf_sha256"] = request.source_urdf_sha256 + if not resolution.resolved: + return None, record + existing = dict(request.existing_evidence_ids) + for role in request.moving_roles: + if role in existing: + record["evidence_ids"][role] = existing[role] + continue + payload = {key: record[key] for key in ( + "task_name", "view", "zero_joints", "session_epoch", "motion_version", + "source_image_stamps", "source_frame_hashes", "constraints", + )} + if image_model is not None: + payload.update(frozen_image_model=record["frozen_image_model"], + image_model_sha256=record["image_model_sha256"]) + payload["tag_role"] = role + encoded = json.dumps(payload, sort_keys=True, separators=(",", ":"), allow_nan=False) + record["evidence_ids"][role] = hashlib.sha256(encoded.encode()).hexdigest() + record["evidence_payloads"][role] = payload + model = MotionBranchModel(request.task_name, request.view, constraints, + tuple(sorted(record["evidence_ids"].items())), image_model) + return model, record + + +class BranchInitialization: + """At most 129 command bins per view from the final approach segment.""" + + def __init__(self, profile, *, image_geometry=True, source_urdf=None): + self.profile = profile + self.image_geometry = image_geometry + self.source_model = None + self.source_urdf_sha256 = "" + if source_urdf is not None: + from ..core.urdf.kinematics import UrdfKinematicModel + self.source_model = UrdfKinematicModel(source_urdf) + self.source_urdf_sha256 = hashlib.sha256(self.source_model.source.read_bytes()).hexdigest() + self.generation = 0 + self.clear() + + def clear(self): + self.generation += 1 + self.task_name = None + self.zero_joints = () + self.motion_version = None + self.session_epoch = None + self._frames = {} + self._source_records = {} + self._existing_ids = {} + self._held_feedback = None + self._invalid_reason = "" + self._requested = set() + self.reports = {} + + def begin(self, motion, *, session_epoch, motion_version): + if motion.phase == "zero_approach": + self.clear() + self.task_name = motion.task_key + self.zero_joints = motion.reference_joints + self.motion_version = motion_version + self.session_epoch = session_epoch + elif motion.phase == "joint_zero": + if (self.task_name, self.zero_joints, self.session_epoch) != ( + motion.task_key, motion.zero_joints, session_epoch): + self.clear() + self.task_name, self.zero_joints = motion.task_key, motion.zero_joints + self.session_epoch = session_epoch + self._invalid_reason = "motion_approach_evidence_missing" + + def _relations(self, view): + task = next(task for task in self.profile.motion.tasks if task.key == self.task_name) + return tuple(MotionRelation(name, spec.parent_role, spec.child_role) + for _, name, spec in observation_streams(self.profile, task) + if name in self.zero_joints and spec.view == view) + + def observe(self, record): + if (record["task_name"], tuple(record["zero_joints"]), record["motion_version"], + record["session_epoch"]) != ( + self.task_name, self.zero_joints, self.motion_version, self.session_epoch): + return + command, feedback = record["command_vector"], record["feedback_vector"] + if command is None or feedback is None or not self.zero_joints: + return + spec = self.profile.motion.joint_zero_references[self.zero_joints[0]] + task = next(task for task in self.profile.motion.tasks if task.key == self.task_name) + index = task.command_index + # The current measured channel moves; every other declared command + # must already be in this reference's actual clearance/holding pose. + if any(abs(value - spec.command[i]) > 1e-9 + for i, value in enumerate(command) if i != index): + return + if self._held_feedback is None: + self._held_feedback = tuple(feedback) + layout = self.profile.command + for i, value in enumerate(feedback): + tolerance = max(1.0 if layout.unit == "u8" else 0.002, + 0.005 * (layout.maximum_feedback_values[i] - layout.minimum_feedback_values[i])) + if i != index and abs(value - self._held_feedback[i]) > tolerance: + self._invalid_reason = "motion_held_feedback_changed" + return + relations = self._relations(record["view"]) + if not relations: + return + roles = tuple(dict.fromkeys(role for relation in relations + for role in (relation.parent_role, relation.child_role))) + items, existing = [], self._existing_ids.setdefault(record["view"], {}) + for role in roles: + item = record["tags"].get(role) + if item is None: + return + diagnostics = item["candidate_diagnostics"] + if diagnostics.get("observation_stamp_ns") != record["image_stamp_ns"]: + return + frozen = diagnostics.get("branch_frozen") is True + if frozen: + if item["selected_pose"] is None: + return + candidates = (_pose(item["selected_pose"]),) + evidence_id = diagnostics.get("motion_evidence_id") + if evidence_id: + if existing.get(role, evidence_id) != evidence_id: + self._invalid_reason = "motion_frozen_evidence_changed" + return + existing[role] = evidence_id + else: + candidates = tuple(_pose(p) for p in item["reprojection_valid_candidates"]) + items.append(RoleCandidates(role, candidates, frozen)) + lower, upper = layout.minimum_values[index], layout.maximum_values[index] + bin_index = round(128 * (command[index] - lower) / (upper - lower)) + self._frames.setdefault(record["view"], {})[bin_index] = MotionEvidenceFrame( + record["image_stamp_ns"], tuple(items)) + self._source_records.setdefault(record["view"], {})[bin_index] = record + + def request(self, view, motion, *, session_epoch, motion_version): + if motion is None or motion.phase != "joint_zero" or view in self._requested: + return None + if (self.task_name, self.zero_joints, self.session_epoch) != ( + motion.task_key, motion.zero_joints, session_epoch): + return None + relations = self._relations(view) + if not relations: + return None + self._requested.add(view) + roles = {role for relation in relations for role in (relation.parent_role, relation.child_role)} + moving = tuple(sorted(tag.role for v in self.profile.vision.views if v.name == view + for tag in v.tags if tag.role in roles and not tag.fixed_reference)) + frames = tuple(sorted(self._frames.get(view, {}).values(), key=lambda frame: frame.stamp_ns)) + sources = tuple(self._source_records.get(view, {}).values()) + by_stamp = {row["image_stamp_ns"]: row for row in sources} + image_frames, reason = (), self._invalid_reason + if self.image_geometry: + try: + image_frames = tuple(ImageMotionFrame(frame, + tuple(tuple(float(v) for v in values) for values in by_stamp[frame.stamp_ns]["camera_matrix"]), + tuple(ImageRoleObservation(item.role, + float(by_stamp[frame.stamp_ns]["tags"][item.role]["tag_size_m"]), + tuple(tuple(float(v) for v in point) for point in + by_stamp[frame.stamp_ns]["tags"][item.role]["corners_xy"])) + for item in frame.roles)) for frame in frames) + except (KeyError, TypeError, ValueError): + reason = reason or "image_motion_preparation_pixels_missing" + geometry_constraints = () + if self.image_geometry and self.source_model is not None: + from ..profiles.observations import compile_parallel_axis_geometry + geometry_constraints = compile_parallel_axis_geometry(self.profile, self.source_model, relations) + return BranchInitializationRequest(self.task_name, view, self.zero_joints, + session_epoch, self.motion_version if self.motion_version is not None else motion_version, self.generation, + frames, + relations, tuple(sorted(self._existing_ids.get(view, {}).items())), moving, + tuple(sorted((str(row["image_stamp_ns"]), source_frame_sha256(row)) + for row in sources)), reason, image_frames, self.image_geometry, + geometry_constraints, self.source_urdf_sha256 if geometry_constraints else "") + + def pending(self): + """A bounded preparation solve is running outside the state lock.""" + return bool(self._requested - self.reports.keys()) + + def accept(self, request, resolution): + if request.generation != self.generation: + return None + model, report = initialization_artifacts(request, resolution) + self.reports[request.view] = report + return model, report + + def error(self): + for view, report in sorted(self.reports.items()): + if report["status"] != "resolved": + return f"view={view}:joints={','.join(self.zero_joints)}:{report['reason']}" + return "" + + def ready(self, motion): + if (motion.task_key, motion.zero_joints) != (self.task_name, self.zero_joints): + return False + views = [view.name for view in self.profile.vision.views if self._relations(view.name)] + return bool(views) and all(self.reports.get(view, {}).get("status") == "resolved" for view in views) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/branch_tracking.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/branch_tracking.py new file mode 100644 index 0000000..4f252c9 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/branch_tracking.py @@ -0,0 +1,94 @@ +"""Apply an immutable task-local model to actual same-image Tag candidates.""" + +from ..core.geometry.tag_pose.motion_evidence import MotionEvidenceFrame, RoleCandidates, select_motion_frame +from ..core.geometry.tag_pose.motion_image_types import ImageMotionFrame, ImageRoleObservation +from ..core.geometry.tag_pose.tracking import MotionPoseSelection, SquareTagPoseTracker + + +class CommittedPoseEvidence: + """Read the metadata copied atomically with this frame's joint decision.""" + + def __init__(self, tracker, managed_roles, snapshots): + self.tracker = tracker + self.managed_roles = frozenset(managed_roles) + self.snapshots = {item.role: item for item in snapshots} + + def observation_snapshot(self, role, stamp_ns): + if role not in self.managed_roles: + return self.tracker.observation_snapshot(role, stamp_ns) + item = self.snapshots.get(role) + if item is None or item.stamp_ns != stamp_ns: + return None + return item.candidates, dict(item.diagnostics), item.corrections + + +def select_motion_poses(frame, model, tracker, selected, *, base, cached, projection_worker=None): + """Called outside the session lock; tracker commits check image versions.""" + if model.image_model is not None: + return _select_image_poses(frame, model, tracker, selected, projection_worker=projection_worker) + ids = dict(model.evidence_ids) + roles = tuple(dict.fromkeys(role for constraint in model.constraints + for role in (constraint.relation.parent_role, constraint.relation.child_role))) + snapshots, candidates = {}, [] + for role in roles: + if role in ids: + snapshot = tracker.motion_snapshot(role, frame.stamp_ns) + if snapshot is not None and snapshot.motion_managed: + snapshots[role] = snapshot + poses = () if snapshot is None else snapshot.candidates + candidates.append(RoleCandidates(role, poses)) + else: + pose = selected.get(role) + if (pose is None and role == base.role and cached is not None + and role not in frame.corners and role not in frame.observed_roles): + pose = cached[0] # Same verified stationary-cache contract as capture. + candidates.append(RoleCandidates(role, () if pose is None else (pose,), frozen=True)) + decision = select_motion_frame(model.constraints, candidates) + if not decision.resolved or set(snapshots) != set(ids): + reason = "pose_branch_" + (decision.reason if not decision.resolved else "motion_candidates_missing") + rejected = SquareTagPoseTracker.reject_motion_frame(tuple(snapshots.values()), reason) + evidence = CommittedPoseEvidence(tracker, ids, rejected.observation_snapshots) + return {}, {role: rejected.reason for role in ids}, evidence + proposals = tuple(MotionPoseSelection(snapshots[item.role], item.pose, ids[item.role]) + for item in decision.selections if item.role in ids) + result = SquareTagPoseTracker.commit_motion_frame(proposals, + establish=any(snapshot.evidence_id is None for snapshot in snapshots.values())) + evidence = CommittedPoseEvidence(tracker, ids, result.observation_snapshots) + if not result.accepted: + return {}, {role: result.reason for role in ids}, evidence + return {item.snapshot.role: item.pose for item in result.selections}, {}, evidence + + +def _select_image_poses(frame, model, tracker, selected, *, projection_worker=None): + """The model owns its children; already frozen parents keep their identity.""" + image_model = model.image_model + children = {item.relation.child_role for item in image_model.geometry} + ids = {role: identity for role, identity in model.evidence_ids if role in children} + snapshots, candidates, observations = {}, [], [] + for role, size in image_model.tag_sizes: + if role not in frame.corners: + continue + observations.append(ImageRoleObservation(role, size, + tuple(tuple(float(v) for v in point) for point in frame.corners[role]))) + if role in children: + snapshot = tracker.motion_snapshot(role, frame.stamp_ns) + if snapshot is not None and snapshot.motion_managed: + snapshots[role] = snapshot + candidates.append(RoleCandidates(role, snapshot.candidates)) + elif role in selected: + candidates.append(RoleCandidates(role, (selected[role],), frozen=True)) + image_frame = ImageMotionFrame(MotionEvidenceFrame(frame.stamp_ns, tuple(candidates)), + tuple(tuple(float(v) for v in row) for row in frame.camera_matrix), tuple(observations)) + decision = SquareTagPoseTracker.verify_image_motion_frame(tuple(snapshots.values()), + image_model, image_frame, ids, projection_worker=projection_worker) + if not decision.accepted: + reason = "pose_branch_" + decision.reason + rejected = SquareTagPoseTracker.reject_motion_frame(tuple(snapshots.values()), reason) + evidence = CommittedPoseEvidence(tracker, children, rejected.observation_snapshots) + return {}, {role: rejected.reason for role in children}, evidence + result = SquareTagPoseTracker.commit_image_motion_frame(decision, + establish=any(snapshot.evidence_id is None for snapshot in snapshots.values())) + evidence = CommittedPoseEvidence(tracker, children, result.observation_snapshots) + if not result.accepted: + return {}, {role: result.reason for role in children}, evidence + return {item.snapshot.role: item.pose for item in result.selections}, {}, evidence diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/branch_worker.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/branch_worker.py new file mode 100644 index 0000000..6c1a137 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/branch_worker.py @@ -0,0 +1,73 @@ +"""Run immutable geometry requests outside the ROS interpreter and its GIL.""" + +import multiprocessing +import threading +import time +import traceback + +from .branch_initialization import solve_initialization +from .branch_diagnostics import record_solver_event +from ..core.geometry.tag_pose.motion_evidence import MotionBranchResolution + + +def _solve(connection, request, diagnostic_path): + started, cpu = time.monotonic(), time.thread_time() + try: + result = solve_initialization(request) + if diagnostic_path is not None: + record_solver_event(diagnostic_path, request, "worker_solve_finished", + stamp_ns=time.time_ns(), monotonic_time=time.monotonic(), + elapsed_seconds=time.monotonic()-started, + thread_cpu_seconds=time.thread_time()-cpu, + resolved=result.resolved, reason=result.reason) + connection.send((result, None)) + except Exception: + connection.send((None, traceback.format_exc())) + finally: + connection.close() + + +class IsolatedBranchSolver: + """One bounded child per request; children have no SDK or ROS interfaces. + + Cancellation only stops computation. The coordinator's epoch and motion + checks remain the sole authority for accepting a returned resolution. + """ + + def __init__(self, diagnostic_path=None, *, timeout_seconds=120.): + self.diagnostic_path = diagnostic_path + self.timeout_seconds = timeout_seconds + self._closed = threading.Event() + + def __call__(self, request): + if self._closed.is_set(): + return MotionBranchResolution(False, "image_motion_solver_cancelled") + context = multiprocessing.get_context("spawn") + receive, send = context.Pipe(duplex=False) + process = context.Process(target=_solve, args=(send, request, self.diagnostic_path), daemon=True) + started = time.monotonic() + try: + process.start() + send.close() + while not self._closed.is_set(): + if receive.poll(.05): + result, error = receive.recv() + if error is not None: + raise RuntimeError(f"geometry worker failed:\n{error}") + return result + if time.monotonic()-started >= self.timeout_seconds: + return MotionBranchResolution(False, "image_motion_solver_timeout") + if not process.is_alive(): + raise RuntimeError(f"geometry worker exited without result: {process.exitcode}") + return MotionBranchResolution(False, "image_motion_solver_cancelled") + finally: + receive.close() + send.close() + if process.pid is not None: + if process.is_alive(): + process.terminate() + process.join() + process.close() + + def close(self): + self._closed.set() diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/capture.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/capture.py index 7a38a0c..cb0d6a3 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/capture.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/capture.py @@ -1,14 +1,28 @@ """Profile observation capture shared by every SDK and ROS camera host.""" from collections import deque -from dataclasses import dataclass +from dataclasses import asdict, dataclass +from threading import RLock import numpy as np from scipy.spatial.transform import Rotation from ..core.geometry.extrinsics import matrix_payload, transform_matrix from ..core.geometry.pnp import SquareTagPose, SquareTagPoseTracker +from ..core.geometry.tag_pose.fixed_reference import FixedTagReference +from ..core.geometry.image_motion_replay import has_image_model +from .pose_evidence import snapshot_pose_evidence from .scan_quality import observation_streams +from .branch_initialization import MotionBranchModel +from .branch_tracking import select_motion_poses + + +@dataclass(frozen=True) +class CaptureTiming: + received_stamp_ns: int + history_snapshot_stamp_ns: int + latest_feedback_stamp_ns: int | None + latest_command_stamp_ns: int | None @dataclass(frozen=True) @@ -21,31 +35,136 @@ class CaptureFrame: command: tuple[float, ...] | None skew_ns: int = 0 command_directions: tuple[str, ...] = () + observed_roles: frozenset[str] = frozenset() + timing: CaptureTiming | None = None + + +class _FramePoseEvidence: + """Keep each model's atomic current-frame metadata after parent-chain solves.""" + + def __init__(self, tracker): + self.tracker = tracker + self.by_role = {} + + def register(self, roles, snapshot): + self.by_role.update((role, snapshot) for role in roles) + + def observation_snapshot(self, role, stamp_ns): + return self.by_role.get(role, self.tracker).observation_snapshot(role, stamp_ns) class ObservationCapture: - def __init__(self, profile, *, reference_lock, extrinsics, tag_size_m=0.016, trackers=None): + def __init__(self, profile, *, reference_lock, extrinsics, tag_size_m=0.016, trackers=None, + projection_worker=None): self.profile, self.reference_lock, self.extrinsics = profile, reference_lock, extrinsics + self.projection_worker = projection_worker self.tag_size_m = float(tag_size_m) self.trackers = trackers or {view.name: SquareTagPoseTracker() for view in profile.vision.views} - self._seen = set() + self._metadata_lock = RLock() + self._metadata_generation = 0 + self._latest_stamp_by_view = {} self._poses = {} + self._pose_revisions = {} self.locked_poses = {} + self.locked_revisions = {} self.installations = {} + self._installation_revisions = {} self.last_missing = {} self.last_filtered = {} + self._branch_events_active = set() + self._motion_models = {} def reset(self): - self._seen.clear() - self._poses.clear() - self.locked_poses.clear() - self.installations.clear() - self.last_missing.clear() - self.last_filtered.clear() + with self._metadata_lock: + self._metadata_generation += 1 + self._latest_stamp_by_view.clear() + self._poses.clear() + self._pose_revisions.clear() + self.locked_poses.clear() + self.locked_revisions.clear() + self.installations.clear() + self._installation_revisions.clear() + self.last_missing.clear() + self.last_filtered.clear() + self._branch_events_active.clear() + self._motion_models.clear() for tracker in self.trackers.values(): tracker.reset() + def install_motion_model(self, model: MotionBranchModel): + """Add evidence before zero capture without replacing existing roles.""" + key = (model.task_name, model.view) + with self._metadata_lock: + owned = self._model_children(model) + for installed in self._motion_models.values(): + if installed.view != model.view: + continue + shared = owned & self._model_children(installed) + if shared and any(dict(installed.evidence_ids).get(role) != dict(model.evidence_ids).get(role) + for role in shared): + raise ValueError("motion_branch_evidence_replacement_forbidden") + if shared and installed.image_model != model.image_model: + raise ValueError("image_motion_model_replacement_forbidden") + previous = self._motion_models.get(key) + if previous is not None: + ids, new_ids = dict(previous.evidence_ids), dict(model.evidence_ids) + if any(ids[role] != new_ids[role] for role in ids.keys() & new_ids.keys()): + raise ValueError("motion_branch_evidence_replacement_forbidden") + old = {item.relation.joint: item for item in previous.constraints} + for item in model.constraints: + if item.relation.joint in old and old[item.relation.joint] != item: + raise ValueError("motion_constraint_replacement_forbidden") + old[item.relation.joint] = item + if (previous.image_model is not None and model.image_model is not None + and previous.image_model != model.image_model): + raise ValueError("image_motion_model_replacement_forbidden") + model = MotionBranchModel(model.task_name, model.view, + tuple(old.values()), tuple(sorted({**ids, **new_ids}.items())), + image_model=model.image_model or previous.image_model) + if previous != model: + self._metadata_generation += 1 + self._motion_models[key] = model + + @staticmethod + def _model_children(model): + geometry = model.constraints if model.image_model is None else model.image_model.geometry + return {item.relation.child_role for item in geometry} + + def _motion_model_schedule(self, view, motion): + """Keep installed identities while any task, return or idle pose changes. + + A model-authorized Tag can never return to independent IPPE selection. + Tracking all installed models also keeps continuity through clearance + and baseline returns; sample collection remains gated by the task below. + """ + del motion # Collection scope does not determine physical Tag identity. + available = {key: model for key, model in self._motion_models.items() if model.view == view} + if not available: + return () + owners = {role: key for key, model in available.items() for role in self._model_children(model)} + ordered, visiting, visited = [], set(), set() + + def include(key): + if key in visited: + return + if key in visiting: + raise ValueError("image_motion_model_dependency_cycle") + visiting.add(key) + model = available[key] + geometry = model.constraints if model.image_model is None else model.image_model.geometry + roots = {item.relation.parent_role for item in geometry} - self._model_children(model) + for role in sorted(roots): + if role in owners and owners[role] != key: + include(owners[role]) + visiting.remove(key) + visited.add(key) + ordered.append(model) + + for key in sorted(available): + include(key) + return tuple(ordered) + @staticmethod def _median_pose(poses): return SquareTagPose( @@ -53,63 +172,207 @@ class ObservationCapture: translation_xyz_m=tuple(np.median([p.translation_xyz_m for p in poses], axis=0)), reprojection_error_px=float(np.median([p.reprojection_error_px for p in poses]))) - def consume(self, frame: CaptureFrame, motion=None, *, collect_installations=False): - identity = (frame.view, frame.stamp_ns) - if frame.stamp_ns <= 0 or identity in self._seen: + @staticmethod + def _image_model_roles(roles, evidence): + """Include frozen moving parents' complete ancestral image evidence.""" + required, pending = set(roles), list(roles) + while pending: + item = evidence.get(pending.pop(), {}) + model = item.get("frozen_image_model") + if model is None: + continue + ancestors = {role for role, _ in model["tag_sizes"]} - required + required.update(ancestors) + pending.extend(ancestors) + return required + + def _candidate_frame(self, frame, motion, task, roles, evidence, selected, locked): + """Persist the exact decision alongside unchanged original candidates.""" + observations = {} + for role in sorted(roles): + if role not in frame.corners and role not in locked: + continue + item = dict(evidence[role]) + pose = selected.get(role) + item.update(locked_reference=role in locked, + selected=None if pose is None else { + "quaternion_xyzw": list(pose.quaternion_xyzw), + "translation_xyz_m": list(pose.translation_xyz_m), + "reprojection_error_px": pose.reprojection_error_px}, + maximum_reprojection_error_px=self.trackers[frame.view].maximum_reprojection_error_px) + item.pop("selected_pose", None) + observations[role] = item + return {"kind": "pnp_candidate_frame", "task_name": task.key, + "cycle": motion.cycle, "direction": motion.direction, "attempt": motion.attempt, + "view": frame.view, "image_stamp_ns": frame.stamp_ns, "tag_size_m": self.tag_size_m, + "sample_phase": motion.phase if motion.phase in {"steady", "joint_zero"} else "sweep", + "camera_matrix": frame.camera_matrix.tolist(), "camera_matrix_source": "CameraInfo.P[:3,:3]", + "input_is_rectified": True, "roles": observations} + + def consume(self, frame: CaptureFrame, motion=None, *, collect_installations=False, + diagnostic=False, allow_recording=True, accepted_joints: frozenset[str] | None = None, + fixed_reference_only=False): + if fixed_reference_only and (motion is not None or collect_installations): + raise ValueError("fixed reference monitoring cannot acquire motion or installations") + if frame.view not in self.reference_lock.fixed_tag_by_view: return (), None - self._seen.add(identity) + with self._metadata_lock: + if frame.stamp_ns <= self._latest_stamp_by_view.get(frame.view, 0): + return (), None + self._latest_stamp_by_view[frame.view] = frame.stamp_ns + generation = self._metadata_generation + models = () if fixed_reference_only else self._motion_model_schedule(frame.view, motion) view = next(v for v in self.profile.vision.views if v.name == frame.view) + tags = tuple(tag for tag in view.tags if not fixed_reference_only or tag.fixed_reference) base = next(tag for tag in view.tags if tag.fixed_reference) - selected, filtered = {}, [] - for tag in view.tags: + cached = self.locked_reference(frame.view) + fixed_reference = None if cached is None else FixedTagReference(*cached) + selected, filtered, evidence, events, reasons = {}, [], {}, [], {} + evidence_tracker = _FramePoseEvidence(self.trackers[frame.view]) + managed_roles = {role for model in models for role in self._model_children(model)} + for tag in tags: points = frame.corners.get(tag.role) if points is None: continue pose, reason = self.trackers[frame.view].estimate(tag.role, points, - tag_size_m=tag.size_m, camera_matrix=frame.camera_matrix, stamp_ns=frame.stamp_ns) + tag_size_m=tag.size_m, camera_matrix=frame.camera_matrix, stamp_ns=frame.stamp_ns, + fixed_reference=fixed_reference if tag.fixed_reference else None, + **({"motion_managed": True} if tag.role in managed_roles else {})) + reasons[tag.role] = reason + if pose is not None: + selected[tag.role] = pose + for model in models: + poses, rejected, model_evidence = select_motion_poses(frame, model, self.trackers[frame.view], selected, + base=base, cached=cached, + **({"projection_worker": self.projection_worker} if self.projection_worker is not None else {})) + evidence_tracker.register(self._model_children(model), model_evidence) + for role in rejected: + selected.pop(role, None) + selected.update(poses) + reasons.update({role: "" for role in poses}) + reasons.update(rejected) + for tag in tags: + points = frame.corners.get(tag.role) + if points is None: + continue + pose, reason = selected.get(tag.role), reasons[tag.role] + item = snapshot_pose_evidence({tag.role: tag}, {tag.role: points}, + {} if pose is None else {tag.role: pose}, evidence_tracker, (), + stamp_ns=frame.stamp_ns)[tag.role] + diagnostics = item["candidate_diagnostics"] + if diagnostics.get("observation_stamp_ns") != frame.stamp_ns: + pose, reason = None, "pose_branch_observation_superseded" + elif pose is not None and diagnostics.get("branch_status") != "tracking": + pose, reason = None, "pose_branch_unconfirmed" + evidence[tag.role] = item + tracking_rejected = pose is None and ( + reason.startswith("pose_branch") or reason == "fixed_reference_pose_unverified") if pose is None: + selected.pop(tag.role, None) filtered.append(tag.tag_id) + if tracking_rejected: + events.append({"kind": "pnp_branch_event", "view": frame.view, + "image_stamp_ns": frame.stamp_ns, "tag_role": tag.role, + "tag_id": tag.tag_id, "reason": reason, + "task_name": None if motion is None else motion.task_key, + "motion_phase": None if motion is None else motion.phase, + "command_unit": self.profile.command.unit, + "command_vector": None if frame.command is None else list(frame.command), + "feedback_vector": None if frame.feedback is None else list(frame.feedback), + "state_image_sync_error_ms": None if frame.feedback is None else abs(frame.skew_ns)/1e6, + **({"timing": asdict(frame.timing)} if frame.timing is not None else {}), + "pnp_observation_evidence": item}) else: selected[tag.role] = pose - self.last_missing[frame.view] = tuple(tag.tag_id for tag in view.tags if tag.role not in frame.corners) - self.last_filtered[frame.view] = tuple(filtered) - movement = None - if frame.view in self.reference_lock.references and base.role in frame.corners: - # Once locked, fixed-object drift is a CORNER measurement. A large - # real displacement may fail the temporal PnP jump filter and must - # not consequently evade the ten-frame movement protection. - movement = self.reference_lock.observe(frame.view, base.tag_id, frame.corners[base.role]) - if base.role in selected: - # Only independent frames with a valid pose may establish a lock. - if frame.view not in self.reference_lock.references: - movement = self.reference_lock.observe(frame.view, base.tag_id, frame.corners[base.role]) - if self.reference_lock.locking_enabled and frame.view not in self.locked_poses: - buffer = self._poses.setdefault(frame.view, deque(maxlen=30)) - buffer.append(selected[base.role]) - if frame.view in self.reference_lock.references and len(buffer) >= self.reference_lock.minimum_frames: - self.locked_poses[frame.view] = self._median_pose(buffer) + with self._metadata_lock: + if (generation != self._metadata_generation + or self._latest_stamp_by_view.get(frame.view) != frame.stamp_ns): + return (), None + if pose is not None and (frame.view, tag.role) in self._branch_events_active: + events.append({"kind": "pnp_branch_event", "view": frame.view, + "image_stamp_ns": frame.stamp_ns, "tag_role": tag.role, + "tag_id": tag.tag_id, "reason": "pose_branch_recovered", + "pnp_observation_evidence": item}) + self._branch_events_active.discard((frame.view, tag.role)) + if tracking_rejected: + self._branch_events_active.add((frame.view, tag.role)) + with self._metadata_lock: + if (generation != self._metadata_generation + or self._latest_stamp_by_view.get(frame.view) != frame.stamp_ns): + return (), None + self.last_missing[frame.view] = tuple(tag.tag_id for tag in view.tags if tag.role not in frame.corners) + self.last_filtered[frame.view] = tuple(filtered) + movement = self._observe_fixed_reference(frame, base, selected, evidence) # Include the last locking frame exactly once; never update mounting # fingerprints during task motion or while the worker is fitting. if collect_installations and self.reference_lock.locking_enabled: - self._observe_installations(frame.view, base.role, selected, + self._observe_installations(frame.view, base.role, selected, evidence, prefix="" if collect_installations is True else str(collect_installations)+"/") - if movement is not None or motion is None or not motion.recording: - return (), movement + if motion is not None and motion.phase == "zero_approach" and motion.reference_joints: + events.append({"kind": "motion_branch_observation", "view": frame.view, + "image_stamp_ns": frame.stamp_ns, "task_name": motion.task_key, + "zero_joints": list(motion.reference_joints), "sample_phase": "zero_approach", + "command_unit": self.profile.command.unit, + "command_vector": None if frame.command is None else list(frame.command), + "feedback_vector": None if frame.feedback is None else list(frame.feedback), + "state_image_sync_error_ms": None if frame.feedback is None else abs(frame.skew_ns)/1e6, + "camera_matrix": frame.camera_matrix.tolist(), "input_is_rectified": True, + "tags": evidence, "is_accepted_joint_sample": False}) + diagnostic_raw = (diagnostic and motion is not None and ( + motion.phase == "steady" or (motion.phase in {"sweep", "joint_zero"} and allow_recording))) + if diagnostic_raw: + # Raw candidate capture has its own record type. It cannot be + # mistaken for an accepted joint angle or enter formal fitting. + events.append({"kind": "diagnostic_observation_frame", "view": frame.view, + "image_stamp_ns": frame.stamp_ns, "task_name": motion.task_key, + "cycle": motion.cycle, "direction": motion.direction, "attempt": motion.attempt, + "sample_phase": motion.phase, "command_unit": self.profile.command.unit, + **({"zero_joints": list(motion.zero_joints)} if motion.phase == "joint_zero" else {}), + **({"steady_index": motion.steady_index, + "steady_target": None if motion.command_index is None else motion.target[motion.command_index], + "minimum_hold_seconds": motion.minimum_hold_seconds, + "target_command_vector": list(motion.target), + "accepted_sample_window_open": bool(allow_recording)} if motion.phase == "steady" else {}), + "command_vector": None if frame.command is None else list(frame.command), + "feedback_vector": None if frame.feedback is None else list(frame.feedback), + "state_image_sync_error_ms": None if frame.feedback is None else abs(frame.skew_ns)/1e6, + **({"timing": asdict(frame.timing)} if frame.timing is not None else {}), + "camera_matrix": frame.camera_matrix.tolist(), "input_is_rectified": True, + "tags": evidence, "is_accepted_joint_sample": False}) + if movement is not None or motion is None or not motion.recording or not allow_recording: + return tuple(events), movement task = next(t for t in self.profile.motion.tasks if t.key == motion.task_key) streams = [item for item in observation_streams(self.profile, task) if item[2].view == frame.view] if motion.phase == "joint_zero": streams = [item for item in streams if item[1] in motion.zero_joints] + if diagnostic: + streams = [item for item in streams if item[1] in (accepted_joints or ())] if not streams or frame.feedback is None or frame.command is None: - return (), movement - locked = [] - if base.role not in selected and frame.view in self.locked_poses: - selected[base.role] = self.locked_poses[frame.view] + return tuple(events), movement + locked = [role for role, item in evidence.items() + if role in selected and item["pose_source"] == "verified_fixed_reference"] + cached = self.locked_reference(frame.view) + if (base.role not in frame.corners and base.role not in frame.observed_roles + and cached is not None + and self.trackers[frame.view].freeze_roles({base.role: cached[1]})): + selected[base.role] = cached[0] locked.append(base.role) + evidence.update(snapshot_pose_evidence({base.role: base}, {}, + {base.role: selected[base.role]}, self.trackers[frame.view], locked, + stamp_ns=frame.stamp_ns, + reference_revisions={base.role: cached[1]})) roles = {role for _, _, spec in streams for role in (spec.parent_role, spec.child_role)} + image_geometry = any(has_image_model(evidence.get(role, {})) for role in roles) + if image_geometry: + roles = self._image_model_roles(roles, evidence) + if any(role not in selected or role not in frame.corners for role in roles): + # Every ancestor is verified in this image. A partial chain or + # cached root cannot become an accepted constrained observation. + return tuple(events), movement if task.key in self.profile.measurement.candidate_selection_tasks and motion.phase != "joint_zero": if any(role not in selected or (role not in frame.corners and role not in locked) for role in roles): - return (), movement # An incomplete articulated frame is a discarded sample. - rows = [] + return tuple(events), movement # An incomplete articulated frame is a discarded sample. + rows = list(events) common_from_view = self.extrinsics.transform(frame.view) unit = self.profile.command.unit for field, joint, spec in streams: @@ -136,10 +399,15 @@ class ObservationCapture: "view_normal_common_xyz": common_from_view[:3, 2].tolist(), "camera_center_common_xyz_m": common_from_view[:3, 3].tolist(), "locked_reference_roles": locked, - "pnp_reprojection_error_px": max(parent.reprojection_error_px, child.reprojection_error_px)} + "pnp_reprojection_error_px": max(parent.reprojection_error_px, child.reprojection_error_px), + "pnp_observation_evidence": {"parent": evidence[spec.parent_role], + "child": evidence[spec.child_role]}} if motion.phase == "steady": row.update(steady_index=motion.steady_index, steady_target=motion.target[task.command_index]) + if motion.sampling_plan_sha256: + row.update(steady_training_nodes=list(motion.steady_training_nodes), + sampling_plan_sha256=motion.sampling_plan_sha256) if spec.parent_role in frame.corners and spec.child_role in frame.corners: row["image_relative_xy_px"] = (np.mean(frame.corners[spec.child_role], axis=0) - np.mean(frame.corners[spec.parent_role], axis=0)).tolist() @@ -149,39 +417,94 @@ class ObservationCapture: if motion.phase == "joint_zero": row["kind"] = "joint_zero_sample" row["joint"] = joint - if task.key in self.profile.measurement.candidate_selection_tasks: - rows.append({"kind": "pnp_candidate_frame", "task_name": task.key, - "cycle": motion.cycle, "direction": motion.direction, "attempt": motion.attempt, - "view": frame.view, "image_stamp_ns": frame.stamp_ns, "tag_size_m": self.tag_size_m, - "camera_matrix": frame.camera_matrix.tolist(), "camera_matrix_source": "CameraInfo.P[:3,:3]", - "input_is_rectified": True, - "roles": {role: {"corners_xy": np.asarray(frame.corners[role]).tolist() if role in frame.corners else None, - "locked_reference": role in locked, - "tag_size_m": next(tag.size_m for tag in view.tags if tag.role == role), - "selected": None if role not in selected else { - "quaternion_xyzw": list(selected[role].quaternion_xyzw), - "translation_xyz_m": list(selected[role].translation_xyz_m)}, - "maximum_reprojection_error_px": self.trackers[frame.view].maximum_reprojection_error_px} - for role in roles if role in frame.corners or role in locked}}) + if ((task.key in self.profile.measurement.candidate_selection_tasks or image_geometry) + and len(rows) > len(events)): + rows.append(self._candidate_frame(frame, motion, task, roles, evidence, selected, locked)) return tuple(rows), movement - def _observe_installations(self, view, base_role, poses, *, prefix=""): + def _observe_fixed_reference(self, frame, base, selected, evidence): + movement = None + if frame.view not in self.reference_lock.missing_views and base.role in frame.corners: + # Pose rejection must not mask actual image-space movement. + movement = self.reference_lock.observe(frame.view, base.tag_id, frame.corners[base.role]) + if base.role not in selected: + return movement + if frame.view in self.reference_lock.missing_views: + movement = self.reference_lock.observe(frame.view, base.tag_id, frame.corners[base.role]) + if not self.reference_lock.locking_enabled: + return movement + revision = evidence[base.role]["candidate_diagnostics"]["branch_revision"] + with self._metadata_lock: + if frame.view in self.locked_poses: + return movement + generation = self._metadata_generation + buffer = self._poses.setdefault(frame.view, deque(maxlen=30)) + if self._pose_revisions.get(frame.view) != revision: + buffer.clear() + self._pose_revisions[frame.view] = revision + buffer.append(selected[base.role]) + poses = tuple(buffer) + if frame.view in self.reference_lock.missing_views or len(poses) < self.reference_lock.minimum_frames: + return movement + median = self._median_pose(poses) + with self._metadata_lock: + if (generation == self._metadata_generation and frame.view not in self.locked_poses + and self._pose_revisions.get(frame.view) == revision + and self.trackers[frame.view].freeze_roles({base.role: revision})): + self.locked_poses[frame.view] = median + self.locked_revisions[frame.view] = revision + return movement + + def _observe_installations(self, view, base_role, poses, evidence, *, prefix=""): if base_role not in poses: return + with self._metadata_lock: + generation = self._metadata_generation parent = poses[base_role] p = transform_matrix(parent.translation_xyz_m, parent.quaternion_xyzw) + observations = [] for role, pose in poses.items(): if role == base_role: continue + key = f"{prefix}{view}:{role}" + revisions = tuple(evidence[name]["candidate_diagnostics"]["branch_revision"] + for name in (base_role, role)) relative = np.linalg.inv(p) @ transform_matrix(pose.translation_xyz_m, pose.quaternion_xyzw) - buffer = self.installations.setdefault(f"{prefix}{view}:{role}", deque(maxlen=30)) - buffer.append(SquareTagPose( + observations.append((key, revisions, SquareTagPose( quaternion_xyzw=tuple(Rotation.from_matrix(relative[:3, :3]).as_quat()), - translation_xyz_m=tuple(relative[:3, 3]), reprojection_error_px=pose.reprojection_error_px)) + translation_xyz_m=tuple(relative[:3, 3]), reprojection_error_px=pose.reprojection_error_px))) + with self._metadata_lock: + if generation != self._metadata_generation: + return + for key, revisions, pose in observations: + buffer = self.installations.setdefault(key, deque(maxlen=30)) + if self._installation_revisions.get(key) != revisions: + buffer.clear() + self._installation_revisions[key] = revisions + buffer.append(pose) + + def locked_reference(self, view): + with self._metadata_lock: + if view not in self.locked_poses: + return None + return self.locked_poses[view], self.locked_revisions[view] + + def locked_reference_views(self): + with self._metadata_lock: + return frozenset(self.locked_poses) + + def installation_count(self, key): + with self._metadata_lock: + return len(self.installations.get(key, ())) def fingerprint_poses(self): + # Copy both the dictionary and each deque while writers hold the same + # short lock. Rotation means and NumPy work use only immutable snapshots. + with self._metadata_lock: + fixed = tuple(self.locked_poses.items()) + moving = tuple((name, tuple(rows)) for name, rows in self.installations.items()) def payload(pose): return {"rotation_xyzw": list(pose.quaternion_xyzw), "translation_xyz_m": list(pose.translation_xyz_m)} - return ({view: payload(pose) for view, pose in self.locked_poses.items()}, - {name: payload(self._median_pose(rows)) for name, rows in self.installations.items() + return ({view: payload(pose) for view, pose in fixed}, + {name: payload(self._median_pose(rows)) for name, rows in moving if len(rows) >= self.reference_lock.minimum_frames}) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/coordinator.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/coordinator.py index 1d6bb84..4a11122 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/coordinator.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/coordinator.py @@ -19,16 +19,21 @@ import numpy as np from ..acquisition import StateSample, interpolate_state_u8 from ..core.domain.profile import CalibrationProfile from ..core.domain.reference import build_joint_zero_reference -from ..storage import append_jsonl, atomic_write_json -from .engine import ACQUISITION_POLICY_VERSION +from ..core.geometry.pnp import POSE_TRACKING_POLICY_VERSION, SquareTagPoseTracker +from ..storage import append_jsonl, append_jsonl_many, atomic_write_json, sync_jsonl +from .engine import ACQUISITION_POLICY_VERSION, CAPTURE_SCHEDULE_VERSION from .reference_lock import ReferenceLock -from .acquisition import load_capture from .artifacts.controller import FinalizationController, FinalizationInputs, FinalizationProtocolError from .cameras import CameraObservations from .camera_preflight import CameraPreflight -from .capture import CaptureFrame, ObservationCapture +from .capture import CaptureFrame, CaptureTiming, ObservationCapture +from .branch_initialization import BranchInitialization, solve_initialization +from .branch_diagnostics import record_solver_event from .execution import SessionExecution +from .diagnostic_capture import diagnostic_hold_timing, resolve_diagnostic_capture, reject_diagnostic_capture +from .diagnostic_reference import build_diagnostic_zero_attempt from .inputs import AdapterFactory, CameraModelInput, DetectionInput, RuntimePorts, ServiceResult +from .pose_evidence import reference_branch_revisions from .motion_execution import MotionExecution from .parameters import RuntimeParameters from .resume import ResumeDecision, ResumeVerifier, fingerprint_from_mapping @@ -37,10 +42,16 @@ from .session import CalibrationPhase as Phase from .snapshot import DeviceReadiness, CameraReadiness, RuntimeSnapshot, build_snapshot, legacy_state +_TERMINAL_STATES = frozenset({"PASSED", "DIAGNOSTIC_COMPLETE", "PAUSED", "ABORTED", "FAILED"}) + + class CalibrationCoordinator: def __init__(self, profile: CalibrationProfile, parameters: RuntimeParameters, - ports: RuntimePorts, adapter_factory: AdapterFactory, *, finalization=None): + ports: RuntimePorts, adapter_factory: AdapterFactory, *, finalization=None, + initialization_solver=None, projection_worker=None): self.profile, self.parameters, self.ports = profile, parameters, ports + self.initialization_solver = initialization_solver + self.projection_worker = projection_worker self.step_data_lock = threading.RLock() self.model_name = profile.key.model self.command_names = tuple(profile.command.names) @@ -48,12 +59,17 @@ class CalibrationCoordinator: self.command_unit = profile.command.unit self.feedback_lower = tuple(profile.command.minimum_feedback_values) self.feedback_upper = tuple(profile.command.maximum_feedback_values) - self.execution = SessionExecution(profile) + self.diagnostic_capture = resolve_diagnostic_capture(profile, parameters.diagnostic_capture) + if self.diagnostic_capture is not None and ( + not parameters.commands_enabled or parameters.resume_raw_samples_path is not None): + raise ValueError("diagnostic_capture_requires_fresh_command_enabled_session") + self.execution = SessionExecution(profile, diagnostic_capture=self.diagnostic_capture) self.safety = SafetyPolicy(profile) self.reference_lock = self._new_reference_lock() - self.trackers = parameters.new_trackers(profile.vision.view_names) + self.trackers = parameters.new_trackers(self.required_observation_views) self.capture = ObservationCapture(profile, reference_lock=self.reference_lock, - extrinsics=parameters.extrinsics, tag_size_m=parameters.tag_size_m, trackers=self.trackers) + extrinsics=parameters.extrinsics, tag_size_m=parameters.tag_size_m, trackers=self.trackers, + projection_worker=projection_worker) self.latest_feedback: tuple[float, ...] = () self.state_history: deque[StateSample] = deque(maxlen=2000) self.state_receive_times: deque[float] = deque(maxlen=300) @@ -61,6 +77,7 @@ class CalibrationCoordinator: self.command_direction_history = deque(maxlen=2000) self._command_directions = [""]*self.command_count self.raw_records = [] + self._image_record_stamps = {} self.last_command = None self.commanded_speed = None self.step_speed_ready_at = 0.0 @@ -70,17 +87,34 @@ class CalibrationCoordinator: self._observation_epoch = 0 self._pause_effects_applied = False self.segment = None + self._settled_pose = None self._segment_number = 0 self._motion = None + self._motion_started_ns = 0 self._steady_capture_after_ns = None - self._resume_message = "not_requested" + self._zero_geometry_started_at = None + self._zero_geometry_completed_at = None + self._zero_capture_started_at = None + self._resume_message = "diagnostic_capture_no_resume" if self.diagnostic_capture else "not_requested" self._resumed_count = 0 - from .joint_resume import JointResume + self._resume_import = None + from .joint_resume import JointResume, PreparedResume self.joint_resume = JointResume(profile) + self._resume_checkpoint, self._resume_checkpoint_error = None, "" + if parameters.resume_raw_samples_path is not None: + try: + self._resume_checkpoint = PreparedResume.load(profile, self.execution.session.engine, + parameters.resume_raw_samples_path, parameters.serial_number) + except (OSError, ValueError, KeyError, TypeError) as error: + self._resume_checkpoint_error = str(error) self._unit_rows = [] self._capture_unit_key = None self._steady_rows = [] self._zero_reference_error = "" + self._branch_observation_reasons = {} + self.branch_initialization = BranchInitialization(profile, source_urdf=parameters.source_urdf) + from .zero_recovery import ZeroRecovery + self.zero_recovery = ZeroRecovery() self._reference_capture_key = None self._reference_observation_started = None self._current_fingerprint = {} @@ -94,14 +128,23 @@ class CalibrationCoordinator: validate_executable_profile(profile, parameters.source_urdf) parameters.session_dir.mkdir(parents=True, exist_ok=True) self.raw_path = parameters.session_dir / "raw_samples.jsonl" - append_jsonl(self.raw_path, { + self._session_header = { "kind": "session_start", "sample_schema_version": profile.artifacts.output_schema_version, "profile_id": profile.key.profile_id, "serial_number": parameters.serial_number, "curve_input_domain": profile.curve_input_domain, "acquisition_policy_version": ACQUISITION_POLICY_VERSION, + "capture_schedule_version": self.execution.session.engine.capture_schedule_version, + "pose_tracking_policy_version": POSE_TRACKING_POLICY_VERSION, + "required_observation_views": list(self.required_observation_views), **parameters.protected_inputs, "resume_checkpoint_requested": parameters.resume_raw_samples_path is not None, - }) + **({"diagnostic_capture": self.diagnostic_capture.as_dict()} if self.diagnostic_capture else {}), + } + if profile.acquisition.command_capture_mode == "separate" and self.diagnostic_capture is None: + from ..core.fitting.command_sampling import SAMPLING_POLICY + self._session_header["command_sampling_policy"] = SAMPLING_POLICY + append_jsonl(self.raw_path, self._session_header) + self.raw_records.append(self._session_header) self.cameras = CameraObservations(parameters.extrinsics, self.raw_path, ports.monotonic) self.sdk_adapter = adapter_factory(self._write_position, self._write_speed, self.feedback_fresh) self.finalization = finalization or FinalizationController(profile, parameters.session_dir) @@ -114,6 +157,10 @@ class CalibrationCoordinator: def started(self) -> bool: return self._started_at is not None + @property + def required_observation_views(self) -> tuple[str, ...]: + return self.execution.session.required_observation_views + def start(self) -> ServiceResult: with self.step_data_lock: if self.execution.session.phase != Phase.READY or not self.parameters.commands_enabled: @@ -128,14 +175,20 @@ class CalibrationCoordinator: # may finish later, but cannot populate the new formal reference. self.reference_lock = self._new_reference_lock() self.reference_lock.begin_session() - self.trackers = self.parameters.new_trackers(self.profile.vision.view_names) + self.trackers = self.parameters.new_trackers(self.required_observation_views) self.capture = ObservationCapture(self.profile, reference_lock=self.reference_lock, - extrinsics=self.parameters.extrinsics, tag_size_m=self.parameters.tag_size_m, trackers=self.trackers) + extrinsics=self.parameters.extrinsics, tag_size_m=self.parameters.tag_size_m, trackers=self.trackers, + projection_worker=self.projection_worker) self.state_history.clear() self.command_history.clear() self.command_direction_history.clear() self._command_directions = [""]*self.command_count - self.raw_records.clear() + # Both the in-memory and process finalizers consume a self-contained + # capture, including the source identity used by CAD constraints. + self.raw_records[:] = [self._session_header] + self._image_record_stamps.clear() + self._branch_observation_reasons.clear() + self.branch_initialization.clear() self.execution.session.start(resume_requested=self.parameters.resume_raw_samples_path is not None) self._started_at = self.ports.monotonic() self._observation_epoch += 1 @@ -145,6 +198,10 @@ class CalibrationCoordinator: def abort(self) -> ServiceResult: with self.step_data_lock: + if self.state in {"FAILED", "ABORTED"}: + return ServiceResult(False, self.reason) + if self.state == "DIAGNOSTIC_COMPLETE": + return ServiceResult(False, "diagnostic_capture_already_complete") if self.state == "PASSED": return ServiceResult(False, "calibration_already_published") self._pause("operator_abort_holding_current_position") @@ -152,22 +209,42 @@ class CalibrationCoordinator: self.reason = "operator_abort_holding_current_position" return ServiceResult(True, self.reason) - def receive_feedback(self, names, positions, stamp_ns: int) -> None: + def receive_feedback(self, names, positions, stamp_ns: int, *, channel_stamps_ns=()) -> StateSample | None: with self.step_data_lock: - self._accept_feedback(names, positions, stamp_ns) + return self._accept_feedback(names, positions, stamp_ns, channel_stamps_ns=channel_stamps_ns) def receive_camera_info(self, view: str, message: CameraModelInput) -> None: with self.step_data_lock: + if (view not in self.required_observation_views or self.finalization.started + or self.state in _TERMINAL_STATES): + return self.cameras.accept(view, message, started=self.started, pause=self._pause, trackers=self.trackers) + def _record_solver_event(self, request, event, **details): + record_solver_event(self.parameters.session_dir / "motion_solver_diagnostic.jsonl", + request, event, stamp_ns=self.ports.clock_ns(), + monotonic_time=self.ports.monotonic(), **details) + def receive_detections(self, observation: DetectionInput) -> None: view, stamp = observation.view, observation.stamp_ns + received_stamp = self.ports.clock_ns() with self.step_data_lock: + if (view not in self.required_observation_views or self.finalization.started + or self.state in _TERMINAL_STATES): + return self.cameras.detections_received_at[view] = self.ports.monotonic() - if view not in self.cameras.matrices or self.state in {"PASSED", "PAUSED", "ABORTED", "FAILED"}: + if view not in self.cameras.matrices or self.state in _TERMINAL_STATES: return capture, motion = self.capture, self._motion + if motion is not None and stamp < self._motion_started_ns: + return epoch = self._observation_epoch + motion_version = self._segment_number + initialization = self.branch_initialization.request(view, motion, + session_epoch=epoch, motion_version=motion_version) + if initialization is not None and self._zero_geometry_started_at is None: + self._zero_geometry_started_at = self.ports.monotonic() + accepted_joints = self._diagnostic_accepted_joints(motion) steady_after = self._steady_capture_after_ns locking = self._reference_capture_key or self.execution.session.phase == Phase.REFERENCE_LOCKING matrix = self.cameras.matrices[view].copy() @@ -175,10 +252,68 @@ class CalibrationCoordinator: feedback_history = tuple(self.state_history) command_history = tuple(self.command_history) direction_history = tuple(self.command_direction_history) + timing = CaptureTiming(received_stamp, self.ports.clock_ns(), + feedback_history[-1].stamp_ns if feedback_history else None, + command_history[-1].stamp_ns if command_history else None) + if initialization is not None: + solver_started = self.ports.monotonic() + self._record_solver_event(initialization, "request_created", + current_motion_version=motion_version) + try: + resolution = (self.initialization_solver or solve_initialization)(initialization) + except Exception: + self._record_solver_event(initialization, "solve_exception", + elapsed_seconds=self.ports.monotonic()-solver_started, + traceback=traceback.format_exc()) + raise + self._record_solver_event(initialization, "solve_finished", + elapsed_seconds=self.ports.monotonic()-solver_started, + resolved=resolution.resolved, reason=resolution.reason) + with self.step_data_lock: + if (epoch != self._observation_epoch or capture is not self.capture + or motion is not self._motion or motion_version != self._segment_number): + self._record_solver_event(initialization, "commit_rejected", + current_session_epoch=self._observation_epoch, + expected_current_motion_version=motion_version, + current_motion_version=self._segment_number, + capture_changed=capture is not self.capture, + motion_changed=motion is not self._motion) + return + accepted = self.branch_initialization.accept(initialization, resolution) + if accepted is None: + self._record_solver_event(initialization, "commit_rejected", + current_generation=self.branch_initialization.generation) + return + model, report = accepted + append_jsonl(self.raw_path, report) + self.raw_records.append(report) + if not self.branch_initialization.pending(): + self._zero_geometry_completed_at = self.ports.monotonic() + if model is not None: + capture.install_motion_model(model) + # Keep feedback readiness independent of geometry. If a + # stable window already exists, only post-confirmation + # images may establish zero; older callbacks are invalid. + self._steady_rows.clear() + self._zero_capture_started_at = None + if self._steady_capture_after_ns is not None: + self._steady_capture_after_ns = self.ports.clock_ns() + steady_after = self._steady_capture_after_ns + accepted_joints = self._diagnostic_accepted_joints(motion) + self._record_solver_event(initialization, "commit_accepted", + resolved=resolution.resolved) roles = {tag.tag_id: tag.role for tag in self._view(view).tags} - corners = {} + corners, quality_events = {}, [] + observed_roles = frozenset(roles[item.tag_id] for item in observation.detections if item.tag_id in roles) for detection in observation.detections: points = np.asarray(detection.corners, dtype=float) + if detection.border_quality is not None and not detection.border_quality["passed"]: + quality_events.append({"kind": "detection_quality_rejected", "view": view, + "image_stamp_ns": stamp, "tag_id": detection.tag_id, + "corners_xy": points.tolist(), "quality": dict(detection.border_quality), + "task_name": None if motion is None else motion.task_key, + "motion_phase": None if motion is None else motion.phase}) + continue if (detection.tag_id not in roles or points.shape != (4, 2) or not np.all(np.isfinite(points)) or detection.hamming > self.parameters.detection.maximum_hamming or detection.decision_margin < self.parameters.detection.minimum_decision_margin or np.min(np.linalg.norm(points-np.roll(points, -1, axis=0), axis=1)) < self.parameters.detection.minimum_edge_pixels @@ -192,29 +327,133 @@ class CalibrationCoordinator: if row.stamp_ns <= stamp and stamp-row.stamp_ns <= self.parameters.maximum_state_image_skew_ns), None) frame = CaptureFrame(view, stamp, matrix, corners, None if matched is None else matched[0], command, 0 if matched is None else matched[1], - next((d for t, d in reversed(direction_history) if t <= stamp), ())) + next((d for t, d in reversed(direction_history) if t <= stamp), ()), observed_roles, timing) record_motion = motion if motion is not None and motion.phase in {"steady", "joint_zero"} and (steady_after is None or stamp < steady_after): record_motion = None + from .image_capture import image_observation_record + raw_image = image_observation_record(self.profile, frame, record_motion) # Pose estimation is deliberately outside the state lock. Start swaps # capture objects; pause/abort invalidates the epoch; movement changes # the motion identity. None of their late results may enter the session. - rows, movement = capture.consume(frame, record_motion, collect_installations=locking) + rows, movement = capture.consume(frame, motion, collect_installations=locking, + diagnostic=self.diagnostic_capture is not None, allow_recording=record_motion is not None, + accepted_joints=accepted_joints) + rows = (*(() if raw_image is None else (raw_image,)), *quality_events, *rows) with self.step_data_lock: if (epoch != self._observation_epoch or capture is not self.capture - or motion is not self._motion): + or motion is not self._motion or motion_version != self._segment_number + or stamp <= self._image_record_stamps.get(view, 0)): return if motion is not None and motion.phase in {"steady", "joint_zero"} and steady_after != self._steady_capture_after_ns: - return + if self.diagnostic_capture is None or motion.phase != "steady" or motion.minimum_hold_seconds <= 0: + return + # The diagnostic needs the actual approach and recovery, too. + # Its unaccepted raw images are retained while ordinary joint + # samples still require an unchanged stable-feedback window. + rows = tuple(row for row in rows if row.get("kind") in { + "diagnostic_observation_frame", "pnp_branch_event"}) + record_motion = None if movement is not None: self._pause(f"fixed_reference_moved:{view}:{movement.drift_px}") return - for row in rows: + branch_events = [row for row in rows if row.get("kind") == "pnp_branch_event"] + self.raw_records.extend(branch_events) + # Per-record fsync while holding this lock stalls SDK feedback + # and command ticks. Persist references and scan checkpoints; + # flush continuous image evidence in batches between them. + append_jsonl_many(self.raw_path, branch_events, durable=False) + for row in branch_events: + if self._accept_branch_event(row): + return + sample_rows = [row for row in rows if row.get("kind") != "pnp_branch_event" + and (record_motion is not None + or (self.diagnostic_capture is not None and motion is not None + and motion.phase == "steady" and motion.minimum_hold_seconds > 0 + and row.get("kind") == "diagnostic_observation_frame") + or row.get("kind") not in { + "joint_zero_sample", "joint_sample", "secondary_joint_sample", "pnp_candidate_frame", "image_observation_frame", + "diagnostic_observation_frame"})] + for row in sample_rows: + if row.get("kind") == "image_observation_frame": + self._image_record_stamps[view] = stamp + row.update(session_epoch=epoch, motion_version=motion_version, + motion_started_ns=self._motion_started_ns) + if row.get("kind") == "diagnostic_observation_frame": + row.update(session_epoch=epoch, motion_version=motion_version, + motion_started_ns=self._motion_started_ns) + if motion.phase == "steady" and motion.minimum_hold_seconds > 0: + row.update(diagnostic_hold_timing(command_history, target=motion.target, + image_stamp_ns=stamp, session_epoch=epoch, motion_version=motion_version, + motion_started_ns=self._motion_started_ns, + feedback_stable_since_ns=steady_after)) + row["accepted_sample_window_open"] = record_motion is not None + if row.get("kind") == "motion_branch_observation": + row.update(session_epoch=epoch, motion_version=motion_version) + self.branch_initialization.observe(row) + self.raw_records.append(row) + continue + for item in row.get("pnp_observation_evidence", {}).values(): + diagnostics = item["candidate_diagnostics"] + if (diagnostics.get("observation_stamp_ns") == row.get("image_stamp_ns") + and diagnostics.get("branch_status") == "tracking"): + self._branch_observation_reasons.pop((row["view"], item["tag_role"]), None) self.raw_records.append(row) self._unit_rows.append(row) if motion is not None and motion.phase in {"steady", "joint_zero"}: self._steady_rows.append(row) - append_jsonl(self.raw_path, row) + if row.get("kind") == "joint_zero_sample": + row["capture_timing"] = { + "received_stamp_ns": received_stamp, + "committed_stamp_ns": self.ports.clock_ns(), + "history_snapshot_stamp_ns": timing.history_snapshot_stamp_ns, + } + if row.get("kind") == "joint_zero_sample" and self._zero_capture_started_at is None: + self._zero_capture_started_at = self.ports.monotonic() + append_jsonl_many(self.raw_path, sample_rows, durable=False) + + def _diagnostic_accepted_joints(self, motion): + """Immutable sample permissions, captured under the observation lock.""" + if self.diagnostic_capture is None: + return None + if motion is not None and motion.phase == "joint_zero": + return frozenset(motion.zero_joints) if self.branch_initialization.ready(motion) else frozenset() + return frozenset(self.execution.zero_references) + + def _accept_branch_event(self, row): + """Apply captured diagnostics only after the ordinary observation version gate.""" + if row["view"] not in self.required_observation_views: + return False + diagnostics = row["pnp_observation_evidence"]["candidate_diagnostics"] + if diagnostics.get("observation_stamp_ns") != row["image_stamp_ns"]: + return False + key = (row["view"], row["tag_role"]) + if diagnostics.get("branch_status") == "tracking": + self._branch_observation_reasons.pop(key, None) + return False + self._branch_observation_reasons[key] = (row["image_stamp_ns"], row["reason"]) + tag = next((tag for tag in self._view(row["view"]).tags + if tag.tag_id == row["tag_id"] and tag.role == row["tag_role"]), None) + if tag is None or row["reason"] != "pose_branch_conflict": + return False + required = tag.fixed_reference + unit = self.execution.session.current_unit + if unit is not None: + from .scan_quality import observation_streams + task = next(task for task in self.profile.motion.tasks if task.key == unit.task_key) + required = required or any(spec.view == row["view"] + and tag.role in {spec.parent_role, spec.child_role} + and (self._motion is None or self._motion.phase != "joint_zero" or name in self._motion.zero_joints) + for _, name, spec in observation_streams(self.profile, task)) + if required: + reason = f"{row['reason']}:view={row['view']}:tag={tag.tag_id}:role={tag.role}" + if tag.fixed_reference and diagnostics.get("pose_constraint") == "fixed_reference": + error = diagnostics.get("fixed_reference_reprojection_error_px", "unavailable") + reason = (f"fixed_reference_pose_inconsistent:view={row['view']}:tag={tag.tag_id}:" + f"role={tag.role}:reprojection_error_px={error}") + self._pause(reason) + return True + return False def tick(self) -> None: with self.step_data_lock: @@ -231,10 +470,14 @@ class CalibrationCoordinator: def close(self) -> None: with self.step_data_lock: self._observation_epoch += 1 - self.finalization.close() + self.branch_initialization.clear() + try: + sync_jsonl(self.raw_path) + finally: + self.finalization.close() def _advance(self): - if self.state in {"PASSED", "PAUSED", "ABORTED", "FAILED"}: + if self.state in _TERMINAL_STATES: return now = self.ports.monotonic() session = self.execution.session @@ -272,22 +515,24 @@ class CalibrationCoordinator: self._poll_finalization() return phase = session.phase + if self._resume_import is not None: + self._publish_command(list(current)) + self._import_resume_batch() + return if phase in {Phase.PREPARE, Phase.MAPPING_PROBE} and self.segment is None and session.current_unit is not None: used, imported = self.joint_resume.take_task(session.current_unit.task_key) if used: - self.execution.first_cycle_spans.update(self.joint_resume.spans_for_task(session.current_unit.task_key)) - for row in imported: - self.raw_records.append(row) - append_jsonl(self.raw_path, row) - self.execution.authorize_task_reuse(used) - self._resumed_count += len(used) + from .joint_resume import TaskResumeImport + self._resume_import = TaskResumeImport(session.current_unit.task_key, used, imported) + self._publish_command(list(current)) + self._import_resume_batch() return if phase == Phase.REFERENCE_LOCKING: self._publish_command(list(current)) if not self.reference_lock.locking_enabled: self.reference_lock.start_locking() self.reason = "locking_fixed_base_references:missing="+",".join(self.reference_lock.missing_views) - if self.reference_lock.locked and set(self.capture.locked_poses) == set(self.profile.vision.view_names): + if self.reference_lock.locked and self.capture.locked_reference_views() == set(self.required_observation_views): session.reference_locked() if session.phase != Phase.REFERENCE_POSES: self._save_reference() @@ -298,17 +543,32 @@ class CalibrationCoordinator: if phase == Phase.EVALUATE: action = self.execution.action quality = self.execution.evaluate(self.raw_records) - append_jsonl(self.raw_path, {"kind": "scan_unit_complete", "task_name": action.scan_unit.task_key, + record = {"kind": "scan_unit_complete", "task_name": action.scan_unit.task_key, "cycle": action.scan_unit.cycle, "direction": action.scan_unit.direction, "attempt": action.retry+1, "passed": quality.passed, - "failures": list(quality.failures), "metrics": dict(quality.metrics)}) + "failures": list(quality.failures), "metrics": dict(quality.metrics), + **({"quality_scope": "diagnostic_observation_coverage", + "accepted_pose_quality": self.execution.last_diagnostic_quality.accepted_pose_report(), + "observation_timing": dict(self.execution.last_diagnostic_quality.observation_timing)} + if self.execution.last_diagnostic_quality is not None else {})} + self.raw_records.append(record) + append_jsonl(self.raw_path, record) if session.phase == Phase.PAUSED: - self._pause("sweep_quality_failed:"+",".join(quality.failures)) + if "observation_timing" in record: + pause = session.status().pause + session.pause(pause.code, pause.reason, + {**pause.details, "observation_timing": record["observation_timing"]}, + suggestion=pause.suggestion) + task = next(task for task in self.profile.motion.tasks if task.key == action.scan_unit.task_key) + self._pause("sweep_quality_failed:"+",".join(quality.failures)+self._task_branch_details(task)) return if phase == Phase.FIT: self._motion = None self._finalize() return + if (phase == Phase.PREPARE and session.current_unit is not None + and session.current_unit.sample_phase == "steady" and self.diagnostic_capture is None): + self._prepare_command_sampling(session.current_unit.task_key) motion = self.execution.motion(current) if motion is None: if phase == Phase.PREPARE and session.current_unit is None: @@ -316,6 +576,8 @@ class CalibrationCoordinator: return self._pause(f"unhandled_session_phase:{phase.value}") return + if self.segment is None and self._reuse_settled_preparation(motion, now): + return if self.segment is None: if self.commanded_speed != motion.speed: self._publish_speed(motion.speed) @@ -325,7 +587,11 @@ class CalibrationCoordinator: return self._segment_number += 1 self._motion = motion + self._motion_started_ns = self.ports.clock_ns() self._steady_capture_after_ns = None + self._zero_geometry_started_at = None + self._zero_geometry_completed_at = None + self._zero_capture_started_at = None self._reference_capture_key = None self._reference_observation_started = None self._begin_motion_capture(motion) @@ -333,7 +599,7 @@ class CalibrationCoordinator: initial_feedback=self.latest_feedback, now=now, identity=str(self._segment_number)) shaped = self.segment.sample(now) self._publish_command(list(shaped)) - self.segment.observe(self.latest_feedback, stamp=self.state_receive_times[-1], now=now) + self.segment.observe(self.latest_feedback, stamp=self.state_history[-1].stamp_ns, now=now) self.reason = motion.phase+":"+str(motion.task_key) steady_complete = False if motion.phase == "steady": @@ -346,9 +612,9 @@ class CalibrationCoordinator: if r.get(field) == joint and r.get("view") == spec.view and r.get("sample_phase") == "steady"}) >= self.profile.acquisition.steady_minimum_samples for field, joint, spec in observation_streams(self.profile, task)) + steady_complete = steady_complete and self.segment.minimum_hold_complete(now) else: - self._steady_capture_after_ns = None - self._steady_rows.clear() + self._reset_steady_capture(motion, now) if motion.phase == "joint_zero": if self.segment.steady_ready(now): if self._steady_capture_after_ns is None: @@ -357,12 +623,49 @@ class CalibrationCoordinator: if session.phase == Phase.PAUSED: return else: - self._steady_capture_after_ns = None - self._steady_rows.clear() - if self.segment.arrived(now) and not steady_complete: - self._pause("joint_zero_unavailable:"+self._zero_reference_error) + self._reset_steady_capture(motion, now) + if self.branch_initialization.pending(): + # Optimizing the immutable preparation images is not a loss + # of current Tag observations. SDK feedback/safety keeps + # updating while this bounded operation holds the same pose. + self.reason = "joint_zero_geometry_solving:" + str(motion.task_key) + if (self._zero_geometry_started_at is not None + and now-self._zero_geometry_started_at > 120.0): + self._pause("joint_zero_motion_unresolved:image_motion_solver_timeout") return - if self.segment.arrived(now) or steady_complete: + # A long preparation solve can require the tracker to recover + # before any zero image is valid. Give that recovery the existing + # observation timeout, then collect the fixed number of stable + # frames within the dedicated zero window. Zero needs ten images; + # an ordinary mapping hold needs only three. Only the + # first accepted zero sample starts it; dropouts and feedback + # resets never extend it. Recovery plus capture remains bounded, + # with no relaxation of sample quality. + timeout = self.profile.acquisition.steady_timeout_seconds + capture_timeout = self.profile.acquisition.joint_zero_timeout_seconds + zero_started = self._zero_geometry_completed_at + if zero_started is not None and self._zero_capture_started_at is not None: + zero_started = min(self._zero_capture_started_at, zero_started+timeout) + timeout = capture_timeout + observation_timed_out = self.segment.arrived(now) and ( + zero_started is None or now-zero_started >= timeout) + if observation_timed_out and not steady_complete: + geometry_error = self.branch_initialization.error() + if geometry_error and self._complete_diagnostic_zero_attempt(motion, now, geometry_error): + steady_complete = True + elif geometry_error: + if self._recover_joint_zero(motion, now, geometry_error): + return + if self.diagnostic_capture is not None: + geometry_error += ":diagnostic_reference=" + self._zero_reference_error + self._pause("joint_zero_motion_unresolved:" + geometry_error) + return + else: + if self._recover_joint_zero(motion, now, ""): + return + self._pause("joint_zero_unavailable:"+self._zero_reference_error+self._joint_zero_branch_details(motion)) + return + if (self.segment.arrived(now) and motion.phase != "joint_zero") or steady_complete: if motion.phase == "reference_pose": waypoint = next(w for w in self.profile.motion.resume_verification_waypoints if w.key == motion.task_key) if self._reference_observation_started is None: @@ -371,19 +674,84 @@ class CalibrationCoordinator: required = [f"{waypoint.key}/{view.name}:{tag.role}" for view in self.profile.vision.views for tag in view.tags if tag.tag_id in waypoint.tag_ids_by_view.get(view.name, ())] - enough = all(len(self.capture.installations.get(key, ())) >= self.reference_lock.minimum_frames for key in required) + enough = all(self.capture.installation_count(key) >= self.reference_lock.minimum_frames for key in required) if not enough and now-self._reference_observation_started < 2.0: return # Missing installation evidence invalidates reuse, not the # entire fresh capture. Never forge a matching fingerprint. self._reference_capture_key = None + if self.segment.steady_ready(now) or ( + motion.phase not in {"steady", "joint_zero"} and self.segment.arrived(now)): + self._settled_pose = (tuple(motion.target), tuple(self.latest_feedback), now) self.execution.motion_complete() if phase == Phase.REFERENCE_POSES and session.phase != phase: self._save_reference() self.segment, self._motion = None, None + if session.phase == Phase.DIAGNOSTIC_COMPLETE: + self._finish_diagnostic_capture() + + def _reset_steady_capture(self, motion, now): + if self._steady_capture_after_ns is not None or self._steady_rows: + record = {"kind": "steady_capture_reset", "stamp_ns": self.ports.clock_ns(), + "task_name": motion.task_key, "motion_phase": motion.phase, + "discarded_joint_samples": sum(r.get("kind") in {"joint_sample", "joint_zero_sample"} + for r in self._steady_rows), + "feedback_window_seconds": self.profile.acquisition.steady_window_seconds, + "feedback_history": [{"age_seconds": now-stamp, "values": list(values)} + for stamp, values in list(self.segment.stamped_history)[-20:]]} + self.raw_records.append(record) + append_jsonl_many(self.raw_path, (record,), durable=False) + self._steady_capture_after_ns = None + self._steady_rows.clear() + + def _finish_diagnostic_capture(self): + """Save diagnostics under the state lock before exposing completion.""" + from .diagnostic_report import build_diagnostic_report + self._observation_epoch += 1 + try: + sync_jsonl(self.raw_path) + report = build_diagnostic_report(self.profile, self.diagnostic_capture, + raw_path=self.raw_path, records=self.raw_records, + references=self.execution.zero_references, + diagnostic_zero_attempts=self.execution.diagnostic_zero_attempts, + completed_units=self.execution.completed_units, + expected_units=self.execution.session.engine.scan_units(), + protected_inputs=self._current_fingerprint.get("protected_hashes", self.parameters.protected_inputs), + command=self.last_command, feedback=self.latest_feedback) + atomic_write_json(self.parameters.session_dir / "diagnostic_capture.json", report) + except (OSError, ValueError, TypeError, KeyError) as error: + self.execution.session.fail("diagnostic_capture_report_failed", str(error)) + self.reason = f"diagnostic_capture_report_failed:{error}" + return + self.reason = "diagnostic_capture_complete_not_calibration_acceptance" + + def _complete_diagnostic_zero_attempt(self, motion, now, geometry_error): + """Continue diagnostics only after a real stable raw reference visit.""" + if self.diagnostic_capture is None: + return False + if not self.segment.steady_ready(now): + self._zero_reference_error = "diagnostic_joint_zero_feedback_unstable" + return False + try: + attempt = build_diagnostic_zero_attempt(self.profile, motion, self._steady_rows, + reason=geometry_error, session_epoch=self._observation_epoch, + motion_version=self._segment_number) + except ValueError as error: + self._zero_reference_error = str(error) + return False + self.execution.record_diagnostic_zero_attempt(attempt) + row = attempt.as_record() + append_jsonl(self.raw_path, row) + self.raw_records.append(row) + return True def _lock_joint_zeros(self, motion): + if not self.branch_initialization.ready(motion): + self._zero_reference_error = (self.branch_initialization.error() + or "motion_geometry_confirmation_pending") + return False references = {} + branch_rows = [] try: for name in motion.zero_joints: rows = [r for r in self._steady_rows if r.get("kind") == "joint_zero_sample" and r.get("joint") == name] @@ -397,6 +765,9 @@ class CalibrationCoordinator: key=lambda r: r["image_stamp_ns"])[-minimum:]] references[name] = build_joint_zero_reference(self.profile, name, rows, session_epoch=self._observation_epoch, motion_version=self._segment_number) + branch_rows.extend(rows) + revisions = reference_branch_revisions(branch_rows, profile=self.profile, + require_motion_evidence=True) except ValueError as error: self._zero_reference_error = str(error) return False @@ -407,16 +778,117 @@ class CalibrationCoordinator: except ValueError as error: self._pause(str(error)) return False + moving = {(view.name, tag.role) for view in self.profile.vision.views + for tag in view.tags if not tag.fixed_reference} + if not SquareTagPoseTracker.freeze_references([ + (self.trackers[view], role, revision) for (view, role), revision in revisions.items() + ], require_motion_evidence=tuple((self.trackers[view], role) + for view, role in revisions if (view, role) in moving)): + self._zero_reference_error = "joint_zero_motion_branch_not_confirmed" + return False self.execution.zero_references.update(references) + provenance = self.joint_resume.take_motion_provenance_records() + append_jsonl_many(self.raw_path, provenance) + self.raw_records.extend(provenance) for reference in references.values(): row = reference.as_record() self.raw_records.append(row) append_jsonl(self.raw_path, row) return True + def _reuse_settled_preparation(self, motion, now): + if motion.phase != "prepare" or self._settled_pose is None: + return False + command, feedback, stamp = self._settled_pose + tolerance = .001 if self.command_unit == "rad" else 1. + if (tuple(motion.target) != command or tuple(self.last_command or ()) != command + or now-stamp > 1.0 or len(feedback) != len(self.latest_feedback) + or any(abs(old-new) > tolerance for old, new in zip(feedback, self.latest_feedback))): + return False + # The previous segment already completed its endpoint/feedback hold. + # Repeating an identical preparatory target needs no second dwell. + self.execution.motion_complete() + record = {"kind": "preparation_reused_settled_pose", "task_name": motion.task_key, + "cycle": motion.cycle, "direction": motion.direction, "stamp_ns": self.ports.clock_ns()} + append_jsonl_many(self.raw_path, (record,), durable=False) + self.raw_records.append(record) + return True + + def _prepare_command_sampling(self, task_key): + if task_key in self.execution.sampling_plans: + return + from ..core.fitting.command_sampling import build_sampling_plan + existing = [row for row in self.raw_records if row.get("kind") == "command_sampling_plan" + and row.get("task_name") == task_key] + if existing: + plan = existing[0] # PreparedResume already verified its source and content. + else: + task = next(task for task in self.profile.motion.tasks if task.key == task_key) + plan = build_sampling_plan(self.profile, task, self.raw_records) + append_jsonl(self.raw_path, plan) + self.raw_records.append(plan) + self.execution.sampling_plans[task_key] = plan + + def _recover_joint_zero(self, motion, now, geometry_error): + if self.diagnostic_capture is not None: + return False + action = self.zero_recovery.take(motion, geometry_error=geometry_error, + reports=self.branch_initialization.reports, referenced_joints=self.execution.zero_references) + if action is None: + return False + record = {"kind": "joint_zero_recovery", "task_name": motion.task_key, + "joints": list(motion.zero_joints), "action": action, "reason": geometry_error or self._zero_reference_error, + "attempt": 1, "stamp_ns": self.ports.clock_ns()} + append_jsonl(self.raw_path, record) + self.raw_records.append(record) + self._observation_epoch += 1 + self._steady_rows.clear() + self._zero_capture_started_at = None + self._steady_capture_after_ns = None + if action == "repeat_approach": + self.execution.repeat_zero_approach(self.last_command or motion.target) + self.branch_initialization.clear() + self._motion = self.segment = None + self._zero_geometry_started_at = self._zero_geometry_completed_at = None + else: + # Keep the frozen model and current physical holding pose. Only + # a fresh image window is opened; feedback and base guards run. + self._zero_geometry_completed_at = now + self.reason = "joint_zero_recovery:" + action + ":" + str(motion.task_key) + return True + + def _joint_zero_branch_details(self, motion): + task = next(task for task in self.profile.motion.tasks if task.key == motion.task_key) + return self._task_branch_details(task, joints=motion.zero_joints) + + def _task_branch_details(self, task, *, joints=None): + from .scan_quality import observation_streams + details = [] + required_views = set() + for _, name, spec in observation_streams(self.profile, task): + if joints is not None and name not in joints: + continue + required_views.add(spec.view) + for role in (spec.parent_role, spec.child_role): + previous = self._branch_observation_reasons.get((spec.view, role)) + if previous is None: + continue + tag = next(tag for tag in self._view(spec.view).tags if tag.role == role) + details.append(f"{name}:{spec.view}:tag={tag.tag_id}:{previous[1]}") + for view in self.profile.vision.views: + if view.name not in required_views: + continue + for tag in view.tags: + previous = self._branch_observation_reasons.get((view.name, tag.role)) + if tag.fixed_reference and previous is not None: + details.append(f"fixed_reference:{view.name}:tag={tag.tag_id}:{previous[1]}") + return "" if not details else ":pose_branches="+",".join(sorted(set(details))) + def _begin_motion_capture(self, motion): - """Keep direction statistics across its separate steady checkpoints.""" + """Aggregate one direction across its moving and stationary segments.""" self._steady_rows = [] + self.branch_initialization.begin(motion, session_epoch=self._observation_epoch, + motion_version=self._segment_number) if motion.recording: key = (motion.task_key, motion.cycle, motion.direction, motion.attempt) if key != self._capture_unit_key: @@ -436,7 +908,8 @@ class CalibrationCoordinator: # These are the expected configuration identities, not live # measurements of serial numbers or physical camera geometry. "expected_cameras": {view: asdict(identity) - for view, identity in self.parameters.extrinsics.cameras.items()}, + for view, identity in self.parameters.extrinsics.cameras.items() + if view in self.required_observation_views}, **report.as_dict(), } atomic_write_json(self.parameters.session_dir / "camera_preflight.json", record) @@ -462,7 +935,7 @@ class CalibrationCoordinator: if self.ports.command_publisher_count() > 1: waiting.append("存在其他机械手指令发布者") cameras = {} - for view in self.profile.vision.view_names: + for view in self.required_observation_views: info_at = self.cameras.info_received_at.get(view) detection_at = self.cameras.detections_received_at.get(view) info_ready = (view in self.cameras.matrices and info_at is not None @@ -478,10 +951,30 @@ class CalibrationCoordinator: waiting.append(f"{view} 未收到新鲜的 Tag 检测消息(检查图像、整流和检测节点)") return DeviceReadiness(not waiting, tuple(waiting), cameras) - def _accept_feedback(self, names, positions, stamp: int) -> None: + def _accept_feedback(self, names, positions, stamp: int, *, channel_stamps_ns=()) -> StateSample | None: state = self.sdk_adapter.parse_feedback(names, positions) if state is None: return + if stamp <= 0 and not channel_stamps_ns: + stamp = self.ports.clock_ns() + if channel_stamps_ns: + from .adapters.base import ordered_feedback_values + ordered = ordered_feedback_values(self.sdk_adapter.command_layout, names, channel_stamps_ns) + if (ordered is None or any(type(value) is not int or value <= 0 for value in ordered) + or max(ordered) != stamp): + return + channel_stamps_ns = ordered + now_ns = self.ports.clock_ns() + if (max(ordered) > now_ns+self.parameters.maximum_state_image_skew_ns + or now_ns-min(ordered) > self.profile.acquisition.feedback_stale_seconds*1e9): + return + if self.state_history and stamp <= self.state_history[-1].stamp_ns: + return # Duplicate/out-of-order publication is not a new measurement. + if self.state_history and channel_stamps_ns: + previous = self.state_history[-1] + if previous.channel_stamps_ns and any(new < old or (new == old and value != old_value) + for new, old, value, old_value in zip(channel_stamps_ns, previous.channel_stamps_ns, state, previous.position_u8)): + return violation = next(( (index, value) for index, value in enumerate(state) @@ -500,12 +993,23 @@ class CalibrationCoordinator: f"upper={self.feedback_upper[index]:.9f}" ) return - if stamp <= 0: - stamp = self.ports.clock_ns() + received_at = self.ports.monotonic() if not self.state_history or stamp > self.state_history[-1].stamp_ns: - self.state_history.append(StateSample(stamp, state)) + self.state_history.append(StateSample(stamp, state, tuple(channel_stamps_ns))) + if self.segment is not None: + # Stability is a property of independent SDK observations, + # not of how often the control timer happened to run. + self.segment.observe(state, stamp=stamp, now=received_at) self.latest_feedback = state - self.state_receive_times.append(self.ports.monotonic()) + self.state_receive_times.append(received_at - max(0, self.ports.clock_ns()-min(channel_stamps_ns))/1e9 + if channel_stamps_ns else received_at) + if self.started and channel_stamps_ns and not self.finalization.started: + record = {"kind": "sdk_feedback_sample", "stamp_ns": stamp, + "received_stamp_ns": self.ports.clock_ns(), "source": "can_feedback_v1", + "state_values": list(state), "channel_stamps_ns": list(channel_stamps_ns)} + self.raw_records.append(record) + append_jsonl_many(self.raw_path, (record,), durable=False) + return self.state_history[-1] def _publish_command(self, values): if self.parameters.commands_enabled: @@ -551,10 +1055,11 @@ class CalibrationCoordinator: def _pause(self, reason: str) -> None: with self.step_data_lock: - if self.state in {"PASSED", "ABORTED"} or self._pause_effects_applied: + if self.state in {"PASSED", "DIAGNOSTIC_COMPLETE", "ABORTED", "FAILED"} or self._pause_effects_applied: return self.finalization.cancel() self._observation_epoch += 1 + self.branch_initialization.clear() self._pause_effects_applied = True if self.execution.session.phase != Phase.PAUSED: from .reporting.reasons_zh import reason_zh @@ -604,6 +1109,7 @@ class CalibrationCoordinator: tag.tag_id for tag in view.tags if tag.fixed_reference ) for view in self.profile.vision.views + if view.name in self.required_observation_views }, minimum_frames=self.profile.acquisition.fixed_reference_minimum_frames, maximum_corner_drift_px=( @@ -619,25 +1125,34 @@ class CalibrationCoordinator: self._current_fingerprint = {"profile_id": self.profile.key.profile_id, "acquisition_policy_version": self.execution.session.engine.checkpoint_token, "protected_hashes": {**self.parameters.protected_inputs, "intrinsics_sha256": hashlib.sha256( - json.dumps({v: m.tolist() for v, m in sorted(self.cameras.matrices.items())}, sort_keys=True).encode()).hexdigest()}, + json.dumps({v: self.cameras.matrices[v].tolist() + for v in sorted(self.required_observation_views)}, sort_keys=True).encode()).hexdigest()}, "fixed_corners_by_view": self.reference_lock.fingerprint(), "fixed_poses": fixed, "moving_tag_poses": moving} append_jsonl(self.raw_path, {"kind": "fixed_base_reference_locked", **self._current_fingerprint}) + def _import_resume_batch(self): + pending = self._resume_import + rows = pending.take_batch() + append_jsonl_many(self.raw_path, rows, durable=False) + self.raw_records.extend(rows) + if pending.complete: + # No scan is skipped until every imported row is durable. Between + # batches the ordinary feedback, abort and safety callbacks run. + sync_jsonl(self.raw_path) + self.execution.first_cycle_spans.update(self.joint_resume.spans_for_task(pending.task_key)) + self.execution.authorize_task_reuse(pending.units) + self._resumed_count += len(pending.units) + self._resume_import = None + def _verify_resume(self): decision = ResumeDecision(False, "基准变化,已放弃旧断点并重新采集") rows = [] try: - rows = load_capture(self.parameters.resume_raw_samples_path) - headers = [r for r in rows if r.get("kind") == "session_start"] - references = [r for r in rows if r.get("kind") == "fixed_base_reference_locked"] - if len(headers) != 1 or len(references) != 1: - raise ValueError("missing unique checkpoint header/reference") - header = headers[0] - if header.get("serial_number") != self.parameters.serial_number or not self.execution.session.engine.resume_compatible(header): - raise ValueError("checkpoint identity/policy changed") - complete = {(r["task_name"], r["cycle"], r["direction"]) for r in rows - if r.get("kind") == "scan_unit_complete" and r.get("passed") is True} + checkpoint = self._resume_checkpoint + if checkpoint is None: + raise ValueError(self._resume_checkpoint_error or "resume_checkpoint_not_prepared") + rows, header, complete = checkpoint.rows, checkpoint.header, checkpoint.completed_units used_tasks = {key[0] for key in complete} from .scan_quality import observation_streams required = {f"{spec.view}:{role}" for task in self.profile.motion.tasks if task.key in used_tasks @@ -649,7 +1164,7 @@ class CalibrationCoordinator: verifier = ResumeVerifier(required_fixed_views=self.profile.vision.view_names, required_fixed_poses=self.profile.vision.view_names, required_moving_poses=required, required_hashes=self._current_fingerprint["protected_hashes"]) - previous = {**references[0], "acquisition_policy_version": header["acquisition_policy_version"]} + previous = {**checkpoint.fixed_reference, "acquisition_policy_version": header["acquisition_policy_version"]} from .resume import select_installation_evidence old, new = select_installation_evidence(fingerprint_from_mapping(previous), fingerprint_from_mapping(self._current_fingerprint), required) @@ -657,19 +1172,18 @@ class CalibrationCoordinator: if decision.reuse: from dataclasses import replace if self.profile.artifacts.output_schema_version >= 3: - self.joint_resume.stage(self.execution.session.engine, rows, complete) + self.joint_resume = checkpoint.joints decision = replace(decision, reason="整场参考通过,等待逐关节验证原零位后复用") else: decision = replace(decision, completed_units=tuple(sorted(complete))) except (OSError, ValueError, KeyError, TypeError) as error: - decision = ResumeDecision(False, decision.reason, (str(error),)) + decision = ResumeDecision(False, "旧断点校验未通过,已重新采集", (str(error),)) decision = self.execution.restore(decision, rows) if decision.reuse: used = set(decision.completed_units) imported = [r for r in rows if (r.get("task_name"), r.get("cycle"), r.get("direction")) in used] - for row in imported: - self.raw_records.append(row) - append_jsonl(self.raw_path, row) + append_jsonl_many(self.raw_path, imported) + self.raw_records.extend(imported) self._resumed_count = len(used) self._resume_message = decision.reason append_jsonl(self.raw_path, {"kind": "resume_verification", "reuse": decision.reuse, @@ -686,13 +1200,24 @@ class CalibrationCoordinator: return 0.0 if elapsed <= 0 else (len(self.state_receive_times) - 1) / elapsed def _finalize(self) -> None: + if self.diagnostic_capture is not None: + self._pause("diagnostic_capture_not_for_publication_or_resume") + return if self.finalization.started or self.execution.session.phase != Phase.FIT: return + sync_jsonl(self.raw_path) + # Closing acquisition freezes the already collected camera/reference + # evidence. Later images cannot invalidate or mutate the fit input; + # feedback, device safety and explicit abort remain live in _advance. + self._observation_epoch += 1 + self.branch_initialization.clear() self.reason = "fitting_validating_and_writing_artifacts" self.finalization.start(FinalizationInputs( self.parameters.session_dir, self.parameters.serial_number, self.parameters.source_urdf, self._current_fingerprint.get("protected_hashes", self.parameters.protected_inputs), - tuple(self.raw_records), + tuple(self.raw_records) + tuple(self.cameras.models.values()), require_motion_evidence=True, + journal_path=self.raw_path, journal_size=self.raw_path.stat().st_size, + camera_extrinsics_file=self.parameters.camera_extrinsics_file, )) def _poll_finalization(self) -> None: @@ -702,14 +1227,10 @@ class CalibrationCoordinator: return correction = result[2] except FinalizationProtocolError as error: - self._pause(f"finalization_protocol_error:{error}") + self._fail_finalization("finalization_protocol_error", error) return except Exception as error: - atomic_write_json(self.parameters.session_dir / "failure_diagnostic.json", { - "profile_id": self.profile.key.profile_id, "reason": str(error), - "traceback": traceback.format_exc(), "source_urdf_unchanged": True, - }) - self._pause(f"fit_or_publication_failed:{error}") + self._fail_finalization("fit_or_publication_failed", error) return self.execution.session.published() self.reason = "partial_calibration_passed" if self.profile.scope.default_scope != "full" else "full_calibration_passed" @@ -717,6 +1238,33 @@ class CalibrationCoordinator: serial_number=self.parameters.serial_number, side=self.profile.key.side, model=self.model_name.lower())) self.final_urdf = str(correction.path) + def _fail_finalization(self, code: str, error: Exception) -> None: + """End a completed acquisition without suggesting another motion run. + + Numeric, final-file and worker-protocol failures share this boundary. + Feedback/safety failures during motion still use the ordinary pause. + Cancellation and epoch invalidation precede diagnostic I/O so even an + unwritable report cannot leave a worker authorized to publish. + """ + stage = self.execution.session.phase.value + self.finalization.cancel() + self._observation_epoch += 1 + self.branch_initialization.clear() + self._pause_effects_applied = True + self.reason = f"{code}:{error}" + details = {"stage": stage, "raw_reason": self.reason, + "raw_samples": str(self.raw_path), + "completed_unit_count": len(self.execution.completed_units), + "expected_unit_count": len(self.execution.session.engine.scan_units())} + self.execution.session.fail(code, "采集已结束,拟合或文件验收失败", details, + suggestion="保留原始采集和配置,先离线复算并检查失败诊断;仅在证据不足时重新采集。") + if self.last_command is not None: + self._publish_command(list(self.last_command)) + diagnostic = {"profile_id": self.profile.key.profile_id, "reason": str(error), + "state": "FAILED", **details, "traceback": traceback.format_exc()} + atomic_write_json(self.parameters.session_dir / "failure_diagnostic.json", diagnostic) + append_jsonl(self.raw_path, {"kind": "finalization_failed", **diagnostic}) + def _finalizer_event(self, event: str) -> None: session = self.execution.session if event == "fit_complete": @@ -731,5 +1279,8 @@ class CalibrationCoordinator: raise ValueError(f"unknown finalizer event:{event}") def _authorize_commit(self) -> None: + if self.diagnostic_capture is not None: + raise RuntimeError("diagnostic_capture_not_for_publication_or_resume") if self.execution.session.phase != Phase.PUBLISH: raise RuntimeError("finalization did not complete all acceptance stages") + sync_jsonl(self.raw_path) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/detection_dispatch.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/detection_dispatch.py new file mode 100644 index 0000000..3152d3a --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/detection_dispatch.py @@ -0,0 +1,98 @@ +"""Bounded image work outside ROS callbacks, with measurement-time lookahead.""" + +from concurrent.futures import ThreadPoolExecutor +from threading import Condition +import math +import time + + +class DetectionDispatcher: + """One active and one pending image per view; SDK callbacks remain short. + + A detected image can arrive before the matching CAN observation is + delivered. Wait at most the existing synchronization tolerance for that + observation, without blocking ROS feedback or motor commands. The capture + layer still enforces its original timestamp and observation-quality gates. + """ + + def __init__(self, views, deliver, *, maximum_wait_seconds, failed): + self._deliver, self._failed = deliver, failed + self._maximum_wait = float(maximum_wait_seconds) + if not math.isfinite(self._maximum_wait) or self._maximum_wait <= 0 or not views or len(set(views)) != len(views): + raise ValueError("invalid image dispatch views or synchronization wait") + self._condition = Condition() + self._pending = dict.fromkeys(views) + self._generation, self._closed = 0, False + self._feedback_stamp_ns = 0 + self._error = None + self._executor = ThreadPoolExecutor(max_workers=len(views), thread_name_prefix="calibration_image") + self._workers = [self._executor.submit(self._guarded_run, view) for view in views] + + def _guarded_run(self, view): + try: + self._run(view) + except Exception as error: + with self._condition: + self._error = error + self._failed(error) + + def raise_if_failed(self): + with self._condition: + if self._error is not None: + raise RuntimeError("image dispatch failed") from self._error + + def submit(self, observation): + with self._condition: + if self._closed: + return + if observation.view not in self._pending: + raise ValueError("undeclared image dispatch view") + self._pending[observation.view] = observation + self._condition.notify_all() + + def feedback_available(self, stamp_ns): + """Advance to the oldest channel of an accepted measurement snapshot. + + The queue never calls back into the coordinator while holding its + condition. Control messages can therefore wake image workers without + waiting for another worker to acquire the session lock. + """ + with self._condition: + self._feedback_stamp_ns = max(self._feedback_stamp_ns, stamp_ns) + self._condition.notify_all() + + def clear(self): + with self._condition: + self._generation += 1 + self._feedback_stamp_ns = 0 + self._pending = dict.fromkeys(self._pending) + self._condition.notify_all() + + def _run(self, view): + while True: + with self._condition: + self._condition.wait_for(lambda: self._closed or self._pending[view] is not None) + if self._closed: + return + observation, generation = self._pending[view], self._generation + self._pending[view] = None + deadline = time.monotonic()+self._maximum_wait + while not self._closed and generation == self._generation: + if self._feedback_stamp_ns >= observation.stamp_ns: + break + remaining = deadline-time.monotonic() + if remaining <= 0: + break + self._condition.wait(timeout=remaining) + if self._closed: + return + if generation != self._generation: + continue + self._deliver(observation) + + def close(self): + with self._condition: + self._closed = True + self._pending = dict.fromkeys(self._pending) + self._condition.notify_all() + self._executor.shutdown(wait=True, cancel_futures=True) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_analysis.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_analysis.py new file mode 100644 index 0000000..87cbbd3 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_analysis.py @@ -0,0 +1,298 @@ +"""Analyze one stopped capture, without moving hardware or authorizing poses. + +Each acquisition stage and session keeps its own image identities and mounting +context. This report is separate from calibration artifacts and the original +capture report; none of its fitted geometry can enter the live reference path. +""" + +from __future__ import annotations + +import argparse +from collections import defaultdict +from dataclasses import asdict +import hashlib +import json +import math +from pathlib import Path + +from ..core.geometry.tag_pose.motion_evidence import MotionEvidenceFrame, MotionRelation, RoleCandidates +from ..core.geometry.tag_pose.types import SquareTagPose +from ..storage import atomic_write_json +from .motion_provenance import source_frame_sha256 +from .scan_quality import observation_streams + + +def _file_sha256(path): + digest = hashlib.sha256() + with path.open("rb") as source: + for block in iter(lambda: source.read(1024 * 1024), b""): + digest.update(block) + return digest.hexdigest() + + +def _check_output_targets(raw_path, output): + raw_path = raw_path.resolve() + for name in ("diagnostic_analysis.json", "diagnostic_analysis.md"): + target = output / name + if target.resolve() == raw_path or (target.exists() and target.samefile(raw_path)): + raise ValueError("diagnostic_analysis_output_overwrites_source") + + +def _pose(payload): + return SquareTagPose(tuple(payload["quaternion_xyzw"]), + tuple(payload["translation_xyz_m"]), float(payload["reprojection_error_px"])) + + +def image_frames(records, relations): + """Convert detached, same-image evidence; never substitute a later pose.""" + from ..core.geometry.tag_pose.motion_image_diagnostics import ImageMotionFrame, ImageRoleObservation + + roots = {relation.parent_role for relation in relations} - {relation.child_role for relation in relations} + roles = tuple(dict.fromkeys(role for relation in relations + for role in (relation.parent_role, relation.child_role))) + frames = [] + for row in records: + stamp = row["image_stamp_ns"] + candidates, observations = [], [] + for role in roles: + item = row.get("tags", {}).get(role) + if item is None or item.get("candidate_diagnostics", {}).get("observation_stamp_ns") != stamp: + break + diagnostics = item["candidate_diagnostics"] + # This offline comparison may inspect rejected moving candidates, + # including old captures. Preserve the verified root, but never + # reinterpret this counterfactual fit as a new live authorization. + frozen = role in roots and diagnostics.get("branch_frozen") is True + payloads = ([item["selected_pose"]] if frozen and item.get("selected_pose") is not None + else [] if frozen else item.get("reprojection_valid_candidates", ())) + if not payloads or item.get("corners_xy") is None: + break + candidates.append(RoleCandidates(role, tuple(_pose(p) for p in payloads), frozen)) + observations.append(ImageRoleObservation(role, float(item["tag_size_m"]), + tuple(tuple(point) for point in item["corners_xy"]))) + else: + frames.append(ImageMotionFrame(MotionEvidenceFrame(stamp, tuple(candidates)), + tuple(tuple(values) for values in row["camera_matrix"]), tuple(observations))) + return tuple(frames) + + +def _stages(profile, rows): + observations = {} + sweeps = defaultdict(dict) + headers = [] + initializations = [] + for row in rows: + kind = row.get("kind") + if kind == "session_start": + headers.append(row) + if kind == "motion_branch_initialization": + initializations.append(row) + if kind not in {"motion_branch_observation", "diagnostic_observation_frame"}: + continue + identity = (row["view"], row["image_stamp_ns"]) + if identity in observations and observations[identity] != row: + raise ValueError("diagnostic_analysis_image_identity_changed") + observations[identity] = row + if kind == "diagnostic_observation_frame" and row.get("sample_phase") == "sweep": + if row.get("cycle") == 3: + raise ValueError("diagnostic_analysis_refuses_holdout_training") + key = (row["task_name"], row["view"], row["cycle"], row["direction"], row.get("attempt", 1)) + sweeps[key][row["image_stamp_ns"]] = row + if len(headers) != 1 or headers[0].get("profile_id") != profile.key.profile_id: + raise ValueError("diagnostic_analysis_profile_or_session_mismatch") + tasks = {task.key: task for task in profile.motion.tasks} + stages = [] + for number, initial in enumerate(initializations): + task = tasks[initial["task_name"]] + relations = tuple(MotionRelation(name, spec.parent_role, spec.child_role) + for _, name, spec in observation_streams(profile, task) + if name in initial["zero_joints"] and spec.view == initial["view"]) + selected = [] + for stamp in initial["source_image_stamps"]: + row = observations.get((initial["view"], stamp)) + if row is None or any(row.get(key) != initial.get(key) + for key in ("task_name", "zero_joints", "session_epoch", "motion_version")): + raise ValueError("diagnostic_analysis_initialization_source_mismatch") + expected = initial.get("source_frame_hashes", {}).get(str(stamp)) + if expected is None or source_frame_sha256(row) != expected: + raise ValueError("diagnostic_analysis_initialization_hash_mismatch") + selected.append(row) + stages.append(({"key": f"initialization:{number}", "task_name": task.key, + "view": initial["view"], "sample_phase": "zero_approach", + "recorded_branch_status": initial["status"], + "recorded_branch_reason": initial["reason"], + "recorded_hypotheses": initial["hypotheses"]}, relations, tuple(selected))) + for (task_key, view, cycle, direction, attempt), images in sorted(sweeps.items()): + task = tasks[task_key] + relations = tuple(MotionRelation(name, spec.parent_role, spec.child_role) + for _, name, spec in observation_streams(profile, task) if spec.view == view) + if not relations: + continue # Retained camera context is not a task measurement. + stages.append(({"key": f"sweep:{task_key}:{view}:{cycle}:{direction}:{attempt}", + "task_name": task_key, "view": view, "sample_phase": "sweep", + "cycle": cycle, "direction": direction, "attempt": attempt}, relations, + tuple(row for _, row in sorted(images.items())))) + return headers[0], stages + + +def _image_tables(report): + lines = [] + labels = {"interleaved": "交错留出", "early_to_late": "前段训练→后段验证", + "late_to_early": "后段训练→前段验证"} + for hypothesis in report.get("hypotheses", ()): + label = ", ".join(f"{role}={index}" for role, index in hypothesis["branches"]) + lines += [f"### 候选 {label}", ""] + if hypothesis.get("reason"): + lines += [f"分析限制:`{hypothesis['reason']}`。", ""] + pnp = hypothesis.get("pnp_geometry", ()) + if pnp: + lines += ["| 关节 | 原 PnP 转心 RMS | 原 PnP 位置 P95 | 深度误差占比 |", + "| --- | ---: | ---: | ---: |"] + lines += [f"| {item['joint']} | {item['translation_rms_m']*1000:.3f} mm | " + f"{item['translation_p95_m']*1000:.3f} mm | {item['camera_depth_squared_error_fraction']*100:.1f}% |" + for item in pnp] + lines.append("") + intervals = hypothesis.get("intervals", ()) + if intervals: + lines += ["| 独立图像检查 | Tag | 留出图像数 | RMS | 逐帧 RMS 的 P95 | 优化收敛 |", + "| --- | --- | ---: | ---: | ---: | --- |"] + for interval in intervals: + state = "是(不代表精度通过)" if interval["converged"] else "否" + for item in interval.get("validation_quality", ()): + lines.append(f"| {labels.get(interval['name'], interval['name'])} | {item['role']} | " + f"{item['frames']} | {item['rms_px']:.3f} px | {item['frame_rms_p95_px']:.3f} px | {state} |") + lines.append("") + if hypothesis.get("repeatability"): + lines += ["| 关节 | 前后段转轴方向差 |", "| --- | ---: |"] + lines += [f"| {item['joint']} | {math.degrees(item['early_late_axis_difference_rad']):.3f}° |" + for item in hypothesis["repeatability"]] + lines += ["", "轴方向差可与现行分支检查的 3° 要求对照;该图像诊断不会代替原验收。", ""] + return lines + + +def _summary_markdown(report): + lines = ["# 原始观测的图像几何诊断", "", + "本报告只解释原始角点,不授权姿态分支、关节零位、JSON 或 URDF。", + "各任务和方向分别拟合;未跨会话拼接,也未作为第四轮精度验收。", "", + f"来源:`{report['raw_samples_path']}`", f"SHA-256:`{report['raw_samples_sha256']}`", ""] + if report.get("missing_evidence"): + lines += ["尚缺以下记录,当前报告不能补出这些真实观测:", ""] + lines += [f"- `{item}`" for item in report["missing_evidence"]] + lines.append("") + if report.get("status") == "failed": + lines += [f"分析未完成:`{report['reason']}`", "原始采集数据仍然保留。"] + for stage in report.get("stages", ()): + lines += [f"## {stage['task_name']} / {stage['view']} / {stage.get('direction', '归零准备')}", "", + f"原始图像 {stage['source_image_count']} 张,可用完整图像 {stage['usable_image_count']} 张。"] + if "recorded_branch_status" in stage: + lines.append(f"原分支判定:`{stage['recorded_branch_status']}`;原因:`{stage['recorded_branch_reason']}`。") + image = stage["image_diagnostics"] + lines += [f"图像诊断:`{image['status']}`;原因:`{image.get('reason', '')}`。", "", + "候选编号只在本阶段有效。下列所有候选均保留,没有把最低分候选写成正确零位。", ""] + lines.extend(_image_tables(image)) + lines += ["## 如何使用", "", + "- 采集完成只代表原始观测已保存;未确认关节仍没有有效零位和角度样本。", + "- 图像误差较小不能换算成毫米精度通过,也不能单凭最低分选择一个分支。", + "- 分段模型不一致或多个候选仍可解释图像时,保留未确认结论。", + "- 正式标定仍须满足原有几何、JSON 映射和独立第四轮验收。", ""] + return "\n".join(lines) + + +def _missing_evidence(profile, header, stages): + missing = [] if stages else ["no_recorded_motion_images"] + diagnostic = header.get("diagnostic_capture") + if not diagnostic: + return missing + observed = {(stage["task_name"], stage["view"], stage.get("cycle"), stage.get("direction")) + for stage in stages if stage["sample_phase"] == "sweep"} + for task in profile.motion.tasks: + if task.key not in diagnostic.get("task_keys", ()): + continue + views = {spec.view for _, _, spec in observation_streams(profile, task)} + for view in sorted(views): + for cycle in diagnostic.get("cycles", ()): + for direction in ("decreasing", "increasing"): + if (task.key, view, cycle, direction) not in observed: + missing.append(f"sweep:{task.key}:{view}:{cycle}:{direction}") + if not any(stage["sample_phase"] == "zero_approach" for stage in stages): + missing.append("no_pre_zero_motion_record; old_sweeps_cannot_replace_initialization") + return missing + + +def analyze_capture(profile, raw_path: Path, *, output_dir: Path | None = None, progress=None): + """Generate bounded per-stage image diagnostics after the ROS stack stops.""" + from ..core.geometry.tag_pose.motion_image_diagnostics import analyze_motion_images + + raw_path = raw_path.resolve() + output = raw_path.parent if output_dir is None else output_dir.resolve() + _check_output_targets(raw_path, output) + before = _file_sha256(raw_path) + with raw_path.open(encoding="utf-8") as source: + rows = [json.loads(line) for line in source if line.strip()] + header, stages = _stages(profile, rows) + analyzed = [] + for index, (identity, relations, records) in enumerate(stages): + if progress is not None: + progress(f"分析观测 {index+1}/{len(stages)}:{identity['task_name']} / {identity['view']} / " + + identity.get("direction", "归零准备")) + frames = image_frames(records, relations) + result = analyze_motion_images(frames, relations) + analyzed.append({**identity, "source_image_count": len(records), + "usable_image_count": len(frames), "image_diagnostics": asdict(result)}) + if _file_sha256(raw_path) != before: + raise ValueError("diagnostic_analysis_source_changed_during_read") + report = {"format": "calibration_image_diagnostic_v1", "schema_version": 1, + "scope": "diagnostic_not_branch_authorization", "status": "complete", + "accuracy_validated": False, "publication_allowed": False, + "moving_candidates_counterfactual": True, + "raw_samples_path": str(raw_path), "raw_samples_sha256": before, + "profile_id": profile.key.profile_id, + "source_protected_inputs": {key: value for key, value in header.items() if key.endswith("_sha256")}, + "stages": analyzed, "missing_evidence": _missing_evidence(profile, header, analyzed), + "limitations": ["Independent sessions are never combined.", + "Per-stage image fits do not authorize a branch or certify 3D accuracy.", + "Training images are not independent validation; split metrics are reported separately."]} + output.mkdir(parents=True, exist_ok=True) + atomic_write_json(output / "diagnostic_analysis.json", report) + (output / "diagnostic_analysis.md").write_text(_summary_markdown(report), encoding="utf-8") + return report + + +def finish_diagnostic_analysis(profile, session: Path): + """Analysis failure must not relabel a completed capture as an SDK failure.""" + try: + report = analyze_capture(profile, session / "raw_samples.jsonl", progress=lambda message: print(message, flush=True)) + except Exception as error: + # This is the boundary between a completed acquisition and optional + # offline computation, including failures raised by numerical libraries. + # KeyboardInterrupt/SystemExit still propagate to the runner. + report = {"status": "failed", "scope": "diagnostic_not_branch_authorization", + "reason": str(error), "accuracy_validated": False, "publication_allowed": False, + "raw_samples_path": str(session / "raw_samples.jsonl"), "raw_samples_sha256": ""} + try: + _check_output_targets(session / "raw_samples.jsonl", session) + atomic_write_json(session / "diagnostic_analysis.json", report) + (session / "diagnostic_analysis.md").write_text(_summary_markdown(report), encoding="utf-8") + except (OSError, ValueError) as write_error: + print(f"离线分析结果也无法保存:{write_error}。原始采集状态保持不变。", flush=True) + print(f"原始采集已完成,但离线图像分析未完成:{error}。数据已保留,可离线复算。", flush=True) + print(f"图像几何诊断:{session / 'diagnostic_analysis.md'}", flush=True) + return report + + +def main(args=None): + from ..product import load_product_config + + parser = argparse.ArgumentParser(description="离线分析已保存的运动 Tag 角点,不启动实机、不发布标定产物") + parser.add_argument("--config", required=True) + parser.add_argument("--raw", required=True) + parser.add_argument("--output", default=None) + selected = parser.parse_args(args) + config = load_product_config(selected.config, check_can=False) + analyze_capture(config.calibration_contract.typed_profile, Path(selected.raw), + output_dir=None if selected.output is None else Path(selected.output), + progress=lambda message: print(message, flush=True)) + + +if __name__ == "__main__": + main() diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_capture.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_capture.py new file mode 100644 index 0000000..1800c48 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_capture.py @@ -0,0 +1,112 @@ +"""Explicit, non-publishing capture presets over the shared motion schedule.""" + +from dataclasses import asdict, dataclass +import math +from numbers import Real +from typing import Mapping, Sequence + +from ..core.domain.profile import CalibrationProfile +from ..profiles.diagnostics import DIAGNOSTIC_CAPTURE_PRESETS +from ..acquisition import StateSample + + +def _finite_number(value) -> bool: + return isinstance(value, Real) and not isinstance(value, bool) and math.isfinite(value) + + +@dataclass(frozen=True) +class DiagnosticCapturePlan: + mode: str + task_keys: tuple[str, ...] + cycles: tuple[int, ...] = (0,) + hold_command_values: tuple[float, ...] = () + hold_seconds: float = 0.0 + + def __post_init__(self) -> None: + # The declared diagnostic allows at most ten seconds per arrival; + # extending that motion contract requires an explicit preset review. + if (not _finite_number(self.hold_seconds) + or (not self.hold_command_values and self.hold_seconds != 0.0) + or (self.hold_command_values and not 0.0 < self.hold_seconds <= 10.0)): + raise ValueError("diagnostic_hold_seconds_invalid") + if (not isinstance(self.hold_command_values, tuple) + or any(not _finite_number(value) for value in self.hold_command_values) + or any(left >= right for left, right in zip( + self.hold_command_values, self.hold_command_values[1:]))): + raise ValueError("diagnostic_hold_values_invalid") + + def as_dict(self) -> dict: + result = asdict(self) + if not self.hold_command_values: + result.pop("hold_command_values") + result.pop("hold_seconds") + return result + + +def diagnostic_capture_label(mode: str) -> str: + preset = DIAGNOSTIC_CAPTURE_PRESETS.get(mode) + return preset.label_zh if preset else "诊断采集" + + +def diagnostic_hold_timing(history: Sequence[StateSample], *, target: tuple[float, ...], + image_stamp_ns: int, session_epoch: int, motion_version: int, motion_started_ns: int, + feedback_stable_since_ns: int | None) -> dict: + """Describe the actual held command without assigning unmeasured exposure time. + + Only command samples at or before this image are considered. A rounded u8 + target may be issued before the trajectory finishes, so its first issue + is retained separately from the motion's start and feedback stability. + """ + first_target = next((sample.stamp_ns for sample in history + if motion_started_ns <= sample.stamp_ns <= image_stamp_ns + and tuple(sample.position_u8) == target), None) + stable_since = feedback_stable_since_ns + if stable_since is not None and not motion_started_ns <= stable_since <= image_stamp_ns: + stable_since = None + return { + "hold_id": f"{session_epoch}:{motion_version}", + "target_first_command_stamp_ns": first_target, + "feedback_stable_since_ns": stable_since, + } + + +def resolve_diagnostic_capture(profile: CalibrationProfile, mode: str) -> DiagnosticCapturePlan | None: + """Select an audited task subset without changing a protected Profile. + + Presets select tasks and optional directional holds. Safety, references + and vision remain the common implementations; no alternative fitter is + created, and these observations cannot certify calibration artifacts. + """ + if not mode: + return None + preset = DIAGNOSTIC_CAPTURE_PRESETS.get(mode) + if preset is None or profile.key != preset.profile_key: + raise ValueError(f"unsupported_diagnostic_capture:{mode}:{profile.key.profile_id}") + expected = dict(preset.tasks) + tasks = tuple(task for task in profile.motion.tasks if task.key in expected) + if len(tasks) != len(expected) or profile.artifacts.output_schema_version < 3: + raise ValueError("diagnostic_thumb_tasks_or_joint_zeros_missing") + for task in tasks: + if (set(task.joints) != set(expected[task.key]) or task.validation_only + or task.command_index != profile.command.command_index_by_joint.get(expected[task.key][0]) + or any(name not in profile.motion.joint_zero_references + or profile.motion.joint_zero_references[name].task_key != task.key for name in task.joints)): + raise ValueError(f"diagnostic_thumb_task_binding_changed:{task.key}") + plan = DiagnosticCapturePlan(mode, tuple(task.key for task in tasks), + hold_command_values=preset.hold_command_values, hold_seconds=preset.hold_seconds) + for task in tasks: + lower = max(min(task.start_value, task.end_value), + profile.command.minimum_values[task.command_index]) + upper = min(max(task.start_value, task.end_value), + profile.command.maximum_values[task.command_index]) + if any(not lower < value < upper for value in plan.hold_command_values): + raise ValueError(f"diagnostic_hold_outside_task_domain:{task.key}") + return plan + + +def reject_diagnostic_capture(records: Sequence[Mapping]) -> None: + """Diagnostic samples must never be promoted to formal fit or resume data.""" + if any((row.get("kind") == "session_start" and row.get("diagnostic_capture")) + or row.get("kind") in {"diagnostic_observation_frame", "diagnostic_joint_zero_attempt"} + or row.get("quality_scope") == "diagnostic_observation_coverage" for row in records): + raise ValueError("diagnostic_capture_not_for_publication_or_resume") diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_quality.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_quality.py new file mode 100644 index 0000000..0e39766 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_quality.py @@ -0,0 +1,84 @@ +"""Separate diagnostic image coverage from accepted-pose calibration quality.""" + +from dataclasses import dataclass +from collections.abc import Mapping, Sequence +import math + +import numpy as np + +from ..core.domain.profile import CalibrationProfile +from .engine import ScanUnit, SweepQuality +from .scan_quality import evaluate_capture_unit, observation_streams +from .observation_timing import summarize_observation_timing + + +@dataclass(frozen=True) +class DiagnosticUnitQuality: + observations: SweepQuality + accepted_poses: SweepQuality + observation_timing: Mapping[str, Mapping] + + def accepted_pose_report(self) -> dict: + return {"passed": self.accepted_poses.passed, + "failures": list(self.accepted_poses.failures), + "metrics": dict(self.accepted_poses.metrics), "accuracy_validated": False} + + +def _complete_vector(values, count: int) -> bool: + return (isinstance(values, (list, tuple)) and len(values) == count + and all(isinstance(value, (int, float)) and math.isfinite(value) for value in values)) + + +def _visible_roles(row: Mapping, tags: Mapping) -> set[str]: + visible = set() + for role, item in row.get("tags", {}).items(): + tag = tags.get(role) + if (tag is None or item.get("tag_role") != role or item.get("tag_id") != tag.tag_id + or item.get("candidate_diagnostics", {}).get("observation_stamp_ns") != row.get("image_stamp_ns")): + continue + points = np.asarray(item.get("corners_xy"), dtype=float) + if points.shape == (4, 2) and np.all(np.isfinite(points)): + visible.add(role) + return visible + + +def evaluate_diagnostic_unit(profile: CalibrationProfile, unit: ScanUnit, attempt: int, + records: Sequence[Mapping], +) -> DiagnosticUnitQuality: + """Same motion/coverage limits; ambiguous poses are evidence for diagnosis. + + Temporary records carry feedback coverage only, never measured angles. + They remain local to this check and cannot be serialized as joint samples. + Missing current moving-Tag corners still fail the ordinary one-retry gate. + """ + accepted = evaluate_capture_unit(profile, unit, attempt, records, + first_cycle_spans={}, include_steady=False) + task = next(task for task in profile.motion.tasks if task.key == unit.task_key) + tags_by_view = {view.name: {tag.role: tag for tag in view.tags} for view in profile.vision.views} + required = {} + for field, name, spec in observation_streams(profile, task): + moving_roles = {tag.role for tag in tags_by_view[spec.view].values() if not tag.fixed_reference} + required[(field, name, spec.view)] = moving_roles & {spec.parent_role, spec.child_role} + coverage = [row for row in records if row.get("kind") != "diagnostic_observation_frame"] + for row in records: + if (row.get("kind") != "diagnostic_observation_frame" + or row.get("sample_phase") != "sweep" + or (row.get("task_name"), row.get("cycle"), row.get("direction"), row.get("attempt")) + != (unit.task_key, unit.cycle, unit.direction, attempt)): + continue + feedback = row.get("feedback_vector") + if (row.get("command_unit") != profile.command.unit + or not _complete_vector(feedback, profile.command.command_count) + or not _complete_vector(row.get("command_vector"), profile.command.command_count)): + continue + visible = _visible_roles(row, tags_by_view.get(row.get("view"), {})) + for (field, name, view), roles in required.items(): + if row.get("view") == view and roles and roles <= visible: + coverage.append({field: name, "view": view, "task_name": unit.task_key, + "cycle": unit.cycle, "direction": unit.direction, "attempt": attempt, + "image_stamp_ns": row["image_stamp_ns"], "sample_phase": "sweep", + f"feedback_{profile.command.unit}": feedback[task.command_index]}) + observed = evaluate_capture_unit(profile, unit, attempt, coverage, + first_cycle_spans={}, include_steady=False) + return DiagnosticUnitQuality(observed, accepted, + summarize_observation_timing(profile, unit, attempt, records)) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_reference.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_reference.py new file mode 100644 index 0000000..eb4faa7 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_reference.py @@ -0,0 +1,100 @@ +"""Unresolved reference observations cannot become calibrated joint zeros. + +The diagnostic runner may finish its declared motion with raw corner evidence. +This record proves only that the reference pose was visited and observed; it +does not supply an angle, a mounting transform, or permission to publish. +""" + +from dataclasses import asdict, dataclass +import math + +import numpy as np + +from .scan_quality import observation_streams + + +@dataclass(frozen=True) +class DiagnosticZeroAttempt: + joint_names: tuple[str, ...] + task_name: str + reason: str + session_epoch: int + motion_version: int + command_vector: tuple[float, ...] + feedback_vector: tuple[float, ...] + source_images: tuple[tuple[str, int], ...] + joint_views: tuple[tuple[str, str], ...] + + def as_record(self): + return {"kind": "diagnostic_joint_zero_attempt", "schema_version": 1, + "status": "unresolved", "no_zero_locked": True, + "is_accepted_joint_sample": False, **asdict(self)} + + +def _complete_vector(values, count): + return (isinstance(values, (list, tuple)) and len(values) == count + and all(isinstance(v, (float, int)) and not isinstance(v, bool) + and math.isfinite(v) for v in values)) + + +def build_diagnostic_zero_attempt(profile, motion, records, *, reason, + session_epoch, motion_version): + """Check current raw images after the caller's SDK steady-window gate. + + Each moving parent/child must have real current-image corners. A stationary + base may use the already frozen session reference, as during normal scans. + Pose selection and geometric stability are intentionally not certified. + """ + if motion.phase != "joint_zero" or not motion.zero_joints or not reason: + raise ValueError("diagnostic_joint_zero_attempt_invalid") + task = next(task for task in profile.motion.tasks if task.key == motion.task_key) + declared = profile.motion.joint_zero_references + if (not set(motion.zero_joints) <= set(task.joints) + or any(name not in declared or declared[name].task_key != motion.task_key + or declared[name].command != motion.target for name in motion.zero_joints)): + raise ValueError("diagnostic_joint_zero_declared_pose_mismatch") + streams = [(name, spec) for _, name, spec in observation_streams(profile, task) + if name in motion.zero_joints] + tags = {view.name: {tag.role: tag for tag in view.tags} for view in profile.vision.views} + frames = {(name, spec.view): {} for name, spec in streams} + count = profile.command.command_count + for row in records: + if (row.get("kind") != "diagnostic_observation_frame" + or row.get("sample_phase") != "joint_zero" + or (row.get("task_name"), row.get("session_epoch"), row.get("motion_version")) + != (motion.task_key, session_epoch, motion_version) + or tuple(row.get("zero_joints", ())) != motion.zero_joints + or row.get("command_unit") != profile.command.unit): + continue + stamp = row.get("image_stamp_ns") + command, feedback = row.get("command_vector"), row.get("feedback_vector") + if (not isinstance(stamp, int) or isinstance(stamp, bool) or stamp <= 0 + or not _complete_vector(command, count) or not _complete_vector(feedback, count) + or any(abs(a-b) > 1e-9 for a, b in zip(command, motion.target))): + continue + for name, spec in streams: + if row.get("view") != spec.view: + continue + roles = [role for role in (spec.parent_role, spec.child_role) + if not tags[spec.view][role].fixed_reference] + complete = bool(roles) + for role in roles: + item = row.get("tags", {}).get(role, {}) + points = np.asarray(item.get("corners_xy"), dtype=float) + complete &= (item.get("tag_role") == role + and item.get("tag_id") == tags[spec.view][role].tag_id + and item.get("candidate_diagnostics", {}).get("observation_stamp_ns") == stamp + and points.shape == (4, 2) and np.all(np.isfinite(points))) + if complete: + frames[name, spec.view][stamp] = row + minimum = profile.acquisition.fixed_reference_minimum_frames + missing = [f"{name}:{view}:{len(items)}/{minimum}" + for (name, view), items in frames.items() if len(items) < minimum] + if not frames or missing: + raise ValueError("diagnostic_joint_zero_observations_missing:" + ",".join(missing)) + selected = {(view, stamp): row for (_, view), items in frames.items() + for stamp, row in sorted(items.items())[-minimum:]} + latest = max(selected.values(), key=lambda row: row["image_stamp_ns"]) + return DiagnosticZeroAttempt(tuple(motion.zero_joints), motion.task_key, reason, + session_epoch, motion_version, tuple(latest["command_vector"]), + tuple(latest["feedback_vector"]), tuple(sorted(selected)), tuple(sorted(frames))) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_report.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_report.py new file mode 100644 index 0000000..9289057 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/diagnostic_report.py @@ -0,0 +1,181 @@ +"""Summarize captured pose evidence without fitting or certifying motion.""" + +from collections import defaultdict +import hashlib +import json +import math +from pathlib import Path + +from ..core.geometry.pnp import rotation_distance_rad + + +def pose_step_summary(records): + """Compare adjacent accepted images within one uninterrupted scan unit. + + Multiple joint observations of the same Tag image count once. Cached + and image-verified fixed poses and long gaps are excluded from the step statistic, + and all original evidence remains in the raw journal. + """ + streams = defaultdict(dict) + for row in records: + if row.get("sample_phase") != "sweep": + continue + for pose in row.get("pnp_observation_evidence", {}).values(): + if pose["pose_source"] != "current_image": + continue + key = (row["view"], pose["tag_role"], row["task_name"], row["cycle"], + row["direction"], row.get("attempt", 1)) + streams[key][row["image_stamp_ns"]] = pose + tags = {} + for (view, role, task, cycle, direction, attempt), frames in streams.items(): + tag = tags.setdefault(f"{view}:{role}", { + "view": view, "tag_role": role, "tag_id": next(iter(frames.values()))["tag_id"], + "accepted_image_count": 0, "adjacent_pair_count": 0, + "maximum_rotation_step_deg": None, "largest_steps": [], + }) + tag["accepted_image_count"] += len(frames) + ordered = sorted(frames.items()) + steps = [] + for (before, a), (after, b) in zip(ordered, ordered[1:]): + interval = (after-before)/1e9 + if not 0 < interval <= .2: + continue + angle = math.degrees(rotation_distance_rad( + a["selected_pose"]["quaternion_xyzw"], b["selected_pose"]["quaternion_xyzw"])) + steps.append({"rotation_step_deg": angle, "interval_ms": interval*1000, + "from_image": f"{view}:{before}", "to_image": f"{view}:{after}", + "task": task, "cycle": cycle, "direction": direction, "attempt": attempt}) + tag["adjacent_pair_count"] += len(steps) + tag["largest_steps"] = sorted((*tag["largest_steps"], *steps), + key=lambda item: item["rotation_step_deg"], reverse=True)[:5] + if tag["largest_steps"]: + tag["maximum_rotation_step_deg"] = tag["largest_steps"][0]["rotation_step_deg"] + return tags + + +def fixed_reference_summary(records): + """Report image consistency, not fabricated zero angular motion of a base.""" + images = {} + for row in records: + evidence = row.get("tags", {}) if row.get("kind") == "diagnostic_observation_frame" else row.get("pnp_observation_evidence", {}) + items = (evidence,) if row.get("kind") == "pnp_branch_event" else evidence.values() + for item in items: + diagnostics = item.get("candidate_diagnostics", {}) + if diagnostics.get("pose_constraint") != "fixed_reference": + continue + stamp = row["image_stamp_ns"] + if diagnostics.get("observation_stamp_ns") == stamp: + images[(row["view"], item["tag_role"], stamp)] = item + summary = {} + for (view, role, _), item in images.items(): + tag = summary.setdefault(f"{view}:{role}", {"view": view, "tag_role": role, + "tag_id": item["tag_id"], "image_count": 0, "accepted_image_count": 0, + "maximum_reprojection_error_px": None}) + tag["image_count"] += 1 + tag["accepted_image_count"] += int(item.get("selected_pose") is not None) + error = item["candidate_diagnostics"].get("fixed_reference_reprojection_error_px") + if error is not None and math.isfinite(float(error)): + previous = tag["maximum_reprojection_error_px"] + tag["maximum_reprojection_error_px"] = float(error) if previous is None else max(previous, float(error)) + return summary + + +def motion_initialization_summary(records): + """Keep per-joint hypothesis evidence without promoting a local score. + + A relation can fit in one hypothesis while the coupled chain remains + unresolved. Both the local result and that dependency are reported; the + smallest residual is never presented as a confirmed physical branch. + """ + summaries = [] + for row in records: + if row.get("kind") != "motion_branch_initialization": + continue + joints = defaultdict(list) + for hypothesis in row.get("hypotheses", ()): + relations = hypothesis.get("relations", ()) + for relation in relations: + joints[relation["joint"]].append({ + **relation, + "branches": hypothesis["branches"], + "chain_accepted": hypothesis["accepted"], + "other_relations_passed": all(not other["reason"] + for other in relations if other["joint"] != relation["joint"]), + }) + summaries.append({ + "task_name": row["task_name"], "view": row["view"], + "status": row["status"], "reason": row["reason"], + "source_image_count": len(row.get("source_image_stamps", ())), + "joint_hypotheses": dict(joints), + "accuracy_validated": False, + }) + return summaries + + +def _joint_zero_status(profile, joints, references, attempts, records): + """Every attempted zero is either actual frozen evidence or explicit raw-only.""" + if (set(references) & set(attempts) + or set(references) | set(attempts) != joints): + raise ValueError("diagnostic_capture_incomplete_units_or_joint_zeros") + recorded = {json.dumps(row, sort_keys=True, allow_nan=False) + for row in records if row.get("kind") == "diagnostic_joint_zero_attempt"} + statuses = {name: {"status": "confirmed", "raw_only": False} + for name in sorted(references)} + for name, attempt in sorted(attempts.items()): + if (name not in attempt.joint_names + or attempt.task_name != profile.motion.joint_zero_references[name].task_key + or json.dumps(attempt.as_record(), sort_keys=True, allow_nan=False) not in recorded): + raise ValueError("diagnostic_joint_zero_attempt_evidence_missing") + if any(row.get("kind") in {"joint_sample", "secondary_joint_sample", "joint_zero_reference"} + and row.get("joint", row.get("observation_joint")) == name for row in records): + raise ValueError("diagnostic_unresolved_joint_has_accepted_samples") + statuses[name] = {"status": "unresolved", "raw_only": True, + "reason": attempt.reason, "attempt": attempt.as_record()} + return statuses + + +def build_diagnostic_report(profile, plan, *, raw_path: Path, records, references, + completed_units, expected_units, protected_inputs, command, feedback, + diagnostic_zero_attempts=None): + expected = {(unit.task_key, unit.cycle, unit.direction) for unit in expected_units} + joints = {name for task in profile.motion.tasks if task.key in plan.task_keys for name in task.joints} + if set(completed_units) != expected: + raise ValueError("diagnostic_capture_incomplete_units_or_joint_zeros") + attempts = {} if diagnostic_zero_attempts is None else diagnostic_zero_attempts + zero_status = _joint_zero_status(profile, joints, references, attempts, records) + counts = {name: {} for name in sorted(joints)} + phase_counts = {name: {} for name in sorted(joints)} + raw_phase_counts = {} + for row in records: + if row.get("kind") == "joint_sample" and row.get("joint") in joints: + key = f"{row['view']}:{row['direction']}:attempt={row.get('attempt', 1)}" + counts[row["joint"]][key] = counts[row["joint"]].get(key, 0)+1 + phase = phase_counts[row["joint"]].setdefault(row.get("sample_phase", "unknown"), {}) + phase[key] = phase.get(key, 0) + 1 + elif row.get("kind") == "diagnostic_observation_frame" and row.get("task_name") in plan.task_keys: + key = f"{row['view']}:{row['direction']}:attempt={row.get('attempt', 1)}" + phase = raw_phase_counts.setdefault(row.get("sample_phase", "unknown"), {}) + phase[key] = phase.get(key, 0) + 1 + unit_quality = [dict(row) for row in records if row.get("kind") == "scan_unit_complete"] + pose_gaps = [row for row in unit_quality if row.get("accepted_pose_quality", {}).get("passed") is False] + return { + "format": "calibration_diagnostic_capture_v1", "schema_version": 1, + **plan.as_dict(), "result": "DIAGNOSTIC_COMPLETE", "publication_allowed": False, + "accuracy_validated": False, "profile_id": profile.key.profile_id, + "raw_samples_path": str(raw_path.resolve()), + "raw_samples_sha256": hashlib.sha256(raw_path.read_bytes()).hexdigest(), + "protected_inputs": dict(protected_inputs), + "completed_units": sorted(completed_units), "joint_sample_counts": counts, + "joint_sample_counts_by_phase": phase_counts, + "raw_observation_counts_by_phase": raw_phase_counts, + "joint_zero_references": {name: references[name].as_record() for name in sorted(references)}, + "joint_zero_status": zero_status, + "missing_joint_zero_references": sorted(attempts), + "motion_initialization_summary": motion_initialization_summary(records), + "final_command": list(command), "final_feedback": list(feedback), + "pose_step_summary": pose_step_summary(records), + "fixed_reference_summary": fixed_reference_summary(records), + "unit_quality": unit_quality, + "accepted_pose_quality_incomplete_attempts": len(pose_gaps), + "note": "仅完成原始观测诊断采集;未确认零位的关节仅保留原始角点和候选,不生成关节角样本。零位状态另见 joint_zero_status,可信姿态覆盖另见 unit_quality。未进行精度验收,未拟合或发布正式 JSON/URDF。", + } diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/engine.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/engine.py index c64d8b3..53bc4ab 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/engine.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/engine.py @@ -9,11 +9,20 @@ from typing import Any, Mapping, Sequence import numpy as np from ..core import CalibrationProfile, TaskSpec, validate_profile +from ..core.geometry.pnp import POSE_TRACKING_POLICY_VERSION +from .diagnostic_capture import DiagnosticCapturePlan from ..core.domain.profile import ACQUISITION_POLICY_VERSION TRAINING_CYCLES = (0, 1, 2) HOLDOUT_CYCLE = 3 +CAPTURE_SCHEDULE_VERSION = "unified_schedule_v2_single_pass" +SEPARATE_CAPTURE_SCHEDULE_VERSION = "unified_schedule_v3_separate_mapping" + + +def capture_schedule_version(profile): + return (SEPARATE_CAPTURE_SCHEDULE_VERSION if profile.acquisition.command_capture_mode == "separate" + else CAPTURE_SCHEDULE_VERSION) @dataclass(frozen=True) @@ -24,6 +33,7 @@ class ScanUnit: start: float end: float speed: float + sample_phase: str = "sweep" @dataclass(frozen=True) @@ -119,21 +129,33 @@ def evaluate_observability( class CalibrationEngine: """Schedule and post-direction quality policy used by the single session executor.""" - def __init__(self, profile: CalibrationProfile) -> None: + def __init__( + self, profile: CalibrationProfile, *, + diagnostic_capture: DiagnosticCapturePlan | None = None, + ) -> None: validate_profile(profile) self.profile = profile + self.diagnostic_capture = diagnostic_capture @property def checkpoint_token(self) -> str: return ACQUISITION_POLICY_VERSION + @property + def capture_schedule_version(self) -> str: + return capture_schedule_version(self.profile) + def scan_units(self) -> tuple[ScanUnit, ...]: units: list[ScanUnit] = [] + plan = self.diagnostic_capture + cycles = plan.cycles if plan is not None else (*TRAINING_CYCLES, HOLDOUT_CYCLE) for task in self.profile.motion.tasks: + if plan is not None and task.key not in plan.task_keys: + continue speed = self._formal_speed(task) forward = "increasing" if task.end_value > task.start_value else "decreasing" reverse = "decreasing" if forward == "increasing" else "increasing" - for cycle in (*TRAINING_CYCLES, HOLDOUT_CYCLE): + for cycle in cycles: units.extend(( ScanUnit( task.key, cycle, forward, task.start_value, @@ -144,6 +166,13 @@ class CalibrationEngine: task.start_value, speed, ), )) + if plan is None and self.profile.acquisition.command_capture_mode == "separate": + from ..core.domain.sampling import command_training_cycles, command_holdout_cycle + for cycle in (*command_training_cycles(self.profile), command_holdout_cycle(self.profile)): + units.extend(( + ScanUnit(task.key, cycle, forward, task.start_value, task.end_value, speed, "steady"), + ScanUnit(task.key, cycle, reverse, task.end_value, task.start_value, speed, "steady"), + )) return tuple(units) def mapping_probe_delta(self, task: TaskSpec) -> float | None: @@ -201,13 +230,29 @@ class CalibrationEngine: return SweepQuality(False, (*quality.failures, "endpoint_observation"), quality.warnings, quality.metrics) return quality - def resume_compatible(self, session_start: Mapping[str, Any]) -> bool: + def capture_compatible(self, session_start: Mapping[str, Any]) -> bool: + """Known capture schedules share the unchanged measurement contract. + + Historical full-sweep/steady journals have no schedule field. They + can still be evaluated on their own, but cannot be mixed into a new + directional-waypoint session through resume. + """ return ( - session_start.get("acquisition_policy_version") + self.diagnostic_capture is None + and not session_start.get("diagnostic_capture") + and session_start.get("acquisition_policy_version") == ACQUISITION_POLICY_VERSION + and session_start.get("capture_schedule_version") in ( + (capture_schedule_version(self.profile),) if self.profile.acquisition.command_capture_mode == "separate" + else (None, CAPTURE_SCHEDULE_VERSION)) + and session_start.get("pose_tracking_policy_version") + == POSE_TRACKING_POLICY_VERSION and session_start.get("profile_id") == self.profile.key.profile_id ) + def resume_compatible(self, session_start: Mapping[str, Any]) -> bool: + return (self.capture_compatible(session_start) + and session_start.get("capture_schedule_version") == capture_schedule_version(self.profile)) def _formal_speed(self, task: TaskSpec) -> float: @@ -222,6 +267,6 @@ class CalibrationEngine: __all__ = [ - "ACQUISITION_POLICY_VERSION", "CalibrationEngine", + "ACQUISITION_POLICY_VERSION", "CAPTURE_SCHEDULE_VERSION", "CalibrationEngine", "HOLDOUT_CYCLE", "ScanUnit", "SweepQuality", "TRAINING_CYCLES", ] diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/execution.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/execution.py index e4be97d..25ac0cc 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/execution.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/execution.py @@ -4,9 +4,13 @@ No ROS dependency, hand-identity branch, alternate retry loop or serializer. The same driver is usable with real SDK transport and a virtual clock. """ +from __future__ import annotations + from dataclasses import replace import math +from .diagnostic_capture import DiagnosticCapturePlan +from .diagnostic_reference import DiagnosticZeroAttempt from .session import CalibrationPhase as Phase, CalibrationSession from .motion_execution import MotionCommand from .scan_quality import evaluate_capture_unit @@ -15,16 +19,20 @@ from .trajectory import (build_calibration_preparation_waypoints, class SessionExecution: - def __init__(self, profile): + def __init__(self, profile, *, diagnostic_capture: DiagnosticCapturePlan | None = None): self.profile = profile - self.session = CalibrationSession(profile) + self.diagnostic_capture = diagnostic_capture + self.session = CalibrationSession(profile, diagnostic_capture=diagnostic_capture) self.first_cycle_spans = {} self.completed_units = set() self.last_quality = None + self.last_diagnostic_quality = None self._effects = [] self._action_key = None self._entered_task = None self.zero_references = {} + self.sampling_plans = {} + self.diagnostic_zero_attempts: dict[str, DiagnosticZeroAttempt] = {} @property def action(self): @@ -72,13 +80,15 @@ class SessionExecution: current = tuple(pose) if self.profile.artifacts.output_schema_version >= 3: pending = {name: spec for name, spec in self.profile.motion.joint_zero_references.items() - if spec.task_key == task.key and name not in self.zero_references} + if spec.task_key == task.key and name not in self.zero_references + and not (self.diagnostic_capture is not None and name in self.diagnostic_zero_attempts)} groups = {} for name, spec in pending.items(): groups.setdefault((spec.command, spec.approach_commands), []).append(name) for (target, approach), names in groups.items(): for pose in (*approach, target): - entry.extend(MotionCommand("zero_approach", point, unit.speed, **common) + entry.extend(MotionCommand("zero_approach", point, unit.speed, + reference_joints=tuple(sorted(names)), **common) for point in build_joint_reference_waypoints(task, pose, profile=self.profile, current_command=current)) current = pose @@ -99,22 +109,58 @@ class SessionExecution: return (MotionCommand("mapping_probe", tuple(target), speed, **common), MotionCommand("mapping_return", start, speed, **common)) if action.phase in {Phase.SWEEP, Phase.RESCAN}: - target = tuple(build_calibration_motion_command(task, unit.end, profile=self.profile)) + if self.diagnostic_capture is None and self.profile.acquisition.command_capture_mode == "separate": + if unit.sample_phase == "sweep": + return (MotionCommand("sweep", tuple(build_calibration_motion_command(task, unit.end, + profile=self.profile)), unit.speed, direction=unit.direction, **common),) + from .steady import steady_targets + plan = self.sampling_plans.get(task.key) + nodes = tuple(plan["training_nodes"]) if plan is not None else None + metadata = {} if plan is None else {"steady_training_nodes": nodes, + "sampling_plan_sha256": plan["plan_sha256"]} + # One move-and-hold effect per mapping point: arrival and the + # stable image window share the same feedback history. + return tuple(MotionCommand("steady", tuple(build_calibration_motion_command(task, value, + profile=self.profile)), unit.speed, direction=unit.direction, steady_index=index, **common, **metadata) + for index, value in enumerate(steady_targets(self.profile, unit, training_nodes=nodes))) + if self.diagnostic_capture is not None: + target = tuple(build_calibration_motion_command(task, unit.end, profile=self.profile)) + sweep = MotionCommand("sweep", target, unit.speed, direction=unit.direction, **common) + if not self.diagnostic_capture.hold_command_values: + return (sweep,) + # Compare the same declared commands after arrival from both + # endpoints. Keep the ordinary safety/trajectory driver and + # retain the complete hold for time-dependent diagnostics. + start = tuple(build_calibration_motion_command(task, unit.start, profile=self.profile)) + values = sorted(self.diagnostic_capture.hold_command_values, + reverse=unit.direction == "decreasing") + return (sweep, MotionCommand("steady_prepare", start, unit.speed, **common), + *(MotionCommand("steady", + tuple(build_calibration_motion_command(task, value, profile=self.profile)), + unit.speed, direction=unit.direction, steady_index=index, + minimum_hold_seconds=self.diagnostic_capture.hold_seconds, **common) + for index, value in enumerate(values))) from .steady import steady_targets - start = tuple(build_calibration_motion_command(task, unit.start, profile=self.profile)) - effects = [MotionCommand("sweep", target, unit.speed, direction=unit.direction, **common), - MotionCommand("steady_prepare", start, unit.speed, **common)] - effects.extend(MotionCommand("steady", - tuple(build_calibration_motion_command(task, value, profile=self.profile)), - unit.speed, direction=unit.direction, steady_index=index, **common) - for index, value in enumerate(steady_targets(self.profile, unit))) + effects = [] + # Traverse the direction once. Separate motion effects preserve + # the original sweep/steady image identities and capture gates; + # a held frame is never copied into the moving sample set. + for index, value in enumerate(steady_targets(self.profile, unit)): + point = tuple(build_calibration_motion_command(task, value, profile=self.profile)) + if index: + effects.append(MotionCommand("sweep", point, unit.speed, + direction=unit.direction, **common)) + effects.append(MotionCommand("steady", point, unit.speed, + direction=unit.direction, steady_index=index, **common)) return tuple(effects) return () def motion_complete(self): if not self._effects: raise RuntimeError("cannot complete a motion that was not requested") - if any(name not in self.zero_references for name in self._effects[0].zero_joints): + if any(name not in self.zero_references and not ( + self.diagnostic_capture is not None and name in self.diagnostic_zero_attempts) + for name in self._effects[0].zero_joints): raise RuntimeError("joint zero acquisition cannot complete without frozen evidence") self._effects.pop(0) if self._effects: @@ -135,12 +181,48 @@ class SessionExecution: else: raise RuntimeError(f"unexpected completed motion:{phase.value}") + def repeat_zero_approach(self, current): + """Replace only the current unreferenced preparation, preserving the scan.""" + motion = self._effects[0] if self._effects else None + if (self.session.phase != Phase.PREPARE or motion is None or motion.phase != "joint_zero" + or set(motion.zero_joints) & self.zero_references.keys()): + raise ValueError("zero_recovery_outside_unreferenced_preparation") + task = next(task for task in self.profile.motion.tasks if task.key == motion.task_key) + specs = [self.profile.motion.joint_zero_references[name] for name in motion.zero_joints] + if any(spec != specs[0] for spec in specs): + raise ValueError("zero_recovery_incompatible_group") + effects = [] + for target in (*specs[0].approach_commands, specs[0].command): + effects.extend(replace(motion, phase="zero_approach", target=point, + zero_joints=(), reference_joints=motion.zero_joints) + for point in build_joint_reference_waypoints(task, target, profile=self.profile, + current_command=current)) + current = target + self._effects[:1] = [*effects, motion] + + def record_diagnostic_zero_attempt(self, attempt: DiagnosticZeroAttempt): + """A diagnostic observation closes an attempt, never creates a zero.""" + motion = self._effects[0] if self._effects else None + if (self.diagnostic_capture is None or motion is None or motion.phase != "joint_zero" + or (attempt.task_name, attempt.joint_names) != (motion.task_key, motion.zero_joints)): + raise ValueError("diagnostic_joint_zero_attempt_outside_current_motion") + if any(name in self.zero_references or name in self.diagnostic_zero_attempts + for name in attempt.joint_names): + raise ValueError("diagnostic_joint_zero_attempt_replacement_forbidden") + self.diagnostic_zero_attempts.update({name: attempt for name in attempt.joint_names}) + def evaluate(self, records): if self.session.phase != Phase.EVALUATE: raise RuntimeError("sweep evaluation outside EVALUATE") action = self.action - quality = evaluate_capture_unit(self.profile, action.scan_unit, action.retry+1, - records, first_cycle_spans=self.first_cycle_spans) + if self.diagnostic_capture is None: + quality = evaluate_capture_unit(self.profile, action.scan_unit, action.retry+1, + records, first_cycle_spans=self.first_cycle_spans) + else: + from .diagnostic_quality import evaluate_diagnostic_unit + self.last_diagnostic_quality = evaluate_diagnostic_unit(self.profile, + action.scan_unit, action.retry+1, records) + quality = self.last_diagnostic_quality.observations self.last_quality = quality if quality.passed: unit = action.scan_unit @@ -150,6 +232,8 @@ class SessionExecution: def restore(self, decision, records): """Recheck each durable unit using the current, hash-bound policy.""" + if self.diagnostic_capture is not None: + raise ValueError("diagnostic capture cannot restore previous data") self.first_cycle_spans.clear() completed = set(decision.completed_units) if decision.reuse else set() valid = [] diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/feedback.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/feedback.py new file mode 100644 index 0000000..5c910b6 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/feedback.py @@ -0,0 +1,19 @@ +"""Validate the SDK measurement-time contract before accepting any feedback.""" + +import json +import math + + +def decode_timed_feedback(text): + data = json.loads(text) + if not isinstance(data, dict) or data.get("schema") != "can_feedback_v1": + raise ValueError("feedback_timing_schema_invalid") + names, positions, stamps = (data.get(key) for key in ("name", "position", "channel_stamps_ns")) + if (not isinstance(names, list) or not isinstance(positions, list) or not isinstance(stamps, list) + or not positions or len(positions) != len(stamps) or len(names) != len(positions) + or any(not isinstance(name, str) for name in names) + or any(isinstance(value, bool) or not isinstance(value, (int, float)) or not math.isfinite(value) for value in positions) + or any(type(stamp) is not int or stamp <= 0 for stamp in stamps) + or type(data.get("stamp_ns")) is not int or data["stamp_ns"] != max(stamps)): + raise ValueError("feedback_timing_payload_invalid") + return tuple(names), tuple(positions), data["stamp_ns"], tuple(stamps) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/image_capture.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/image_capture.py new file mode 100644 index 0000000..95416d7 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/image_capture.py @@ -0,0 +1,33 @@ +"""Keep every camera's measured corners under the same motion/time identity. + +These records do not depend on a PnP branch or a task-local hinge model. +Additional views observe the existing motion; they never request a movement, +declare a joint measured, or change the primary stream's acquisition gates. +""" + +IMAGE_OBSERVATION_POLICY = "all_view_rectified_corners_v1" + + +def image_observation_record(profile, frame, motion): + if motion is None or not motion.recording or motion.phase not in {"sweep", "steady", "joint_zero"}: + return None + tags = next(view.tags for view in profile.vision.views if view.name == frame.view) + return { + "kind": "image_observation_frame", "policy": IMAGE_OBSERVATION_POLICY, + "task_name": motion.task_key, "cycle": motion.cycle, + "direction": motion.direction, "attempt": motion.attempt, + "sample_phase": motion.phase, "steady_index": motion.steady_index, + "view": frame.view, "image_stamp_ns": frame.stamp_ns, + "command_unit": profile.command.unit, + "command_vector": None if frame.command is None else list(frame.command), + "feedback_vector": None if frame.feedback is None else list(frame.feedback), + "command_direction_by_index": list(frame.command_directions), + "state_image_sync_error_ns": None if frame.feedback is None else frame.skew_ns, + "camera_matrix": frame.camera_matrix.tolist(), + "camera_matrix_source": "CameraInfo.P[:3,:3]", "input_is_rectified": True, + "tags": {tag.role: { + "tag_id": tag.tag_id, "tag_size_m": tag.size_m, + "fixed_reference": tag.fixed_reference, + "corners_xy": frame.corners[tag.role].tolist(), + } for tag in tags if tag.role in frame.corners}, + } diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/image_pairing.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/image_pairing.py new file mode 100644 index 0000000..340a6b4 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/image_pairing.py @@ -0,0 +1,27 @@ +"""Bounded exact-stamp pairing; transport arrival order is not image identity.""" + +from collections import OrderedDict + + +class ExactImagePairing: + def __init__(self, capacity=8): + self.capacity = capacity + self.images, self.detections = OrderedDict(), OrderedDict() + self.last_emitted = -1 + + def add(self, kind, stamp, message): + if stamp <= self.last_emitted: + return None + own, other = ((self.images, self.detections) if kind == "image" + else (self.detections, self.images)) + own[stamp] = message + while len(own) > self.capacity: + own.popitem(last=False) + if stamp not in other: + return None + image, detection = self.images.pop(stamp), self.detections.pop(stamp) + self.last_emitted = stamp + for buffer in (self.images, self.detections): + for old in [value for value in buffer if value <= stamp]: + del buffer[old] + return image, detection diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/inputs.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/inputs.py index c5ea961..0edf454 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/inputs.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/inputs.py @@ -24,6 +24,7 @@ class TagDetection: corners: tuple[tuple[float, float], ...] hamming: int decision_margin: float + border_quality: Mapping[str, object] | None = None @dataclass(frozen=True) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/joint_resume.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/joint_resume.py index df38b2d..a005aae 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/joint_resume.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/joint_resume.py @@ -1,5 +1,9 @@ """Stage old data until its individual zero poses have been checked again.""" +from __future__ import annotations + +from dataclasses import dataclass + import numpy as np from scipy.spatial.transform import Rotation @@ -8,12 +12,97 @@ from .scan_quality import evaluate_capture_unit from .resume import ResumeVerifier, TagPoseFingerprint +@dataclass +class TaskResumeImport: + """Bounded journal transfer while live feedback and safety keep running.""" + + task_key: str + units: tuple + rows: tuple + offset: int = 0 + + def take_batch(self): + batch = self.rows[self.offset:self.offset + 64] + self.offset += len(batch) + return batch + + @property + def complete(self): + return self.offset == len(self.rows) + + +@dataclass(frozen=True) +class PreparedResume: + """Static checkpoint checks completed before live callbacks or motion. + + This grants no reuse: the coordinator must still compare the current + fixed reference and verify each joint's physical zero installation. + """ + + header: dict + fixed_reference: dict + rows: tuple + completed_units: frozenset + joints: JointResume + + @classmethod + def load(cls, profile, engine, path, serial_number): + from .acquisition import load_capture + from .diagnostic_capture import reject_diagnostic_capture + + rows = tuple(load_capture(path)) + reject_diagnostic_capture(rows) + headers = [r for r in rows if r.get("kind") == "session_start"] + references = [r for r in rows if r.get("kind") == "fixed_base_reference_locked"] + if len(headers) != 1 or len(references) != 1: + raise ValueError("missing unique checkpoint header/reference") + header = headers[0] + if header.get("serial_number") != serial_number or not engine.resume_compatible(header): + raise ValueError("checkpoint identity/policy changed") + complete = frozenset((r["task_name"], r["cycle"], r["direction"]) for r in rows + if r.get("kind") == "scan_unit_complete" and r.get("passed") is True) + joints = JointResume(profile) + if profile.artifacts.output_schema_version >= 3: + joints.stage(engine, rows, complete) + return cls(header, references[0], rows, complete, joints) + + def _pose(samples, field): poses = [getattr(sample, field) for sample in samples] return TagPoseFingerprint(tuple(Rotation.from_quat([p.quaternion_xyzw for p in poses]).mean().as_quat()), tuple(np.median([p.translation_xyz_m for p in poses], axis=0))) +def _index_motion_provenance(rows): + from .motion_provenance import source_frame_sha256 + + zero_images = {(r.get("view"), r.get("image_stamp_ns")) for r in rows + if r.get("kind") == "joint_zero_sample"} + kinds = {"motion_branch_observation", "motion_branch_initialization", + "joint_zero_motion_reference_verified", "joint_zero_sample"} + return {source_frame_sha256(row): row for row in rows + if row.get("kind") in kinds or (row.get("kind") == "pnp_candidate_frame" + and (row.get("view"), row.get("image_stamp_ns")) in zero_images)} + + +def reference_pose_failures(previous, current): + """The same read-back check authorizes live and persisted reference reuse.""" + failures = [] + if (previous.joint != current.joint or previous.task_key != current.task_key + or previous.channel != current.channel or previous.baseline_command != current.baseline_command): + failures.append("reference_contract") + verifier = ResumeVerifier() + for view in {s.view for s in previous.samples}: + old = [s for s in previous.samples if s.view == view] + new = [s for s in current.samples if s.view == view] + if not new or {s.command for s in old} != {s.command for s in new}: + failures.append(f"held_pose:{view}") + continue + for field in ("parent_pose_common", "child_pose_common"): + verifier._compare_poses(field, {view: _pose(old, field)}, {view: _pose(new, field)}, failures) + return failures + + class JointResume: def __init__(self, profile): self.profile = profile @@ -22,9 +111,19 @@ class JointResume: self.units = set() self.rows = () self.first_cycle_spans = {} + self._motion_provenance_pending = {} + self._motion_provenance_source = None + self._motion_provenance_emitted = set() def stage(self, engine, rows, units): + headers = [row for row in rows if row.get("kind") == "session_start"] + if len(headers) != 1 or not engine.resume_compatible(headers[0]): + raise ValueError("joint_resume_checkpoint_policy_incompatible") references = read_joint_zero_references(self.profile, rows) + from .motion_provenance import validate_motion_provenance + validate_motion_provenance(self.profile, rows, references=references) + from ..core.fitting.command_sampling import validate_sampling_plans + validate_sampling_plans(self.profile, rows) spans, valid = {}, set() for unit in engine.scan_units(): key = (unit.task_key, unit.cycle, unit.direction) @@ -38,6 +137,9 @@ class JointResume: valid.add(key) self.references, self.units, self.rows = references, valid, tuple(rows) self.first_cycle_spans = spans + self._motion_provenance_source = _index_motion_provenance(self.rows) + self._motion_provenance_pending.clear() + self._motion_provenance_emitted.clear() def spans_for_task(self, task_key): from .scan_quality import observation_streams @@ -50,23 +152,45 @@ class JointResume: previous = self.references.get(reference.joint) if previous is None: return reference - failures = [] - if previous.task_key != reference.task_key or previous.channel != reference.channel: - failures.append("reference_contract") - verifier = ResumeVerifier() - for view in {s.view for s in previous.samples}: - old = [s for s in previous.samples if s.view == view] - new = [s for s in reference.samples if s.view == view] - if not new or {s.command for s in old} != {s.command for s in new}: - failures.append(f"held_pose:{view}") - continue - for field in ("parent_pose_common", "child_pose_common"): - verifier._compare_poses(field, {view: _pose(old, field)}, {view: _pose(new, field)}, failures) + failures = reference_pose_failures(previous, reference) + if previous.branch_references and not reference.branch_references: + failures.append("motion_evidence_missing") if failures: raise ValueError(f"joint_resume_reference_changed:{reference.joint}:{','.join(failures)}; 旧数据不能与新零位拼接,请重新开始") self.verified.add(reference.joint) + if previous.branch_references: + if self._motion_provenance_source is None: + self._motion_provenance_source = _index_motion_provenance(self.rows) + zero_images = {(row.get("view"), row.get("image_stamp_ns")) + for row in self._motion_provenance_source.values() + if row.get("kind") == "joint_zero_sample" and row.get("joint") == reference.joint} + for identity, row in self._motion_provenance_source.items(): + if (row["kind"] in {"joint_zero_sample", "joint_zero_motion_reference_verified"} + and row.get("joint") != reference.joint): + continue # An interrupted resume must not import unverified joints' references. + if (row["kind"] == "pnp_candidate_frame" + and (row.get("view"), row.get("image_stamp_ns")) not in zero_images): + continue + if identity not in self._motion_provenance_emitted: + self._motion_provenance_pending[identity] = row + self._motion_provenance_pending[f"verified:{reference.joint}"] = { + "kind": "joint_zero_motion_reference_verified", "joint": reference.joint, + "current_reference": reference.as_record(), "preserved_reference": previous.as_record(), + } return previous # Preserve the original measured zero, including its identity. + def take_motion_provenance_records(self): + """Drain only after the caller atomically freezes all verified roles.""" + from .motion_provenance import source_frame_sha256 + rows = [] + for key, row in self._motion_provenance_pending.items(): + identity = source_frame_sha256(row) if key.startswith("verified:") else key + if identity not in self._motion_provenance_emitted: + rows.append(row) + self._motion_provenance_emitted.add(identity) + self._motion_provenance_pending.clear() + return tuple(rows) + def take_task(self, task_key): task = next(t for t in self.profile.motion.tasks if t.key == task_key) if not set(task.joints) <= self.verified: @@ -74,5 +198,8 @@ class JointResume: units = {key for key in self.units if key[0] == task_key} rows = tuple(r for r in self.rows if (r.get("task_name"), r.get("cycle"), r.get("direction")) in units and r.get("kind") not in {"joint_zero_reference", "joint_zero_sample"}) + if units: + rows = tuple(r for r in self.rows if r.get("kind") == "command_sampling_plan" + and r.get("task_name") == task_key) + rows self.units.difference_update(units) return tuple(sorted(units)), rows diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/motion_execution.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/motion_execution.py index 1e971ed..067fd98 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/motion_execution.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/motion_execution.py @@ -26,6 +26,10 @@ class MotionCommand: attempt: int = 1 steady_index: int | None = None zero_joints: tuple[str, ...] = () + reference_joints: tuple[str, ...] = () + minimum_hold_seconds: float = 0.0 + steady_training_nodes: tuple[float, ...] = () + sampling_plan_sha256: str = "" @property def recording(self): @@ -36,6 +40,8 @@ class MotionExecution: def __init__(self, profile, command: MotionCommand, *, initial_command, initial_feedback, now: float, identity: str): self.profile, self.command, self.identity = profile, command, str(identity) + if not math.isfinite(command.minimum_hold_seconds) or command.minimum_hold_seconds < 0: + raise ValueError("minimum hold must be finite and nonnegative") self.start = tuple(float(v) for v in initial_command) self.start_feedback = tuple(float(v) for v in initial_feedback) self.started_at = float(now) @@ -113,7 +119,8 @@ class MotionExecution: # Data sufficiency is evaluated after the direction; never wait # indefinitely for a Tag or exact command/feedback equality. return (self.finished_at is not None and - now-self.finished_at >= self.profile.acquisition.steady_timeout_seconds) + now-self.finished_at >= max(self.profile.acquisition.steady_timeout_seconds, + self.command.minimum_hold_seconds)) if self.phase < 1 or self.finished_at is None or len(self.history) < 3: return False hold = float(self.profile.motion.speed_parameters.get("endpoint_hold_seconds", 0.25)) @@ -127,13 +134,29 @@ class MotionExecution: return False return True + def minimum_hold_complete(self, now): + return self.finished_at is not None and now-self.finished_at >= self.command.minimum_hold_seconds + def steady_ready(self, now): if self.command.phase not in {"steady", "joint_zero"} or self.finished_at is None: return False window = self.profile.acquisition.steady_window_seconds rows = [(stamp, value) for stamp, value in self.stamped_history - if stamp >= max(self.finished_at, now-window-0.05)] - if len(rows) < 3 or rows[-1][0]-rows[0][0] < window: + if stamp >= self.finished_at] + if not rows or now-rows[-1][0] > window: + return False + # Use the latest independent feedback as the window end, so timer + # ticks between feedback messages cannot turn stable data unstable. + # Keep the sample bracketing its start: a fixed 50 ms margin loses it + # under scheduler jitter. Add the duration to avoid cancellation when + # subtracting large monotonic timestamps at the exact window edge. + end = rows[-1][0] + boundary = next((i for i in range(len(rows)-1, -1, -1) + if rows[i][0]+window <= end), None) + if boundary is None: + return False + rows = rows[boundary:] + if len(rows) < 3: return False indices = range(self.profile.command.command_count) if self.command.phase == "joint_zero" else (self.command.command_index,) return all(max(v[i] for _, v in rows)-min(v[i] for _, v in rows) <= max(2*self.resolution, diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/motion_provenance.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/motion_provenance.py new file mode 100644 index 0000000..bf9783c --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/motion_provenance.py @@ -0,0 +1,272 @@ +"""Read-back of pre-zero geometric branch evidence, without fitting new poses. + +Legacy journals remain readable with their original meaning. A motion-aware +journal cannot lose its header and silently fall back to legacy confirmation. +""" + +import hashlib +import json +import re +from collections.abc import Mapping + +from ..core.domain.reference import joint_zero_moving_roles, read_joint_zero_references +from ..core.geometry.image_motion_replay import ( + IMAGE_POSE_SOURCE, frozen_image_model, has_image_model, replay_image_models, + validate_image_sample, +) +from ..core.geometry.tag_pose.parameters import POSE_TRACKING_POLICY_VERSION + + +MOTION_BRANCH_POLICY_VERSION = POSE_TRACKING_POLICY_VERSION +MOTION_BRANCH_POLICY_VERSIONS = { + "confirmed_branch_v4_motion_geometry", "confirmed_branch_v5_image_motion", + "confirmed_branch_v6_source_geometry", "confirmed_branch_v7_final_image_sampling", + MOTION_BRANCH_POLICY_VERSION, +} + + +def uses_motion_evidence(records): + return any( + row.get("kind") in {"motion_branch_initialization", "motion_branch_observation", + "joint_zero_motion_reference_verified"} + or row.get("pose_tracking_policy_version") in MOTION_BRANCH_POLICY_VERSIONS + or bool(row.get("branch_references")) + or row.get("pnp_observation_evidence", {}).get("candidate_diagnostics", {}).get("motion_evidence_verified") is True + or any(isinstance(item, Mapping) and item.get("candidate_diagnostics", {}).get("motion_evidence_verified") is True + for item in row.get("pnp_observation_evidence", {}).values()) + for row in records) + + +def _fail(reason): + raise ValueError(f"motion_branch_provenance_{reason}") + + +def _canonical(value): + return json.dumps(value, sort_keys=True, separators=(",", ":"), allow_nan=False) + + +def source_frame_sha256(record): + """Bind the entire captured frame, including corners, candidates and SDK state.""" + return hashlib.sha256(_canonical(record).encode()).hexdigest() + + +def _check_source_hashes(payload, observations, hashes): + stamps = payload.get("source_image_stamps", ()) + declared = payload.get("source_frame_hashes") + if (not isinstance(stamps, (tuple, list)) or not stamps + or any(type(stamp) is not int or stamp < 1 for stamp in stamps) + or list(stamps) != sorted(set(stamps)) + or not isinstance(declared, Mapping) or set(declared) != {str(stamp) for stamp in stamps} + or any(not isinstance(value, str) or re.fullmatch(r"[0-9a-f]{64}", value) is None + for value in declared.values())): + _fail("source_hash_coverage_invalid") + for stamp in stamps: + key = (payload["view"], stamp) + source = observations.get(key) + if source is None or any(source.get(field) != payload[field] for field in ( + "task_name", "zero_joints", "session_epoch", "motion_version")): + _fail("source_image_missing_or_changed") + if key not in hashes: + hashes[key] = source_frame_sha256(source) + if hashes[key] != declared[str(stamp)]: + _fail("source_content_changed") + + +def _initializations(records): + observations, evidence, resolved = {}, {}, [] + for row in records: + if row.get("kind") == "motion_branch_observation": + key = (row.get("view"), row.get("image_stamp_ns")) + if key in observations and _canonical(observations[key]) != _canonical(row): + _fail("source_image_changed") + observations[key] = row + if row.get("kind") == "motion_branch_initialization" and row.get("status") == "resolved": + resolved.append(row) + for role, payload in row.get("evidence_payloads", {}).items(): + identity = row.get("evidence_ids", {}).get(role) + if (not isinstance(identity, str) or re.fullmatch(r"[0-9a-f]{64}", identity) is None + or hashlib.sha256(_canonical(payload).encode()).hexdigest() != identity): + _fail("initialization_hash_invalid") + if payload.get("tag_role") != role or any(payload.get(key) != row.get(key) for key in ( + "task_name", "view", "zero_joints", "session_epoch", "motion_version", + "source_image_stamps", "source_frame_hashes", "constraints", + "frozen_image_model", "image_model_sha256")): + _fail("initialization_binding_changed") + stamps = payload.get("source_image_stamps", ()) + if (not stamps or any(type(stamp) is not int or stamp < 1 for stamp in stamps) + or list(stamps) != sorted(set(stamps)) + or not (payload.get("constraints") or payload.get("frozen_image_model")) + or any(type(payload.get(key)) is not int or payload[key] < 1 + for key in ("session_epoch", "motion_version"))): + _fail("initialization_support_invalid") + if identity in evidence and _canonical(evidence[identity]) != _canonical(payload): + _fail("initialization_identity_reused") + evidence[identity] = payload + source_hashes = {} + for row in resolved: + _check_source_hashes(row, observations, source_hashes) + for role, identity in row.get("evidence_ids", {}).items(): + payload = evidence.get(identity) + if payload is None or (payload["view"], payload["tag_role"]) != (row.get("view"), role): + _fail("initialization_role_unbound") + for payload in evidence.values(): + _check_source_hashes(payload, observations, source_hashes) + if has_image_model(payload): + _check_image_initialization(payload, observations) + model = frozen_image_model(payload) + source_hash = getattr(model, "source_urdf_sha256", "") + if source_hash: + headers = [row for row in records if row.get("kind") == "session_start"] + if len(headers) != 1 or headers[0].get("source_urdf_sha256") != source_hash: + _fail("image_model_source_urdf_changed") + return evidence + + +def validate_source_geometry(profile, source_urdf, records): + """Re-derive source constraints before finalization; never trust labels alone.""" + from ..core.geometry.tag_pose.cad_image_model import CAD_IMAGE_MOTION_MODEL_POLICY + from ..core.urdf.kinematics import UrdfKinematicModel + from ..profiles.observations import compile_parallel_axis_geometry + + source_model = None + for row in records: + if row.get("kind") != "motion_branch_initialization" or row.get("status") != "resolved": + continue + payload = row.get("frozen_image_model", {}) + if payload.get("policy") != CAD_IMAGE_MOTION_MODEL_POLICY: + continue + model = frozen_image_model(row) + if source_model is None: + source_model = UrdfKinematicModel(source_urdf) + source_hash = hashlib.sha256(source_model.source.read_bytes()).hexdigest() + if (model.source_urdf_sha256 != source_hash + or model.constraints != compile_parallel_axis_geometry(profile, source_model, model.relations)): + _fail("image_model_source_geometry_changed") + + +def _check_image_initialization(payload, observations): + model = frozen_image_model(payload) + if (list(model.source_stamps_ns) != payload["source_image_stamps"] + or len(model.source_stamps_ns) < 24 + or min(len(model.training_stamps_ns), len(model.validation_stamps_ns)) < 12 + or payload["tag_role"] not in {item.relation.child_role for item in model.geometry} + or not {item.relation.joint for item in model.geometry} <= set(payload["zero_joints"])): + _fail("image_model_support_or_roles_changed") + for stamp in model.source_stamps_ns: + source = observations[(payload["view"], stamp)] + if (source.get("cycle") == 3 or source.get("sample_phase") != "zero_approach" + or source.get("input_is_rectified") is not True + or _canonical(source.get("camera_matrix")) != _canonical(model.camera_matrix)): + _fail("image_model_support_not_pre_zero") + for role, size in model.tag_sizes: + if source.get("tags", {}).get(role, {}).get("tag_size_m") != size: + _fail("image_model_tag_size_changed") + + +def _image_frames(records): + frames = {} + for row in records: + if row.get("kind") != "pnp_candidate_frame": + continue + key = (row.get("view"), row.get("image_stamp_ns")) + if key in frames and _canonical(frames[key]) != _canonical(row): + _fail("image_frame_identity_reused") + frames[key] = row + return frames + + +def _reference_authorizations(profile, reference, evidence): + roles = {(item.view, item.tag_role) for item in reference.branch_references} + if roles != joint_zero_moving_roles(profile, reference.joint): + _fail(f"zero_evidence_missing:{reference.joint}") + result = {} + for item in reference.branch_references: + payload = evidence.get(item.motion_evidence_id) + if payload is None or (payload["view"], payload["tag_role"]) != (item.view, item.tag_role): + _fail(f"zero_evidence_unbound:{reference.joint}") + first_zero = min(sample.image_stamp_ns for sample in reference.samples if sample.view == item.view) + if max(payload["source_image_stamps"]) >= first_zero: + _fail(f"initialization_not_before_zero:{reference.joint}") + result[(item.view, item.tag_role, item.motion_evidence_id)] = (item.branch_revision, first_zero) + return result + + +def validate_motion_provenance(profile, records, *, required=False, require_complete=False, references=None): + """Check capture authorization, never certify physical accuracy. + + The explicit ``required`` flag is set by the production owner. Independent + record markers preserve this requirement after a header is removed. + """ + if not required and not uses_motion_evidence(records): + return {"status": "legacy_image_confirmation", "motion_evidence_verified": False} + references = read_joint_zero_references(profile, records) if references is None else references + expected = {name for task in profile.motion.tasks for name in task.joints} + if not references or (require_complete and not expected <= references.keys()): + _fail("joint_zero_references_missing") + evidence = _initializations(records) + image_frames, replayed = _image_frames(records), {} + authorizations = {name: _reference_authorizations(profile, reference, evidence) + for name, reference in references.items()} + zero_images = {(name, sample.view, sample.image_stamp_ns) + for name, reference in references.items() for sample in reference.samples} + for row in records: + if row.get("kind") != "joint_zero_motion_reference_verified": + continue + from .joint_resume import reference_pose_failures + original = read_joint_zero_references(profile, [row["preserved_reference"]]) + current = read_joint_zero_references(profile, [row["current_reference"]]) + name = row.get("joint") + if set(original) != {name} or set(current) != {name} or name not in references: + _fail("resume_reference_binding_invalid") + if (_canonical(original[name].as_record()) != _canonical(references[name].as_record()) + or reference_pose_failures(original[name], current[name])): + _fail(f"resume_reference_changed:{name}") + authorizations[name].update(_reference_authorizations(profile, current[name], evidence)) + zero_images.update((name, sample.view, sample.image_stamp_ns) for sample in current[name].samples) + cycles = {} + for row in records: + kind = row.get("kind") + if kind not in {"joint_sample", "secondary_joint_sample", "joint_zero_sample"}: + continue + name, view, stamp = row.get("joint", row.get("observation_joint")), row.get("view"), row.get("image_stamp_ns") + is_zero = kind == "joint_zero_sample" or row.get("sample_phase") == "joint_zero" + if is_zero and (name, view, stamp) not in zero_images: + continue # Earlier unsettled zero images are retained diagnostics. + if name not in authorizations: + _fail(f"sample_zero_missing:{name}") + roles = {role for role_view, role in joint_zero_moving_roles(profile, name) if role_view == view} + observed = row.get("pnp_observation_evidence", {}) + by_role = {item.get("tag_role"): item for item in observed.values()} + if not roles or not roles <= by_role.keys(): + _fail(f"sample_role_missing:{name}") + for role in roles: + item = by_role[role] + diagnostics = item.get("candidate_diagnostics", {}) + key = (view, role, diagnostics.get("motion_evidence_id")) + authorization = authorizations[name].get(key) + if (authorization is None or item.get("pose_source") not in {"current_image", IMAGE_POSE_SOURCE} + or diagnostics.get("motion_evidence_verified") is not True + or diagnostics.get("branch_revision") != authorization[0] + or diagnostics.get("observation_stamp_ns") != stamp + or diagnostics.get("branch_status") != "tracking" + or (not is_zero and (diagnostics.get("branch_frozen") is not True or stamp <= authorization[1]))): + _fail(f"sample_not_authorized:{name}:{view}:{role}") + payload = evidence[diagnostics["motion_evidence_id"]] + if has_image_model(payload) or has_image_model(item): + if (item.get("pose_source") != IMAGE_POSE_SOURCE + or not has_image_model(payload) + or item.get("image_model_sha256") != payload.get("image_model_sha256")): + _fail(f"sample_image_model_changed:{name}:{view}:{role}") + frame_key = (view, stamp) + frame = image_frames.get(frame_key) + if frame is None: + _fail(f"sample_image_frame_missing:{name}:{view}:{role}") + if frame_key not in replayed: + replayed[frame_key] = replay_image_models(frame) + validate_image_sample(row, frame, replayed[frame_key]) + if not is_zero: + cycles.setdefault(name, set()).add(row.get("cycle")) + if require_complete and any(not {0, 1, 2, 3} <= cycles.get(name, set()) for name in expected): + _fail("independent_cycles_missing") + return {"status": "motion_evidence_verified", "motion_evidence_verified": True, + "joint_count": len(references), "evidence_count": len(evidence), "is_accuracy_certificate": False} diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/observation_scope.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/observation_scope.py new file mode 100644 index 0000000..42386a4 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/observation_scope.py @@ -0,0 +1,36 @@ +"""Resolve the immutable observation scope of one complete capture session. + +Formal calibration keeps the Profile's full reference contract. A diagnostic +subset needs every primary and secondary view of its selected tasks, including +tasks scheduled later. This never changes as individual scan units advance. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from ..core.domain.profile import CalibrationProfile + +if TYPE_CHECKING: + from .diagnostic_capture import DiagnosticCapturePlan + + +def required_observation_views( + profile: CalibrationProfile, + diagnostic_capture: DiagnosticCapturePlan | None = None, +) -> tuple[str, ...]: + declared = profile.vision.view_names + if diagnostic_capture is None: + return declared + keys = set(diagnostic_capture.task_keys) + tasks = {task.key: task for task in profile.motion.tasks} + if not keys or not keys <= tasks.keys(): + raise ValueError("diagnostic_observation_tasks_invalid") + measurements = profile.measurement + names = {name for key in keys for name in tasks[key].joints} + names.update(measurements.cross_view_sources[name] + for name in tuple(names) if name in measurements.cross_view_sources) + views = {measurements.measurements[name].view for name in names} + if not views or not views <= set(declared): + raise ValueError("diagnostic_observation_views_invalid") + return tuple(view for view in declared if view in views) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/observation_timing.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/observation_timing.py new file mode 100644 index 0000000..2957973 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/observation_timing.py @@ -0,0 +1,113 @@ +"""Describe recorded image and synchronized-observation gaps without judging them.""" + +from collections.abc import Mapping, Sequence +import math +from numbers import Real + +from ..core.domain.profile import CalibrationProfile +from .engine import ScanUnit +from .scan_quality import observation_streams + + +def _finite_number(value: object) -> bool: + return isinstance(value, Real) and not isinstance(value, bool) and math.isfinite(value) + + +def _complete_vector(values: object, count: int) -> bool: + return (isinstance(values, (list, tuple)) and len(values) == count + and all(_finite_number(value) for value in values)) + + +def _current_corners(item: object, role: str, tag_id: int, stamp_ns: int) -> bool: + if not isinstance(item, Mapping): + return False + diagnostics = item.get("candidate_diagnostics") + if (item.get("tag_role") != role or item.get("tag_id") != tag_id + or not isinstance(diagnostics, Mapping) + or diagnostics.get("observation_stamp_ns") != stamp_ns): + return False + corners = item.get("corners_xy") + return (isinstance(corners, (list, tuple)) and len(corners) == 4 + and all(isinstance(point, (list, tuple)) and len(point) == 2 + and all(_finite_number(value) for value in point) for point in corners)) + + +def _maximum_gap_seconds(stamps: Sequence[int]) -> float | None: + ordered = sorted(set(stamps)) + if len(ordered) < 2: + return None + return max(right - left for left, right in zip(ordered, ordered[1:])) / 1e9 + + +def summarize_observation_timing( + profile: CalibrationProfile, + unit: ScanUnit, + attempt: int, + records: Sequence[Mapping], +) -> dict[str, dict[str, int | float | None]]: + """Count raw diagnostic images separately for each required observation stream. + + Only explicit raw image records supply the denominator; accepted joint + samples cannot establish how many images were received. A duplicate image + uses its first record, so separate duplicates cannot manufacture a complete + Tag pair or synchronized command/feedback vectors. + + Visibility requires current corners for every required moving Tag. A + synchronized observation additionally needs complete finite command and + feedback vectors in the Profile's unit. Missing-vector counts cover all raw + images, including those without visible Tags. Gaps use image timestamps; + fewer than two images yield None, and unknown sweep boundaries are not + inferred. These facts neither apply thresholds nor imply accuracy. + """ + task = next(task for task in profile.motion.tasks if task.key == unit.task_key) + streams = tuple(observation_streams(profile, task)) + required_views = {spec.view for _, _, spec in streams} + frames: dict[str, dict[int, Mapping]] = {view: {} for view in required_views} + for row in records: + if (row.get("kind") != "diagnostic_observation_frame" + or row.get("sample_phase") != "sweep" + or (row.get("task_name"), row.get("cycle"), row.get("direction"), row.get("attempt")) + != (unit.task_key, unit.cycle, unit.direction, attempt)): + continue + view, stamp = row.get("view"), row.get("image_stamp_ns") + if (view not in frames or not isinstance(stamp, int) + or isinstance(stamp, bool) or stamp <= 0): + continue + frames[view].setdefault(stamp, row) + + tags_by_view = {view.name: {tag.role: tag for tag in view.tags} + for view in profile.vision.views} + result = {} + for field, name, spec in streams: + required = {role: tags_by_view[spec.view][role].tag_id + for role in {spec.parent_role, spec.child_role} + if not tags_by_view[spec.view][role].fixed_reference} + visible_stamps, synchronized_stamps = [], [] + missing_command, missing_feedback = 0, 0 + for stamp, row in frames[spec.view].items(): + correct_unit = row.get("command_unit") == profile.command.unit + command = correct_unit and _complete_vector(row.get("command_vector"), profile.command.command_count) + feedback = correct_unit and _complete_vector(row.get("feedback_vector"), profile.command.command_count) + missing_command += not command + missing_feedback += not feedback + tags = row.get("tags") + visible = (bool(required) and isinstance(tags, Mapping) + and all(_current_corners(tags.get(role), role, tag_id, stamp) + for role, tag_id in required.items())) + if visible: + visible_stamps.append(stamp) + if command and feedback: + synchronized_stamps.append(stamp) + result[f"{field}:{name}:{spec.view}:{unit.direction}"] = { + "raw_frames": len(frames[spec.view]), + "visible_frames": len(visible_stamps), + "synchronized_frames": len(synchronized_stamps), + "missing_command_frames": missing_command, + "missing_feedback_frames": missing_feedback, + "maximum_image_gap_seconds": _maximum_gap_seconds(tuple(frames[spec.view])), + "maximum_synchronized_gap_seconds": _maximum_gap_seconds(synchronized_stamps), + } + return result + + +__all__ = ["summarize_observation_timing"] diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/parameters.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/parameters.py index 6a341cb..98a523c 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/parameters.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/parameters.py @@ -2,7 +2,7 @@ from __future__ import annotations -from dataclasses import dataclass +from dataclasses import asdict, dataclass from pathlib import Path from typing import Mapping @@ -39,13 +39,7 @@ class RuntimeParameters: tag_size_m: float detection: DetectionPolicy tracking: PoseTrackingParameters + diagnostic_capture: str = "" def new_trackers(self, views): - return {view: SquareTagPoseTracker( - maximum_reprojection_error_px=self.tracking.maximum_reprojection_error_px, - reprojection_tie_px=self.tracking.reprojection_tie_px, - maximum_pose_jump_rad=self.tracking.maximum_pose_jump_rad, - maximum_translation_jump_m=self.tracking.maximum_translation_jump_m, - maximum_tag_tilt_rad=self.tracking.maximum_tag_tilt_rad, - reset_after_seconds=self.tracking.reset_after_seconds, - ) for view in views} + return {view: SquareTagPoseTracker(**asdict(self.tracking)) for view in views} diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/pose_evidence.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/pose_evidence.py new file mode 100644 index 0000000..726bdf5 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/pose_evidence.py @@ -0,0 +1,128 @@ +"""Diagnostic snapshots of the actual inputs and candidates used by capture. + +Evidence binds accepted images to confirmed, frozen branches; it never changes +the fitting formula or refits a reference. Camera matrices remain in the +session's rectified_camera_model records, indexed by view. +""" + +from collections.abc import Mapping, Sequence +from typing import Protocol + +import numpy as np + +from ..core.domain.profile import TagSpec +from ..core.geometry.pnp import SquareTagPose + + +class PoseDiagnostics(Protocol): + def observation_snapshot(self, role: str, stamp_ns: int) -> tuple[ + tuple[SquareTagPose, ...], dict, int + ] | None: ... + + +def _pose_payload(pose: SquareTagPose) -> dict: + return { + "quaternion_xyzw": list(pose.quaternion_xyzw), + "translation_xyz_m": list(pose.translation_xyz_m), + "reprojection_error_px": float(pose.reprojection_error_px), + } + + +def snapshot_pose_evidence( + tags: Mapping[str, TagSpec], + corners: Mapping[str, np.ndarray], + selected: Mapping[str, SquareTagPose], + tracker: PoseDiagnostics, + cached_roles: Sequence[str], + *, stamp_ns: int, + reference_revisions: Mapping[str, int] | None = None, +) -> dict[str, dict]: + evidence = {} + for role in sorted(selected.keys() | corners.keys()): + pose = selected.get(role) + tag = tags[role] + points = corners.get(role) + # estimate() only runs for present corners. A missing fixed Tag may + # use a frozen pose, but its tracker's previous candidates are not + # observations of this image and must never be copied into this row. + snapshot = None if points is None else tracker.observation_snapshot(role, stamp_ns) + candidates, diagnostics, corrections = snapshot if snapshot is not None else ((), {}, 0) + source = "cached_fixed_reference" if role in cached_roles else "current_image" + if pose is not None and diagnostics.get("fixed_reference_verified") is True: + source = "verified_fixed_reference" + if diagnostics.get("frozen_image_model") is not None: + # A shared hinge model produces a current-image constrained pose; + # it must never be described as one of the raw IPPE candidates. + source = "image_constrained_current_image" + evidence[role] = { + "tag_role": role, + "tag_id": tag.tag_id, + "tag_size_m": float(tag.size_m), + "pose_source": source, + "corners_xy": None if points is None else np.asarray(points, dtype=float).tolist(), + "selected_pose": None if pose is None else _pose_payload(pose), + # The tracker retains positive-depth candidates that pass its + # reprojection gate, before independent tilt/continuity selection. + "reprojection_valid_candidates": [_pose_payload(item) for item in candidates], + "candidate_diagnostics": dict(diagnostics), + "branch_corrections_total": int(corrections), + } + if source == "image_constrained_current_image": + evidence[role].update(frozen_image_model=diagnostics["frozen_image_model"], + image_model_sha256=diagnostics.get("image_model_sha256")) + if role in cached_roles: + evidence[role]["reference_branch_revision"] = (reference_revisions or {})[role] + elif source == "verified_fixed_reference": + evidence[role]["reference_branch_revision"] = diagnostics["branch_revision"] + return evidence + + +def reference_branch_revisions(rows: Sequence[Mapping], *, profile, + require_motion_evidence=False) -> dict[tuple[str, str], int]: + """Extract one confirmed physical-Tag revision from every reference image.""" + result = {} + for row in rows: + evidence = row.get("pnp_observation_evidence", {}) + if set(evidence) != {"parent", "child"}: + raise ValueError("joint_zero_branch_evidence_missing") + joint = row["joint"] + names = (joint, profile.measurement.cross_view_sources.get(joint)) + spec = next((profile.measurement.measurements[name] for name in names + if name is not None and profile.measurement.measurements[name].view == row["view"]), None) + if spec is None: + raise ValueError("joint_zero_branch_view_invalid") + for side, role in (("parent", spec.parent_role), ("child", spec.child_role)): + item = evidence[side] + if item.get("tag_role") != role: + raise ValueError("joint_zero_branch_tag_binding_changed") + source = item["pose_source"] + if source in {"cached_fixed_reference", "verified_fixed_reference"}: + if not any(tag.role == role and tag.fixed_reference for view in profile.vision.views + if view.name == row["view"] for tag in view.tags): + raise ValueError("joint_zero_branch_cached_moving_tag") + revision = item.get("reference_branch_revision") + if source != "cached_fixed_reference": + diagnostics = item["candidate_diagnostics"] + if (diagnostics.get("observation_stamp_ns") != row["image_stamp_ns"] + or diagnostics.get("branch_status") != "tracking"): + raise ValueError("joint_zero_branch_observation_unconfirmed") + if source == "verified_fixed_reference" and ( + diagnostics.get("fixed_reference_verified") is not True + or revision != diagnostics.get("branch_revision")): + raise ValueError("joint_zero_fixed_reference_unverified") + revision = diagnostics.get("branch_revision") + key = (row["view"], item["tag_role"]) + fixed = any(tag.role == role and tag.fixed_reference for view in profile.vision.views + if view.name == row["view"] for tag in view.tags) + if require_motion_evidence and not fixed: + from ..core.domain.reference import JointZeroBranchReference + diagnostics = item.get("candidate_diagnostics", {}) + if diagnostics.get("motion_evidence_verified") is not True: + raise ValueError("joint_zero_motion_evidence_unconfirmed") + JointZeroBranchReference(*key, revision, diagnostics.get("motion_evidence_id")) + if isinstance(revision, bool) or not isinstance(revision, int) or revision < 0: + raise ValueError("joint_zero_branch_revision_invalid") + if key in result and result[key] != revision: + raise ValueError(f"joint_zero_branch_revision_changed:{key[0]}:{key[1]}") + result[key] = revision + return result diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/reporting/reasons_zh.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/reporting/reasons_zh.py index da2023f..a9c5dff 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/reporting/reasons_zh.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/reporting/reasons_zh.py @@ -11,6 +11,18 @@ def reason_zh( ) -> tuple[str, str, str]: """Map a common runtime reason to a stable code, explanation and action.""" reason = str(status.get("reason", "unknown")) + if "tag_installation_not_rigid:" in reason: + return ("FIT-RIGID-CHAIN-510", "采集已完成,但实测 Tag 轨迹与整条 URDF 运动链未通过刚性一致性验证。", + "查看 tag_installation_diagnostics.json 中全部 Tag 的角度和位置残差,核对坐标转换、视觉重建和模型几何;这不等于已确认硬件松动,不应直接重复整场采集。") + if reason.startswith("joint_zero_motion_unresolved:"): + return ("OBS-JOINT-BRANCH-111", f"归零准备运动未能可靠确认当前关节的姿态分支:{reason}", + "查看所列关节、机位及 motion_branch_initialization 记录;检查 Tag 刚性、完整可见性和运动几何证据。保持当前位置,不把分数较低的候选直接当作真值,不覆盖已冻结零位。") + if reason.startswith("fixed_reference_pose_inconsistent:"): + return ("OBS-BASE-POSE-110", f"固定基准的当前图像持续无法验证已冻结姿态:{reason}", + "已保持当前位置并禁止发布。检查所列固定 Tag 的遮挡、变形、成像及相机/支架是否移动;不重新定位基准来拼接旧样本。") + if reason.startswith(("pose_branch_conflict:", "pose_branch_recovery_required:")): + return ("OBS-POSE-BRANCH-109", f"当前所需 Tag 的姿态分支与已冻结参考无法可靠保持一致:{reason}", + "已保持当前位置并禁止发布。检查所列机位和 Tag 的刚性、视角与成像;不重置既有零位,不用镜像姿态继续拼接数据。") if "joint_zero_unavailable" in reason: return ("OBS-JOINT-ZERO-107", f"当前关节未取得稳定零位观测:{reason}", "查看所列关节、机位和 Tag;排除持续遮挡或抖动后重新开始,已有零位不会被覆盖。") diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/resume.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/resume.py index 4c63766..a300be8 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/resume.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/resume.py @@ -11,7 +11,9 @@ from typing import Any, Mapping, Sequence import numpy as np -from .engine import ACQUISITION_POLICY_VERSION +from ..core.geometry.pnp import POSE_TRACKING_POLICY_VERSION +from .engine import ACQUISITION_POLICY_VERSION, CAPTURE_SCHEDULE_VERSION, SEPARATE_CAPTURE_SCHEDULE_VERSION +from .diagnostic_capture import reject_diagnostic_capture def discover_resume_candidate( @@ -59,6 +61,7 @@ def discover_resume_candidate( row = json.loads(line) if not isinstance(row, dict): raise ValueError("invalid checkpoint record") + reject_diagnostic_capture((row,)) kind = str(row.get("kind", "")) if kind == "session_start": if start is not None: @@ -92,6 +95,10 @@ def discover_resume_candidate( continue if start.get("acquisition_policy_version") != ACQUISITION_POLICY_VERSION: continue + if start.get("capture_schedule_version") not in {CAPTURE_SCHEDULE_VERSION, SEPARATE_CAPTURE_SCHEDULE_VERSION}: + continue + if start.get("pose_tracking_policy_version") != POSE_TRACKING_POLICY_VERSION: + continue recorded = start.get("protected_hashes", start) if not isinstance(recorded, Mapping) or any(recorded.get(key) != value for key, value in protected_hashes.items()): continue diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/calibration_node.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/calibration_node.py index eab7d6f..595b2cc 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/calibration_node.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/calibration_node.py @@ -2,9 +2,7 @@ import time -from apriltag_msgs.msg import AprilTagDetectionArray -from rclpy.callback_groups import MutuallyExclusiveCallbackGroup -from rclpy.executors import MultiThreadedExecutor +from rclpy.executors import SingleThreadedExecutor from rclpy.node import Node from rclpy.qos import qos_profile_sensor_data from sensor_msgs.msg import CameraInfo, JointState @@ -13,8 +11,10 @@ from std_srvs.srv import Trigger from ..adapters.ros_binding import SdkBindingPorts, bind_ros_sdk from ..coordinator import CalibrationCoordinator +from ..branch_worker import IsolatedBranchSolver +from ...core.geometry.tag_pose.image_projection_worker import ImageProjectionWorker from ..inputs import RuntimePorts -from .io import RosCalibrationIO, _stamp_ns, camera_input, detection_input +from .io import RosCalibrationIO, _stamp_ns, camera_input, checked_detection_input from .parameters import load_runtime_parameters, parameter_defaults @@ -24,9 +24,6 @@ class UnifiedCalibrationNode(Node): for name, default in parameter_defaults(profile).items(): self.declare_parameter(name, default) parameters = load_runtime_parameters(profile, lambda name: self.get_parameter(name).value) - self.motion_callback_group = MutuallyExclusiveCallbackGroup() - self.vision_callback_groups = { - view: MutuallyExclusiveCallbackGroup() for view in profile.vision.view_names} self.io = RosCalibrationIO(self, profile, parameters) ports = RuntimePorts(lambda: int(self.get_clock().now().nanoseconds), time.monotonic, lambda: self.count_publishers(parameters.command_topic), @@ -35,57 +32,88 @@ class UnifiedCalibrationNode(Node): def subscribe_health(receive): self.create_subscription(String, parameters.command_topic.rsplit("/", 1)[0]+"/calibration_health", - lambda message: receive(message.data), 10, callback_group=self.motion_callback_group) + lambda message: receive(message.data), 10) def adapter_factory(publish, set_speed, feedback_fresh): return bind_ros_sdk(profile, SdkBindingPorts(feedback_fresh, ports.monotonic, subscribe_health), publish=publish, set_speed=set_speed) - self.coordinator = CalibrationCoordinator(profile, parameters, ports, adapter_factory) - self.create_subscription(JointState, parameters.state_topic, self._state_callback, 30, - callback_group=self.motion_callback_group) - for view in profile.vision.view_names: + self.branch_solver = IsolatedBranchSolver(parameters.session_dir / "motion_solver_diagnostic.jsonl") + self.projection_worker = ImageProjectionWorker() + from ..artifacts.controller import FinalizationController + self.coordinator = CalibrationCoordinator(profile, parameters, ports, adapter_factory, + finalization=FinalizationController(profile, parameters.session_dir, isolated=True), + initialization_solver=self.branch_solver, projection_worker=self.projection_worker) + from ..detection_dispatch import DetectionDispatcher + self.detection_dispatch = DetectionDispatcher(self.coordinator.required_observation_views, + self.coordinator.receive_detections, + maximum_wait_seconds=parameters.maximum_state_image_skew_ns/1e9, + failed=lambda error: self.get_logger().error(f"image processing failed: {error}")) + self.fixed_ids = {view.name: frozenset(t.tag_id for t in view.tags if t.fixed_reference) + for view in profile.vision.views} + if profile.sdk_adapter == "legacy_byte_sdk": + self.create_subscription(String, parameters.state_topic+"_timed", self._timed_state_callback, 30) + else: + self.create_subscription(JointState, parameters.state_topic, self._state_callback, 30) + for view in self.coordinator.required_observation_views: self.create_subscription(CameraInfo, parameters.camera_info_topics[view], lambda message, selected=view: self._camera_info_callback(selected, message), - qos_profile_sensor_data, callback_group=self.vision_callback_groups[view]) - self.create_subscription(AprilTagDetectionArray, parameters.detection_topics[view], + qos_profile_sensor_data) + self.create_subscription(String, parameters.detection_topics[view]+"_checked", lambda message, selected=view: self._detections_callback(selected, message), - qos_profile_sensor_data, callback_group=self.vision_callback_groups[view]) - self.create_service(Trigger, f"{profile.namespace}/start", self._start, - callback_group=self.motion_callback_group) - self.create_service(Trigger, f"{profile.namespace}/abort", self._abort, - callback_group=self.motion_callback_group) - self.create_timer(1.0/parameters.command_rate_hz, self._tick, - callback_group=self.motion_callback_group) + qos_profile_sensor_data) + self.create_service(Trigger, f"{profile.namespace}/start", self._start) + self.create_service(Trigger, f"{profile.namespace}/abort", self._abort) + self.create_timer(1.0/parameters.command_rate_hz, self._tick) self.create_timer(0.5, self._publish_status) def _start(self, _request, response): + self.detection_dispatch.clear() result = self.coordinator.start() response.success, response.message = result.success, result.message return response def _abort(self, _request, response): + self.detection_dispatch.clear() result = self.coordinator.abort() response.success, response.message = result.success, result.message return response def _state_callback(self, message): - self.coordinator.receive_feedback(tuple(message.name), tuple(message.position), - _stamp_ns(message.header.stamp)) + self._receive_feedback(tuple(message.name), tuple(message.position), _stamp_ns(message.header.stamp)) + + def _timed_state_callback(self, message): + from ..feedback import decode_timed_feedback + try: + names, positions, stamp, channel_stamps = decode_timed_feedback(message.data) + except (ValueError, TypeError) as error: + self.get_logger().error(str(error)) + return # Invalid data cannot refresh the independent feedback watchdog. + self._receive_feedback(names, positions, stamp, channel_stamps_ns=channel_stamps) + + def _receive_feedback(self, names, positions, stamp, *, channel_stamps_ns=()): + sample = self.coordinator.receive_feedback(names, positions, stamp, channel_stamps_ns=channel_stamps_ns) + if sample is not None: + self.detection_dispatch.feedback_available(min(sample.channel_stamps_ns or (sample.stamp_ns,))) def _camera_info_callback(self, view, message): self.coordinator.receive_camera_info(view, camera_input(message)) def _detections_callback(self, view, message): - self.coordinator.receive_detections(detection_input(view, message)) + self.detection_dispatch.submit(checked_detection_input(view, message, + fixed_ids=self.fixed_ids[view])) def _tick(self): + self.detection_dispatch.raise_if_failed() self.coordinator.tick() def _publish_status(self): self.io.publish_status(self.coordinator.snapshot()) def destroy_node(self): + self.detection_dispatch.close() + self.branch_solver.close() + self.projection_worker.close() self.coordinator.close() return super().destroy_node() @@ -94,7 +122,10 @@ def run_profile_node(profile, args=None): import rclpy rclpy.init(args=args) node = UnifiedCalibrationNode(profile) - executor = MultiThreadedExecutor(num_threads=4) + # ROS callbacks only decode messages and advance the control state. Heavy + # image work has its own bounded workers. Parallel ROS callbacks would + # compete for the same session lock and delay feedback and command ticks. + executor = SingleThreadedExecutor() executor.add_node(node) try: executor.spin() diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/io.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/io.py index edba2c1..dfa52a1 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/io.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/io.py @@ -20,10 +20,29 @@ def camera_input(message) -> CameraModelInput: tuple(message.k), tuple(message.d), tuple(message.r), tuple(message.p)) -def detection_input(view: str, message) -> DetectionInput: +def detection_input(view: str, message, *, gray=None, fixed_ids=()) -> DetectionInput: + from ...core.geometry.tag_border import inspect_tag_border + def convert(tag): + corners = tuple((float(p.x), float(p.y)) for p in tag.corners) + border = None + if int(tag.id) in fixed_ids: + border = ({"passed": False, "reason": "border_image_missing"} if gray is None + else inspect_tag_border(gray, corners)) + return TagDetection(int(tag.id), corners, int(tag.hamming), float(tag.decision_margin), border) return DetectionInput(view, _stamp_ns(message.header.stamp), tuple( - TagDetection(int(tag.id), tuple((float(p.x), float(p.y)) for p in tag.corners), - int(tag.hamming), float(tag.decision_margin)) for tag in message.detections)) + convert(tag) for tag in message.detections)) + + +def checked_detection_input(view, message, *, fixed_ids): + data = json.loads(message.data) + if data.get("policy") != "image_border_checked_v1" or data.get("view") != view: + raise ValueError("checked detection provenance differs from subscribed view") + detections = tuple(TagDetection(int(tag["tag_id"]), tuple(map(tuple, tag["corners"])), + int(tag["hamming"]), float(tag["decision_margin"]), tag.get("border_quality")) + for tag in data["detections"]) + if any(tag.tag_id in fixed_ids and not isinstance(tag.border_quality, dict) for tag in detections): + raise ValueError("fixed Tag is missing its current-image border check") + return DetectionInput(view, int(data["stamp_ns"]), detections) class RosCalibrationIO: diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/parameters.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/parameters.py index 7ec295e..c9ee945 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/parameters.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/parameters.py @@ -32,6 +32,7 @@ def parameter_defaults(profile) -> dict[str, Any]: "state_topic": f"/{model}/cb_right_hand_state", "setting_topic": f"/{model}/cb_hand_setting_cmd", "commands_enabled": True, + "diagnostic_capture": "", "speed_settle_seconds": float( speed_parameters.get("speed_settle_seconds", 0.2) ), @@ -47,6 +48,9 @@ def parameter_defaults(profile) -> dict[str, Any]: "pnp_maximum_translation_jump_m": DEFAULT_POSE_TRACKING_PARAMETERS.maximum_translation_jump_m, "pnp_maximum_tag_tilt_deg": math.degrees(DEFAULT_POSE_TRACKING_PARAMETERS.maximum_tag_tilt_rad), "pnp_tracker_reset_seconds": DEFAULT_POSE_TRACKING_PARAMETERS.reset_after_seconds, + "pnp_branch_confirmation_frames": DEFAULT_POSE_TRACKING_PARAMETERS.branch_confirmation_frames, + "pnp_branch_confirmation_minimum_seconds": DEFAULT_POSE_TRACKING_PARAMETERS.branch_confirmation_minimum_seconds, + "pnp_branch_equivalence_deg": math.degrees(DEFAULT_POSE_TRACKING_PARAMETERS.branch_equivalence_rad), "command_rate_hz": float(speed_parameters.get("command_rate_hz", 100.0)), } for view in profile.vision.view_names: @@ -60,6 +64,12 @@ def parameter_defaults(profile) -> dict[str, Any]: def load_runtime_parameters(profile, value: Callable[[str], Any]) -> RuntimeParameters: + from ..diagnostic_capture import resolve_diagnostic_capture + + diagnostic_capture = str(value("diagnostic_capture")).strip() + diagnostic = resolve_diagnostic_capture(profile, diagnostic_capture) + if diagnostic is not None and not bool(value("commands_enabled")): + raise ValueError("diagnostic_capture requires commands_enabled") serial_number = str(value("serial_number")) session_dir = Path(str(value("session_dir"))).expanduser().resolve() source_urdf = Path(str(value("source_urdf_path"))).expanduser().resolve() @@ -102,6 +112,8 @@ def load_runtime_parameters(profile, value: Callable[[str], Any]) -> RuntimePara value("profile_config_expected_sha256") ) resume_value = str(value("resume_raw_samples_path")).strip() + if diagnostic is not None and resume_value: + raise ValueError("diagnostic_capture does not support resuming prior samples") resume_raw_samples_path = ( None if not resume_value @@ -145,6 +157,7 @@ def load_runtime_parameters(profile, value: Callable[[str], Any]) -> RuntimePara detection=DetectionPolicy(int(value("maximum_hamming")), float(value("minimum_decision_margin")), float(value("minimum_edge_pixels"))), tracking=tracking_parameters(value), + diagnostic_capture=diagnostic_capture, ) @@ -157,4 +170,7 @@ def tracking_parameters(value: Callable[[str], Any]) -> PoseTrackingParameters: maximum_translation_jump_m=float(value("pnp_maximum_translation_jump_m")), maximum_tag_tilt_rad=math.radians(float(value("pnp_maximum_tag_tilt_deg"))), reset_after_seconds=float(value("pnp_tracker_reset_seconds")), + branch_confirmation_frames=int(value("pnp_branch_confirmation_frames")), + branch_confirmation_minimum_seconds=float(value("pnp_branch_confirmation_minimum_seconds")), + branch_equivalence_rad=math.radians(float(value("pnp_branch_equivalence_deg"))), ) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/tag_border_filter.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/tag_border_filter.py new file mode 100644 index 0000000..fa715f5 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/ros/tag_border_filter.py @@ -0,0 +1,71 @@ +"""Image preprocessing in its own process, isolated from SDK/control callbacks.""" + +import argparse +from dataclasses import asdict +import hashlib +import json +from pathlib import Path + +from apriltag_msgs.msg import AprilTagDetectionArray +from cv_bridge import CvBridge +import rclpy +from rclpy.node import Node +from rclpy.qos import qos_profile_sensor_data +from sensor_msgs.msg import Image +from std_msgs.msg import String + +from ...profiles import load_hand_profile +from ..image_pairing import ExactImagePairing +from .io import detection_input + + +class TagBorderFilter(Node): + def __init__(self, profile, views): + super().__init__("tag_border_filter") + self.bridge = CvBridge() + self.pairing = {view: ExactImagePairing() for view in views} + self.fixed_ids = {v.name: frozenset(t.tag_id for t in v.tags if t.fixed_reference) + for v in profile.vision.views} + self.publishers_by_view = {} + for view in views: + raw = f"{profile.namespace}/{view}/apriltag/detections" + self.publishers_by_view[view] = self.create_publisher(String, raw+"_checked", 10) + self.create_subscription(Image, f"{profile.namespace}/{view}/camera/image_rect", + lambda msg, v=view: self.receive(v, "image", msg), qos_profile_sensor_data) + self.create_subscription(AprilTagDetectionArray, raw, + lambda msg, v=view: self.receive(v, "detections", msg), qos_profile_sensor_data) + + def receive(self, view, kind, message): + stamp = message.header.stamp.sec*1_000_000_000+message.header.stamp.nanosec + pair = self.pairing[view].add(kind, stamp, message) + if pair is None: + return + image, detections = pair + gray = self.bridge.imgmsg_to_cv2(image, desired_encoding="mono8") + observation = detection_input(view, detections, gray=gray, fixed_ids=self.fixed_ids[view]) + output = String() + output.data = json.dumps({"policy": "image_border_checked_v1", **asdict(observation)}, + separators=(",", ":"), allow_nan=False) + self.publishers_by_view[view].publish(output) + + +def main(args=None): + parser = argparse.ArgumentParser() + parser.add_argument("--profile-config", required=True) + parser.add_argument("--profile-sha256", required=True) + parser.add_argument("--views", nargs="+", required=True) + selected, ros_args = parser.parse_known_args(args) + path = Path(selected.profile_config) + if hashlib.sha256(path.read_bytes()).hexdigest() != selected.profile_sha256: + raise ValueError("border filter Profile hash differs from launch") + profile = load_hand_profile(path) + if not set(selected.views) <= set(profile.vision.view_names): + raise ValueError("border filter view is not declared by Profile") + rclpy.init(args=ros_args) + node = TagBorderFilter(profile, selected.views) + try: + rclpy.spin(node) + finally: + node.destroy_node() + if rclpy.ok(): + rclpy.shutdown() diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/runner.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/runner.py index 14799b8..6d73682 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/runner.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/runner.py @@ -7,6 +7,7 @@ hardware. Pending legacy status/artifact contracts are isolated in compat. from __future__ import annotations import argparse +import json import os from pathlib import Path import subprocess @@ -19,7 +20,7 @@ from ..storage import atomic_write_json from .status import normalize_status, render_status_zh -TERMINAL_STATES = frozenset({"COMPLETE", "PAUSED", "ABORTED", "FAILED"}) +TERMINAL_STATES = frozenset({"COMPLETE", "DIAGNOSTIC_COMPLETE", "PAUSED", "ABORTED", "FAILED"}) def drive_session( @@ -102,6 +103,7 @@ def _write_failure(config: ProductConfig, session: Path, status: Mapping[str, An def run_online( config: ProductConfig, *, record_bag: bool = False, commands_enabled: bool = True, allow_resume: bool = True, + diagnostic_capture: str = "", ) -> int: import rclpy from std_srvs.srv import Trigger @@ -109,6 +111,7 @@ def run_online( from .artifacts.completion import finish_online_artifacts from ..hikrobot_camera import configure_fastdds_large_image_transport from .resume import discover_resume_candidate + from .diagnostic_capture import diagnostic_capture_label, resolve_diagnostic_capture from .adapters.ros_topics import sdk_topics from .runner_support import ( CalibrationMonitor, ProgressConsole, create_session_directory, @@ -116,24 +119,26 @@ def run_online( ) profile = config.calibration_contract.typed_profile + diagnostic = resolve_diagnostic_capture(profile, diagnostic_capture) + if diagnostic is not None and not commands_enabled: + raise ValueError("diagnostic_capture requires commands_enabled") + session = create_session_directory(config.session_root) + from ..profiles.validator import validate_executable_profile, require_release_geometry + contract_report = validate_executable_profile(profile, config.source_urdf) + atomic_write_json(session/"measurement_contract.json", contract_report) + if profile.artifacts.output_schema_version >= 3 and commands_enabled and diagnostic is None: + require_release_geometry(contract_report) resume = discover_resume_candidate( config.session_root, profile_id=profile.key.profile_id, serial_number=config.serial_number, protected_hashes=protected_inputs(config), - ) if allow_resume and commands_enabled else None - session = create_session_directory(config.session_root) - from ..profiles.validator import validate_executable_profile - contract_report = validate_executable_profile(profile, config.source_urdf) - atomic_write_json(session/"measurement_contract.json", contract_report) - coverage = contract_report["independent_measurement_coverage"] - if profile.artifacts.output_schema_version >= 3 and (coverage["unresolved_motion_joints"] or coverage["missing_baseline_geometry"]): - print("当前 Profile 仍有未实测且未声明迁移的关节,或缺少绝对零位来源;本次可采集诊断数据,但不能发布完整 v3 JSON/URDF;" - "缺失关节见 measurement_contract.json。", flush=True) + ) if allow_resume and commands_enabled and diagnostic is None else None if contract_report["cad_constraint_conflicts"]: print("原始 CAD 存在限位/mimic 冲突,未修改原文件;详见 measurement_contract.json。最终范围验证不通过时禁止发布。", flush=True) log_path = session / "calibration.log" configure_fastdds_large_image_transport() process = None monitor = None + capture_complete = False owns_context = not rclpy.ok() with log_path.open("a", encoding="utf-8", buffering=1) as log_stream: try: @@ -164,9 +169,19 @@ def run_online( print(f"发现候选断点:{resume.name};尚未复用,启动后验证基准和 Tag 安装关系。", flush=True) if not commands_enabled: print("仅预览:不自动开始、不发送标定运动;Ctrl+C 退出。", flush=True) + if diagnostic is not None: + if diagnostic.hold_command_values: + values = "/".join(f"{value:g}" for value in diagnostic.hold_command_values) + print(f"{diagnostic_capture_label(diagnostic.mode)}:仅执行 yaw 一次往返;" + f"每个方向扫描后回到该方向起点,按方向依次到达 {values},各保持 {diagnostic.hold_seconds:g} 秒;" + "不复用断点、不拟合或发布标定产物。", flush=True) + else: + print("拇指短程诊断:仅执行拇指任务各一次往返,保留准备和零位采集;不复用断点、不拟合或发布标定产物。", flush=True) + launch_options = {"diagnostic_capture": diagnostic_capture} if diagnostic is not None else {} process = subprocess.Popen( launch_command(config, session, record_bag=record_bag, - commands_enabled=commands_enabled, resume_from=resume), + commands_enabled=commands_enabled, resume_from=resume, + **launch_options), cwd=config.workspace, env=environment, stdout=log_stream, stderr=subprocess.STDOUT, text=True, start_new_session=True, ) @@ -176,6 +191,35 @@ def run_online( auto_start=commands_enabled, ) atomic_write_json(session / "node_status.json", status) + if diagnostic is not None: + if normalize_status(status)["state"] != "DIAGNOSTIC_COMPLETE": + _write_failure(config, session, status) + return 3 + report_path = session / "diagnostic_capture.json" + report = json.loads(report_path.read_text(encoding="utf-8")) + if (not isinstance(report, dict) + or report.get("mode") != diagnostic_capture + or report.get("result") != "DIAGNOSTIC_COMPLETE" + or report.get("publication_allowed") is not False): + raise ValueError("diagnostic_capture_completion_report_invalid") + capture_complete = True + capture_label = diagnostic_capture_label(diagnostic.mode) if diagnostic.hold_command_values else "拇指诊断" + print(f"{capture_label}数据已采完;本次未进行精度验收,未发布 JSON 或修正 URDF。\n诊断报告:{report_path}\n原始样本:{session / 'raw_samples.jsonl'}", flush=True) + pose_gaps = report.get("accepted_pose_quality_incomplete_attempts", 0) + unresolved = report.get("missing_joint_zero_references", ()) + if unresolved: + print("零位未确认,仅保留原始观测:" + ", ".join(unresolved) + + ";未生成这些关节的零位或角度样本。", flush=True) + if pose_gaps: + print(f"其中 {pose_gaps} 次方向采集的可信姿态覆盖不足;已保留原始候选供诊断,不能据此确认标定精度。", flush=True) + # The expensive diagnostic is independent of live sampling. + # Stop camera/SDK processes first; it can never delay feedback + # or change a reference after the capture has completed. + stop_stack(process) + process = None + from .diagnostic_analysis import finish_diagnostic_analysis + finish_diagnostic_analysis(profile, session) + return 0 if normalize_status(status)["state"] != "COMPLETE": _write_failure(config, session, status) return 3 @@ -187,10 +231,11 @@ def run_online( print(f"{name}:{path}", flush=True) return 0 except KeyboardInterrupt: - if monitor is not None and monitor.abort_client.service_is_ready(): + if not capture_complete and process is not None and monitor is not None and monitor.abort_client.service_is_ready(): monitor.abort_client.call_async(Trigger.Request()) rclpy.spin_once(monitor, timeout_sec=0.2) - print(f"已请求中止;本次数据保留在 {session}。", flush=True) + message = "诊断采集已完成;已取消后续分析" if capture_complete else "已请求中止" + print(f"{message};本次数据保留在 {session}。", flush=True) return 130 except Exception as error: import traceback @@ -219,19 +264,39 @@ def main(args: list[str] | None = None) -> None: parser.add_argument("--record-bag", action="store_true") parser.add_argument("--commands-disabled", action="store_true") parser.add_argument("--no-resume", action="store_true") + diagnostic_options = parser.add_mutually_exclusive_group() + diagnostic_options.add_argument("--diagnostic-thumb", action="store_true", + help="仅 O6 右手:拇指任务各采一次往返,只保存诊断数据") + diagnostic_options.add_argument("--diagnostic-yaw-hold", action="store_true", + help="仅 O6 右手:yaw 两方向扫描后各在 64/128/192 保持 10 秒,只保存诊断数据") parser.add_argument("--offline-raw", default="") parser.add_argument("--offline-output", default="") parser.add_argument("--publish-offline", action="store_true") selected = parser.parse_args(args) + diagnostic_flag = ("--diagnostic-thumb" if selected.diagnostic_thumb else + "--diagnostic-yaw-hold" if selected.diagnostic_yaw_hold else "") + if diagnostic_flag and (selected.offline_raw or selected.offline_output + or selected.publish_offline or selected.commands_disabled): + parser.error(f"{diagnostic_flag} cannot be combined with --offline-raw, --offline-output, --publish-offline or --commands-disabled") config = load_product_config( selected.config or str(default_product_config_path()), workspace=selected.workspace, check_can=not (selected.validate_only or selected.offline_raw or selected.commands_disabled), ) + diagnostic_capture = ("o6_thumb" if selected.diagnostic_thumb else + "o6_yaw_hold" if selected.diagnostic_yaw_hold else "") + diagnostic = None + if diagnostic_capture: + from .diagnostic_capture import resolve_diagnostic_capture + try: + diagnostic = resolve_diagnostic_capture(config.calibration_contract.typed_profile, diagnostic_capture) + except ValueError as error: + parser.error(str(error)) if selected.validate_only: - import json from ..profiles.validator import validate_executable_profile - print(json.dumps(validate_executable_profile(config.calibration_contract.typed_profile, - config.source_urdf), ensure_ascii=False, indent=2)) + report = validate_executable_profile(config.calibration_contract.typed_profile, config.source_urdf) + if diagnostic is not None: + report["diagnostic_capture"] = {**diagnostic.as_dict(), "publication_allowed": False} + print(json.dumps(report, ensure_ascii=False, indent=2)) print(f"配置与受保护输入一致:{config.profile_key.profile_id}。此检查不代表 URDF 精度或硬件验收通过。") return if selected.offline_raw: @@ -242,6 +307,8 @@ def main(args: list[str] | None = None) -> None: return if selected.offline_output or selected.publish_offline: parser.error("--offline-output/--publish-offline require --offline-raw") + online_options = {"diagnostic_capture": diagnostic_capture} if diagnostic is not None else {} raise SystemExit(run_online(config, record_bag=selected.record_bag, commands_enabled=not selected.commands_disabled, - allow_resume=not selected.no_resume)) + allow_resume=not selected.no_resume and diagnostic is None, + **online_options)) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/runner_support.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/runner_support.py index 05b0b21..801674f 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/runner_support.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/runner_support.py @@ -95,6 +95,7 @@ def launch_command( commands_enabled: bool, sdk_startup_speed_u8: int | None = None, resume_from: Path | None = None, + diagnostic_capture: str = "", ) -> list[str]: """Build the single launch invocation entirely from the product contract.""" arguments = { @@ -122,13 +123,14 @@ def launch_command( "vendor_sdk_python_package": str(getattr(config, "sdk_python_package", None) or ""), "sdk_package_expected_sha256": getattr(config, "sdk_package_sha256", ""), "record_bag": str(record_bag).lower(), + "diagnostic_capture": diagnostic_capture, } startup_speed = sdk_startup_speed_u8 if startup_speed is None: startup_speed = config.calibration_contract.typed_profile.motion.speed_parameters.get("baseline_u8") if startup_speed is not None: arguments["calibration_speed"] = str(int(startup_speed)) - if resume_from is not None: + if resume_from is not None and not diagnostic_capture: arguments["resume_raw_samples_path"] = str( resume_from / "raw_samples.jsonl" ) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/scan_quality.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/scan_quality.py index 652893f..41d354b 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/scan_quality.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/scan_quality.py @@ -8,17 +8,13 @@ from typing import Mapping, Sequence from .engine import CalibrationEngine, ScanUnit, SweepQuality -def observation_streams(profile, task): - """(record field, joint identity, measurement) required by one task.""" - for name in task.joints: - yield "joint", name, profile.measurement.measurements[name] - secondary = profile.measurement.cross_view_sources.get(name) - if secondary is not None: - yield "observation_joint", name, profile.measurement.measurements[secondary] +from ..core.domain.profile import observation_streams as observation_streams def evaluate_capture_unit(profile, unit: ScanUnit, attempt: int, - records: Sequence[Mapping], *, first_cycle_spans: dict[str, float]) -> SweepQuality: + records: Sequence[Mapping], *, first_cycle_spans: dict[str, float], + include_steady: bool = True, +) -> SweepQuality: """No rates, coupling, endpoint tracking error or Tag percentages stop a scan. Count unique images separately for EVERY required joint/view. A good @@ -33,6 +29,21 @@ def evaluate_capture_unit(profile, unit: ScanUnit, attempt: int, if span <= 0: raise ValueError("a scan needs a nonzero declared range") failures, warnings, metrics, references = [], [], {}, {} + if unit.sample_phase == "steady": + from .steady import steady_failures, steady_targets + failures = steady_failures(profile, unit, attempt, records) + targets = steady_targets(profile, unit, records=records) + for field, name, spec in observation_streams(profile, task): + rows = [r for r in records if r.get(field) == name and r.get("view") == spec.view + and r.get("sample_phase") == "steady" and r.get("task_name") == unit.task_key + and r.get("cycle") == unit.cycle and r.get("direction") == unit.direction + and int(r.get("attempt", 1)) == attempt] + covered = {r["steady_index"] for r in rows} + metrics[f"{field}:{name}:{spec.view}:{unit.direction}"] = { + "quality_scope": "steady_command_nodes", "valid_frames": len({r["image_stamp_ns"] for r in rows}), + "feedback_span": len(covered) / len(targets), "feedback_bins": len(covered), + "maximum_bin_gap": 0, "required_nodes": len(targets)} + return SweepQuality(not failures, tuple(failures), (), metrics) for field, name, spec in observation_streams(profile, task): identity = f"{field}:{name}:{spec.view}:{unit.direction}" samples = {} @@ -68,8 +79,9 @@ def evaluate_capture_unit(profile, unit: ScanUnit, attempt: int, warnings.extend(f"{identity}:{reason}" for reason in decision.warnings) metrics[identity] = dict(decision.metrics) references[identity] = float(decision.metrics["feedback_span"]) - from .steady import steady_failures - failures.extend(steady_failures(profile, unit, attempt, records)) + if include_steady and profile.acquisition.command_capture_mode == "interleaved": + from .steady import steady_failures + failures.extend(steady_failures(profile, unit, attempt, records)) if not failures and unit.cycle == 0: first_cycle_spans.update(references) return SweepQuality(not failures, tuple(failures), tuple(warnings), metrics) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/session.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/session.py index c643c29..bdfeecf 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/session.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/session.py @@ -11,7 +11,9 @@ from enum import Enum from typing import Mapping from ..core import CalibrationProfile, CalibrationStatus, PauseStatus, TaskStatus +from .diagnostic_capture import DiagnosticCapturePlan from .engine import CalibrationEngine, ScanUnit, SweepQuality +from .observation_scope import required_observation_views from .resume import ResumeDecision @@ -34,6 +36,7 @@ class CalibrationPhase(str, Enum): VALIDATE_URDF = "VALIDATE_URDF" PUBLISH = "PUBLISH" COMPLETE = "COMPLETE" + DIAGNOSTIC_COMPLETE = "DIAGNOSTIC_COMPLETE" PAUSED = "PAUSED" ABORTED = "ABORTED" FAILED = "FAILED" @@ -49,9 +52,14 @@ class SessionAction: class CalibrationSession: """Deterministic orchestration kernel with no model-specific branches.""" - def __init__(self, profile: CalibrationProfile) -> None: + def __init__( + self, profile: CalibrationProfile, *, + diagnostic_capture: DiagnosticCapturePlan | None = None, + ) -> None: self.profile = profile - self.engine = CalibrationEngine(profile) + self.diagnostic_capture = diagnostic_capture + self.required_observation_views = required_observation_views(profile, diagnostic_capture) + self.engine = CalibrationEngine(profile, diagnostic_capture=diagnostic_capture) self.phase = CalibrationPhase.WAIT_DEVICE self._units = self.engine.scan_units() self._unit_index = 0 @@ -81,6 +89,8 @@ class CalibrationSession: def start(self, *, resume_requested: bool = False) -> None: self._require(CalibrationPhase.READY) + if self.diagnostic_capture is not None and resume_requested: + raise ValueError("diagnostic capture cannot resume previous data") self._resume_requested = bool(resume_requested) self._reference_locked = False self.phase = CalibrationPhase.BASELINE @@ -109,6 +119,7 @@ class CalibrationSession: ) def resume_checked(self, decision: ResumeDecision) -> None: + self._require_formal_resume() self._require(CalibrationPhase.RESUME_VERIFY) if self.profile.artifacts.output_schema_version >= 3 and decision.completed_units: raise ValueError("v3 resume requires per-joint zero verification before skipping units") @@ -150,6 +161,7 @@ class CalibrationSession: def authorize_task_reuse(self, units): """Only the coordinator can call this after checking frozen joint zeros.""" + self._require_formal_resume() self._require_any(CalibrationPhase.PREPARE, CalibrationPhase.MAPPING_PROBE) current = self.current_unit valid = {(u.task_key, u.cycle, u.direction) for u in self._units} @@ -205,7 +217,11 @@ class CalibrationSession: def return_complete(self) -> None: self._require(CalibrationPhase.RETURN_BASELINE) - self.phase = CalibrationPhase.FIT + self.phase = ( + CalibrationPhase.DIAGNOSTIC_COMPLETE + if self.diagnostic_capture is not None + else CalibrationPhase.FIT + ) def fit_complete(self) -> None: self._require(CalibrationPhase.FIT) @@ -244,8 +260,9 @@ class CalibrationSession: self._pause = PauseStatus("operator_abort", "操作者已中止标定") self.phase = CalibrationPhase.ABORTED - def fail(self, code: str, reason: str) -> None: - self._pause = PauseStatus(code, reason) + def fail(self, code: str, reason: str, details: Mapping[str, object] | None = None, + *, suggestion: str = "") -> None: + self._pause = PauseStatus(code, reason, suggestion, details or {}) self.phase = CalibrationPhase.FAILED def action(self) -> SessionAction: @@ -274,13 +291,13 @@ class CalibrationSession: ) progress = ( 1.0 - if self.phase == CalibrationPhase.COMPLETE + if self.phase in {CalibrationPhase.COMPLETE, CalibrationPhase.DIAGNOSTIC_COMPLETE} else 0.90*self._unit_index / max(1, len(self._units)) ) required: dict[str, tuple[int, ...]] = {} if self.phase in {CalibrationPhase.REFERENCE_LOCKING, CalibrationPhase.RESUME_VERIFY}: required = {view.name: tuple(tag.tag_id for tag in view.tags if tag.fixed_reference) - for view in self.profile.vision.views} + for view in self.profile.vision.views if view.name in self.required_observation_views} if task is not None: measurement_names = set(task.joints) measurement_names.update( @@ -313,8 +330,16 @@ class CalibrationSession: task=TaskStatus( key="" if task is None else task.key, label="" if task is None else " / ".join(task.joints), - cycle=None if unit is None else unit.cycle + 1, + cycle=None if unit is None or unit.sample_phase == "steady" else unit.cycle + 1, + cycle_count=( + len(self.diagnostic_capture.cycles) + if self.diagnostic_capture is not None + else 4 + ), direction="" if unit is None else unit.direction, + capture_stage=("command_validation" if unit is not None and unit.sample_phase == "steady" + and unit.cycle == 5 else "command_training" if unit is not None + and unit.sample_phase == "steady" else "motion"), required_tag_ids_by_view=required, ), resume={"message": self._resume_message}, @@ -328,6 +353,10 @@ class CalibrationSession: f"expected {expected.value}" ) + def _require_formal_resume(self) -> None: + if self.diagnostic_capture is not None: + raise ValueError("diagnostic capture cannot reuse previous data") + def _require_any(self, *expected: CalibrationPhase) -> None: if self.phase not in expected: allowed = ",".join(value.value for value in expected) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/snapshot.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/snapshot.py index 26c00f0..d22e74f 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/snapshot.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/snapshot.py @@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Mapping from ..core.domain.status import CalibrationStatus, MotionStatus from .camera_preflight import CameraPreflight +from .diagnostic_quality import evaluate_diagnostic_unit from .scan_quality import evaluate_capture_unit from .session import CalibrationPhase as Phase @@ -17,6 +18,7 @@ if TYPE_CHECKING: _LEGACY_STATES = { Phase.WAIT_DEVICE: "WAIT_DEVICES", Phase.READY: "READY", Phase.COMPLETE: "PASSED", Phase.PAUSED: "PAUSED", + Phase.DIAGNOSTIC_COMPLETE: "DIAGNOSTIC_COMPLETE", Phase.ABORTED: "ABORTED", Phase.FAILED: "FAILED", **{phase: "FINALIZING" for phase in ( Phase.FIT, Phase.HOLDOUT_VALIDATE, Phase.BUILD_ARTIFACTS, @@ -57,6 +59,7 @@ class RuntimeSnapshot: steady: bool = False steady_node: int | None = None camera_preflight: CameraPreflight | None = None + diagnostic_capture: Mapping | None = None def as_dict(self) -> dict: value = self.calibration.as_dict() @@ -67,6 +70,8 @@ class RuntimeSnapshot: value["devices"] = asdict(self.devices) if self.camera_preflight is not None: value["camera_preflight"] = self.camera_preflight.as_dict() + if self.diagnostic_capture is not None: + value["diagnostic_capture"] = dict(self.diagnostic_capture) if self.steady: value["acquisition"]["steady_node"] = self.steady_node return value @@ -100,18 +105,28 @@ def build_snapshot(host: CalibrationCoordinator) -> RuntimeSnapshot: missing = {tag for view, ids in required.items() for tag in host.capture.last_missing.get(view, ids)} & required_ids filtered = {tag for ids in host.capture.last_filtered.values() for tag in ids} & required_ids - steady = motion is not None and motion.phase in {"steady", "steady_prepare"} + interrupted = session.phase in {Phase.PAUSED, Phase.ABORTED, Phase.FAILED} + steady = not interrupted and motion is not None and motion.phase in {"steady", "steady_prepare"} steady_node = None if steady and motion.steady_index is not None: steady_node = motion.steady_index+1 task = replace(task, label=task.label+f"(稳态点 {steady_node})") index = None if motion is None else motion.command_index health = host.sdk_adapter.health() - quality = host.execution.last_quality if session.phase == Phase.PAUSED else None - if session.phase in {Phase.SWEEP, Phase.RESCAN, Phase.EVALUATE}: - quality = evaluate_capture_unit(profile, session.current_unit, - host.execution.action.retry+1, tuple(host._unit_rows), - first_cycle_spans=dict(host.execution.first_cycle_spans)) + # A pause can interrupt a new task before its first evaluation. The last + # completed quality result may belong to another task/direction; scope + # paused statistics to the current unit just like live scan statistics. + quality = None + diagnostic_quality = None + if session.current_unit is not None and session.phase in {Phase.SWEEP, Phase.RESCAN, Phase.EVALUATE, Phase.PAUSED}: + if host.diagnostic_capture is not None: + diagnostic_quality = evaluate_diagnostic_unit(profile, session.current_unit, + host.execution.action.retry+1, tuple(host._unit_rows)) + quality = diagnostic_quality.observations + else: + quality = evaluate_capture_unit(profile, session.current_unit, + host.execution.action.retry+1, tuple(host._unit_rows), + first_cycle_spans=dict(host.execution.first_cycle_spans), include_steady=True) acquisition = replace(status.acquisition, retry_count=host.execution.action.retry) if quality is not None and quality.metrics: metrics = tuple(quality.metrics.values()) @@ -121,7 +136,7 @@ def build_snapshot(host: CalibrationCoordinator) -> RuntimeSnapshot: bins=min(m["feedback_bins"] for m in metrics), maximum_gap=max(m["maximum_bin_gap"] for m in metrics)) status = replace(status, task=task, acquisition=acquisition, - phase=motion.phase if motion is not None and motion.phase in + phase=motion.phase if not interrupted and motion is not None and motion.phase in {"steady", "steady_prepare", "joint_zero", "zero_approach"} else status.phase, recognized_tag_ids=tuple(sorted(required_ids-missing-filtered)), missing_tag_ids=tuple(sorted(missing)), rejected_tag_ids=tuple(sorted(filtered)), @@ -136,11 +151,26 @@ def build_snapshot(host: CalibrationCoordinator) -> RuntimeSnapshot: "source_session": "" if parameters.resume_raw_samples_path is None else parameters.resume_raw_samples_path.parent.name}, outputs={"json": host.final_json, "urdf": host.final_urdf, + "raw_samples": str(host.raw_path), + "failure_diagnostic": str(parameters.session_dir / "failure_diagnostic.json") + if session.phase == Phase.FAILED and host.finalization.started else "", "publication_pointer": str(parameters.session_dir.parent/profile.artifacts.publication_pointer) - if session.phase == Phase.COMPLETE else ""}) + if session.phase == Phase.COMPLETE else "", + **({"diagnostic_report": + str(parameters.session_dir / "diagnostic_capture.json") + if session.phase == Phase.DIAGNOSTIC_COMPLETE else ""} if host.diagnostic_capture else {})}) devices = None if host.started else host._device_status(host.ports.monotonic()) preflight = host._camera_preflight if devices is None else CameraPreflight.from_readiness(devices.cameras) + diagnostic = None + if host.diagnostic_capture is not None: + diagnostic = {**host.diagnostic_capture.as_dict(), "publication_allowed": False, + "accuracy_validated": False, "coverage_scope": "raw_observations", + "unresolved_joint_zero_references": sorted(host.execution.diagnostic_zero_attempts), + "confirmed_joint_zero_references": sorted(host.execution.zero_references)} + if diagnostic_quality is not None: + diagnostic["accepted_pose_quality"] = diagnostic_quality.accepted_pose_report() return RuntimeSnapshot(status, profile.key.model, profile.key.side, profile.key.profile_id, parameters.serial_number, host.reason, str(parameters.session_dir), dict(host.reference_lock.latest_drift_px), - devices, steady, steady_node, preflight) + devices, steady, steady_node, preflight, + diagnostic) diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/status.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/status.py index f4dcb8b..65094f6 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/status.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/status.py @@ -6,6 +6,7 @@ from typing import Any, Mapping from ..core.domain.profile import CalibrationProfile from ..core.domain.status import CalibrationStatus +from .diagnostic_capture import diagnostic_capture_label from .reporting.reasons_zh import reason_zh @@ -20,6 +21,7 @@ _PHASES = { "FIT": "训练数据拟合", "HOLDOUT_VALIDATE": "第四轮独立验证", "BUILD_ARTIFACTS": "生成产物", "VALIDATE_URDF": "校验导出 URDF", "PUBLISH": "发布", "COMPLETE": "会话完成", "PAUSED": "已暂停", + "DIAGNOSTIC_COMPLETE": "诊断采集完成(未进行精度验收)", "ABORTED": "已中止", "FAILED": "失败", "RUNNING": "标定中", "FINALIZING": "拟合与产物校验", "FITTING": "拟合与验证", "baseline": "安全恢复基准", "preflight": "固定通道映射点动", @@ -96,7 +98,7 @@ def normalize_status( if progress is None: count = int(value.get("step_count", 0) or 0) progress = (int(value.get("step_index", 0) or 0) + float(value.get("step_fraction", 0) or 0)) / count if count else 0.0 - result["overall_progress_01"] = 1.0 if state == "COMPLETE" else min(1.0, max(0.0, float(progress))) + result["overall_progress_01"] = 1.0 if state in {"COMPLETE", "DIAGNOSTIC_COMPLETE"} else min(1.0, max(0.0, float(progress))) result["phase"] = str(row.get("phase") or state) result["missing_tag_ids"] = value.get("missing_tag_ids", value.get("unrecognized_tag_ids", ())) result["hardware"] = { @@ -125,15 +127,42 @@ def render_status_zh(value: Mapping[str, Any], estimator=None) -> str: return "未知" if item is None else f"{float(item):.3f} {unit}" def tags(ids): return "/".join(f"ID{int(item)}" for item in ids) or "无" + phase = str(value.get("phase")) + phase_label = _PHASES.get(phase, value.get("phase")) + diagnostic_info = value.get("diagnostic_capture") or {} + diagnostic = bool(diagnostic_info) + sample_label = "原始观测" if diagnostic_info.get("coverage_scope") == "raw_observations" else "有效样本" + if diagnostic and phase in {"SWEEP", "sweep"}: + phase_label = "诊断扫描" + if diagnostic_info.get("hold_command_values"): + if phase == "steady_prepare": + phase_label = "方向差保持采集起点准备" + elif phase == "steady": + phase_label = f"方向差保持采集(每点至少 {diagnostic_info['hold_seconds']:g} 秒)" + capture_stage = task.get("capture_stage", "motion") + stage_line = f"阶段:{phase_label}(第 {task.get('cycle') or '-'}/{task.get('cycle_count', 4)} 轮)" + if capture_stage in {"command_training", "command_validation"}: + stage_line = "阶段:" + ("稳态指令映射训练" if capture_stage == "command_training" else "稳态指令映射独立验证") lines = [ f"[{value.get('model', '未知型号')} / {value.get('side', '?')} / {value.get('serial_number', '?')}] {progress:6.1%} 预计剩余 {eta_text}", - f"阶段:{_PHASES.get(str(value.get('phase')), value.get('phase'))}(第 {task.get('cycle') or '-'}/{task.get('cycle_count', 4)} 轮)", + stage_line, f"任务:{task.get('label') or task.get('key') or '无'};方向:{task.get('direction') or '-'}", f"命令/反馈:{angle(motion.get('command'))}/{angle(motion.get('feedback'))}", "任务所需 Tag:" + (";".join(f"{view}={tags(ids)}" for view, ids in task.get("required_tag_ids_by_view", {}).items()) or "无"), f"Tag:已识别 {tags(value.get('recognized_tag_ids', ()))};缺失 {tags(value.get('missing_tag_ids', ()))};过滤 {tags(value.get('rejected_tag_ids', ()))}", - f"有效样本 {acquisition.get('valid_samples', 0)};覆盖 {float(acquisition.get('coverage_01', 0)):.1%};分箱 {acquisition.get('bins', 0)};内部空白 {acquisition.get('maximum_gap', 0)};重扫 {acquisition.get('retry_count', 0)} 次", + f"{sample_label} {acquisition.get('valid_samples', 0)};覆盖 {float(acquisition.get('coverage_01', 0)):.1%};分箱 {acquisition.get('bins', 0)};内部空白 {acquisition.get('maximum_gap', 0)};重扫 {acquisition.get('retry_count', 0)} 次", ] + if diagnostic: + label = diagnostic_capture_label(diagnostic_info.get("mode", "o6_thumb")) + lines.insert(1, f"模式:{label};仅采集观测,不发布标定 JSON/URDF") + accepted = diagnostic_info.get("accepted_pose_quality", {}) + metrics = tuple(accepted.get("metrics", {}).values()) + if metrics: + lines.append(f"可信姿态样本 {min(m['valid_frames'] for m in metrics)};" + f"覆盖 {min(m['feedback_span'] for m in metrics):.1%}(未进行精度验收)") + unresolved = diagnostic_info.get("unresolved_joint_zero_references", ()) + if unresolved: + lines.append("零位未确认,仅保留原始观测:" + ", ".join(unresolved)) waiting = value.get("devices", {}).get("waiting_for", ()) if waiting: lines.append("等待项:" + ";".join(waiting)) @@ -163,7 +192,8 @@ def render_status_zh(value: Mapping[str, Any], estimator=None) -> str: resume = value.get("resume", {}) if resume: message = resume.get('message', resume.get('reason', '')) - message = {"not_requested": "本次完整重采,未请求恢复"}.get(message, message) + message = {"not_requested": "本次完整重采,未请求恢复", + "diagnostic_capture_no_resume": "诊断采集,不复用断点"}.get(message, message) lines.append(f"断点:{message};复用 {resume.get('completed_unit_count', 0)} 单元;来源 {resume.get('source_session') or '-'}") hardware = value.get("hardware", {}) lines.append(f"SDK:反馈 {hardware.get('feedback_hz', '未知')} Hz;模式确认 {hardware.get('mode_verified', '未知')};故障 {hardware.get('faults', '未知')}") diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/steady.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/steady.py index aa3abbb..1af09c2 100644 --- a/src/linkerhand_calibration/linkerhand_calibration/runtime/steady.py +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/steady.py @@ -3,10 +3,12 @@ import math -def steady_targets(profile, unit): - from ..core.domain.sampling import command_nodes +def steady_targets(profile, unit, *, records=(), training_nodes=None): + from ..core.domain.sampling import command_nodes, command_holdout_cycle task = next(t for t in profile.motion.tasks if t.key == unit.task_key) - values = command_nodes(profile, task, holdout=unit.cycle == 3) + from ..core.fitting.command_sampling import declared_training_nodes + values = (command_nodes(profile, task, holdout=True) if unit.cycle == command_holdout_cycle(profile) + else training_nodes if training_nodes is not None else declared_training_nodes(profile, task, records)) return tuple(values if unit.direction == "increasing" else reversed(values)) @@ -18,7 +20,7 @@ def steady_failures(profile, unit, attempt, records): and r.get("task_name") == unit.task_key and r.get("cycle") == unit.cycle and r.get("direction") == unit.direction and int(r.get("attempt", 1)) == attempt] for field, name, spec in observation_streams(profile, task): - for index, target in enumerate(steady_targets(profile, unit)): + for index, target in enumerate(steady_targets(profile, unit, records=records)): images = {r.get("image_stamp_ns") for r in selected if r.get("sample_phase") == "steady" and r.get(field) == name and r.get("view") == spec.view and r.get("task_name") == unit.task_key diff --git a/src/linkerhand_calibration/linkerhand_calibration/runtime/zero_recovery.py b/src/linkerhand_calibration/linkerhand_calibration/runtime/zero_recovery.py new file mode 100644 index 0000000..d6d2164 --- /dev/null +++ b/src/linkerhand_calibration/linkerhand_calibration/runtime/zero_recovery.py @@ -0,0 +1,29 @@ +"""One bounded recovery per unreferenced joint group, without new zero claims.""" + + +class ZeroRecovery: + def __init__(self): + self._used = set() + + def take(self, motion, *, geometry_error, reports, referenced_joints): + if not motion.zero_joints or set(motion.zero_joints) & set(referenced_joints): + return None + if geometry_error: + # A partially installed multi-view model must not be refitted. + # Faults, changed holding poses and corrupt input are not retries. + if any(report.get("status") == "resolved" for report in reports.values()): + return None + reasons = {report.get("reason", "") for report in reports.values()} + recoverable = {"image_motion_insufficient_independent_frames", "image_motion_no_observable_image_model", + "image_motion_validation_frame_unresolved", "image_motion_families_not_distinguishable", + "image_motion_incomplete_candidate_family", "motion_geometry_unresolved"} + if not reasons or not reasons <= recoverable: + return None + action = "repeat_approach" + else: + action = "retry_zero_images" + key = (motion.task_key, tuple(motion.zero_joints)) + if key in self._used: + return None + self._used.add(key) + return action diff --git a/src/linkerhand_calibration/setup.py b/src/linkerhand_calibration/setup.py index 6bf6905..8904e62 100644 --- a/src/linkerhand_calibration/setup.py +++ b/src/linkerhand_calibration/setup.py @@ -52,12 +52,15 @@ setup( license="MIT", entry_points={ "console_scripts": [ + "rebuild_calibrated_urdf = linkerhand_calibration.runtime.artifacts.urdf_from_json:main", + "analyze_calibration_capture = linkerhand_calibration.runtime.diagnostic_analysis:main", "compare_calibration_urdfs = linkerhand_calibration.urdf_comparison:main", ( "hikrobot_camera_node = " "linkerhand_calibration.hikrobot_camera:main" ), "calibration_node = linkerhand_calibration.runtime.ros.entrypoint:main", + "tag_border_filter_node = linkerhand_calibration.runtime.ros.tag_border_filter:main", "o12_sdk_bridge = linkerhand_calibration.runtime.adapters.o12_bridge:main", ( "three_camera_calibration_node = " diff --git a/src/linkerhand_calibration/test/engine_capture_fixture.py b/src/linkerhand_calibration/test/engine_capture_fixture.py index 5d4de0e..a0417fd 100644 --- a/src/linkerhand_calibration/test/engine_capture_fixture.py +++ b/src/linkerhand_calibration/test/engine_capture_fixture.py @@ -17,6 +17,7 @@ from linkerhand_calibration.runtime.capture import CaptureFrame, ObservationCapt from linkerhand_calibration.runtime.execution import SessionExecution from linkerhand_calibration.runtime.reference_lock import ReferenceLock from linkerhand_calibration.runtime.session import CalibrationPhase as Phase +from pose_branch_fixture import IdealPoseTracker def collect_with_engine(profile, observations): @@ -38,11 +39,12 @@ def collect_with_engine(profile, observations): key = (row["task_name"], row["cycle"], row["direction"], row.get("sample_phase", "sweep"), row.get("steady_index")) frame_groups[key][row["sample_id"]].append((row, spec)) poses = {view: {} for view in profile.vision.view_names} - class IdealPoseSensor: - maximum_reprojection_error_px = 1.5 - def __init__(self, view): self.view = view - def reset(self): pass - def estimate(self, role, corners, **kwargs): return poses[self.view][role], "" + class IdealPoseSensor(IdealPoseTracker): + def __init__(self, view): + super().__init__() + self.view = view + def estimate(self, role, corners, **kwargs): + return self.accept_ideal_pose(role, poses[self.view][role], kwargs["stamp_ns"]) def camera_pose(payload): value = camera_inverse @ transform_matrix(payload["translation_xyz_m"], payload["quaternion_xyzw"]) return SquareTagPose(tuple(Rotation.from_matrix(value[:3, :3]).as_quat()), tuple(value[:3, 3]), 0.) diff --git a/src/linkerhand_calibration/test/fixtures/camera_clock_receipts.json.gz b/src/linkerhand_calibration/test/fixtures/camera_clock_receipts.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..7a7432933b7e0568528c70756794061bf364419c GIT binary patch literal 12185 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 literal 0 HcmV?d00001 diff --git a/src/linkerhand_calibration/test/fixtures/o6_front_20260914_175815_raw_replay.json.gz b/src/linkerhand_calibration/test/fixtures/o6_front_20260914_175815_raw_replay.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..dc20bf79397803cda6869a1b8f264c6746068858 GIT binary patch literal 345861 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�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 z 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

J?K{SkR2-Xrhs(NA7G|=j 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 P{qO%D`t5OIIjtZ7abAR$ literal 0 HcmV?d00001 diff --git a/src/linkerhand_calibration/test/fixtures/o6_front_20260914_192637_raw_replay.json.gz b/src/linkerhand_calibration/test/fixtures/o6_front_20260914_192637_raw_replay.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..33963fab0898c982e26c599073a9913f58810ebf GIT binary patch literal 112690 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

&wDK9U?O)F;zgAFW-G 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 z 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 z zw=t5tV%SE3Y{CI%%(s=(RTbw^K-Rv>zO6N?#!<1(UtvlJqYeU9x~PACP7wJlFuy?L zCjgQ4oH*6js-SXo{&65O*jY5&8r7ZV{C=GehIL%wIHw|d@$8A~lR@OZNWW(PsZezA zzQid^fk_G=vKo7DN_tB$SUHHM@%STqnRyQ&Y&(5|l;W6z$!Y?NN!t$r3#B3KHz&VL zufj_x_~UTS7YYUwivuT-4I~fcdP5sCmgUyo!CfyyPFF1p=~{t1O<~23ZDnEd)n|lt z_bj4cchyC>v6YC4pct>bhJd3q*(F;LgX8QiG5P-;}qh(uA}!N?7aB-wO*3&Rg5 zDB|gUa|E8kR#_ zNhV>d6_EhaT%zhuEYYWa>$X5 zk5m{(459s?Y%@^g8c&vStsYF%05MlpixWdu$TDxwA%X;k+##RNLq-N&RKXpRucgeX znWBW`56O8f@s&duEY5l{|Ky$)dlKj8+}76jTOi{hCq|X$Jfj zN0aRmQ76Zv4)vzKmZ+Pr-$oT>t+2)r5PEVSQLp$uxAX-f-v=V+J3-|1C-h?))Pk05 z`uCNOv3)E*3pVaOf|mP=s+nsKf{lxrCM(M7pZS5x6(uP|s>ZJ{HvMg|F+2)VIZ9yu zDPiOHkKo^6AL~XA)3-3m$MfubLBLh&)$QZ({FaOo7P|;+QDL}siGFB^+3_>2K z?gkkrjzv2dqIxoej7?p5R8aXTG%FgX&m>_F`+Cui)j^bYx5o2YWA&3NjD<&9S&7vh)VQQ`KSfEwz{NaT~y5&>I=D z3w3tW$vcF5vPaj+o1M}Ki)E18K9`s04Kz9izF?wPV*|42pH-=*3Aknztz#+nBY0E7*ze;#mR>c^V;kjXBv{jzRsEpfPWw zak~x|9U51UTC>dl4?#O_DXs{k_CmgAgr2gU0Y=Y^wHN}f&6sb{<9=#TUxXNqvA#NktgB=0w(77BS!}s4<@bE(Jd=JM;<~BieACamW3ka~&>LO~?Zc z*VkZqGSgP@-k{eVVu-iB{|0RZq{6Je%s|vy^EC_=s`gAl&h4tcz9C#K69gjNz+_$C zu#uDOSl;~)!Nz}$QvYn+m^Bi&JH7?Q!;x0QZpwpxW)viEZpID#uBj+2-=4u_ z_yB~n>RJ&hA){OBjKyDDLy2L)3*ToWfgQan# z60RaG!wX)X-yf!Nvqxk;kM8=uP|5*Aq}WjDj84RibHp1mHfx(VM!A481 z4!97x3y*%B?h#@HdrsRmjr_uoIJbc1=cE9RZg)6NG9Th8uEgIlz`fokR?XJVI=iYy zahFe`;8~F;dWWP#5|+0v&b9B%c(iVhw)D7eh;=er2zx z@#gk$4Qa@S!C&9E5;$OE6Q1%i+Vad087*^zB_yt3e}py1+Z^w9Vr~5QyuXav6~Eu( zqBWi6q1e8s5p!e%G3QC1X?Xqe`HMFLTn(GQ#J1r$rE->oFK|VLC}G7`Lc!7?D~~Um zo^(cXu7Zi7?VG3+c^A4Ih`DnKl5i>6uDXXOGXRpz$aDLkoz=y2`4Ogs@A$YeZZS-4 zZbBq&sPv{!#`utnE)cml#pJ7p$LF13Kxf-GE>N3XjFGYqDR6$E0vxZ&y~g??M5l{! urRBXbpIhpa%B47)w23~Q#ZavcV|KiCiRJ9ja(S7%diVfwMoFO7rwRZfr{OaI literal 0 HcmV?d00001 diff --git a/src/linkerhand_calibration/test/fixtures/o6_front_base_20260914_161416_quality_events.json b/src/linkerhand_calibration/test/fixtures/o6_front_base_20260914_161416_quality_events.json new file mode 100644 index 0000000..61b44e0 --- /dev/null +++ b/src/linkerhand_calibration/test/fixtures/o6_front_base_20260914_161416_quality_events.json @@ -0,0 +1,474 @@ +{ + "source_session": "O6_RIGHT_001/20260914_161416", + "source_raw_sha256": "3eefc417ab968b54879241855336bf9ae5a5b5d31d8ae41c2915eac109afa912", + "scope": "Recorded frozen reference and five deformed front ID0 detections. No images after the pause were recorded; this fixture cannot prove actual recovery.", + "view": "front", + "tag_role": "front_base", + "tag_id": 0, + "tag_size_m": 0.016, + "camera_matrix": [ + [ + 3792.732421875, + 0.0, + 606.0140166893543 + ], + [ + 0.0, + 3825.36962890625, + 568.5244182036549 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ], + "reference_corners_xy": [ + [ + 651.6114807128906, + 852.3683776855469 + ], + [ + 708.6465759277344, + 845.8826293945312 + ], + [ + 701.8229675292969, + 787.6893920898438 + ], + [ + 644.96826171875, + 794.0107421875 + ] + ], + "reference_pose": { + "rotation_xyzw": [ + 0.05122618750941407, + 0.07257853631173206, + 0.05376952559779321, + -0.9945939231148989 + ], + "translation_xyz_m": [ + 0.019507045482571185, + 0.06870574180366773, + 1.0452219935270852 + ] + }, + "events": [ + { + "kind": "pnp_branch_event", + "view": "front", + "image_stamp_ns": 1789373674703790511, + "tag_role": "front_base", + "tag_id": 0, + "reason": "pose_branch_unconfirmed", + "pnp_observation_evidence": { + "tag_role": "front_base", + "tag_id": 0, + "tag_size_m": 0.016, + "pose_source": "current_image", + "corners_xy": [ + [ + 650.7820434570314, + 849.603271484375 + ], + [ + 708.2871704101564, + 846.6178588867189 + ], + [ + 701.3147583007812, + 787.5760498046876 + ], + [ + 644.46142578125, + 793.9281005859375 + ] + ], + "selected_pose": null, + "reprojection_valid_candidates": [ + { + "quaternion_xyzw": [ + 0.0689494439189896, + 0.0892766701000514, + -0.052421998914305996, + 0.9922336339738425 + ], + "translation_xyz_m": [ + 0.01927051064753879, + 0.0683358712786946, + 1.04173012169382 + ], + "reprojection_error_px": 0.8203549082285015 + }, + { + "quaternion_xyzw": [ + -0.13467791812542612, + -0.07440102998631523, + -0.04520090653912901, + 0.987057862110635 + ], + "translation_xyz_m": [ + 0.019286540244008814, + 0.0683040199267154, + 1.0416235980482782 + ], + "reprojection_error_px": 0.8884351664530162 + } + ], + "candidate_diagnostics": { + "solved_candidate_count": 2, + "reprojection_candidate_count": 2, + "independent_tilt_candidate_count": 2, + "maximum_reprojection_error_px": 1.5, + "maximum_independent_tilt_deg": 75.0, + "minimum_reprojection_error_px": 0.8203549082285015, + "minimum_candidate_tilt_deg": 12.9537577740523, + "maximum_candidate_tilt_deg": 17.701656618264686, + "pose_constraint": "fixed_reference", + "fixed_reference_reprojection_error_px": 1.5511578554957017, + "fixed_reference_verified": false, + "observation_stamp_ns": 1789373674703790511, + "branch_revision": 2, + "branch_status": "fixed_reference_pending", + "branch_reason": "pose_branch_unconfirmed", + "branch_confirmation_count": 1, + "branch_frozen": true + }, + "branch_corrections_total": 1 + } + }, + { + "kind": "pnp_branch_event", + "view": "front", + "image_stamp_ns": 1789373674737107853, + "tag_role": "front_base", + "tag_id": 0, + "reason": "pose_branch_unconfirmed", + "pnp_observation_evidence": { + "tag_role": "front_base", + "tag_id": 0, + "tag_size_m": 0.016, + "pose_source": "current_image", + "corners_xy": [ + [ + 650.986572265625, + 849.4489135742189 + ], + [ + 708.4765625, + 846.6978759765625 + ], + [ + 701.5152587890625, + 787.5821533203124 + ], + [ + 644.6348266601564, + 793.9254150390625 + ] + ], + "selected_pose": null, + "reprojection_valid_candidates": [ + { + "quaternion_xyzw": [ + 0.07274616669000304, + 0.09173097100644584, + -0.05204806881576712, + 0.9917582481243471 + ], + "translation_xyz_m": [ + 0.019305954335328467, + 0.06827132134840262, + 1.0408129484230706 + ], + "reprojection_error_px": 0.8723269389119234 + }, + { + "quaternion_xyzw": [ + -0.1384302855795689, + -0.07679770133531316, + -0.044595240947531964, + 0.9863824986225185 + ], + "translation_xyz_m": [ + 0.019322228608171357, + 0.0682377527103236, + 1.0406931830626773 + ], + "reprojection_error_px": 0.944197809557915 + } + ], + "candidate_diagnostics": { + "solved_candidate_count": 2, + "reprojection_candidate_count": 2, + "independent_tilt_candidate_count": 2, + "maximum_reprojection_error_px": 1.5, + "maximum_independent_tilt_deg": 75.0, + "minimum_reprojection_error_px": 0.8723269389119234, + "minimum_candidate_tilt_deg": 13.446654725220979, + "maximum_candidate_tilt_deg": 18.217183252762826, + "pose_constraint": "fixed_reference", + "fixed_reference_reprojection_error_px": 1.573755927601701, + "fixed_reference_verified": false, + "observation_stamp_ns": 1789373674737107853, + "branch_revision": 2, + "branch_status": "fixed_reference_pending", + "branch_reason": "pose_branch_unconfirmed", + "branch_confirmation_count": 2, + "branch_frozen": true + }, + "branch_corrections_total": 1 + } + }, + { + "kind": "pnp_branch_event", + "view": "front", + "image_stamp_ns": 1789373674770184767, + "tag_role": "front_base", + "tag_id": 0, + "reason": "pose_branch_unconfirmed", + "pnp_observation_evidence": { + "tag_role": "front_base", + "tag_id": 0, + "tag_size_m": 0.016, + "pose_source": "current_image", + "corners_xy": [ + [ + 651.1397705078125, + 849.4150390625003 + ], + [ + 708.6453247070312, + 846.7749633789064 + ], + [ + 701.6513061523439, + 787.606018066406 + ], + [ + 644.8040161132812, + 793.9556884765625 + ] + ], + "selected_pose": null, + "reprojection_valid_candidates": [ + { + "quaternion_xyzw": [ + 0.07377523792253451, + 0.09322053207746203, + -0.051915478601721, + 0.9915502658716917 + ], + "translation_xyz_m": [ + 0.01934209599907669, + 0.068254351936264, + 1.040451517901641 + ], + "reprojection_error_px": 0.9029827200918235 + }, + { + "quaternion_xyzw": [ + -0.1394470128719977, + -0.07824625765923164, + -0.04434376576803557, + 0.9861367472115171 + ], + "translation_xyz_m": [ + 0.0193585945536293, + 0.06822023556286914, + 1.040326908596307 + ], + "reprojection_error_px": 0.975293447689657 + } + ], + "candidate_diagnostics": { + "solved_candidate_count": 2, + "reprojection_candidate_count": 2, + "independent_tilt_candidate_count": 2, + "maximum_reprojection_error_px": 1.5, + "maximum_independent_tilt_deg": 75.0, + "minimum_reprojection_error_px": 0.9029827200918235, + "minimum_candidate_tilt_deg": 13.65513087280534, + "maximum_candidate_tilt_deg": 18.402160681532425, + "pose_constraint": "fixed_reference", + "fixed_reference_reprojection_error_px": 1.5711497009701978, + "fixed_reference_verified": false, + "observation_stamp_ns": 1789373674770184767, + "branch_revision": 2, + "branch_status": "fixed_reference_pending", + "branch_reason": "pose_branch_unconfirmed", + "branch_confirmation_count": 3, + "branch_frozen": true + }, + "branch_corrections_total": 1 + } + }, + { + "kind": "pnp_branch_event", + "view": "front", + "image_stamp_ns": 1789373674803781247, + "tag_role": "front_base", + "tag_id": 0, + "reason": "pose_branch_unconfirmed", + "pnp_observation_evidence": { + "tag_role": "front_base", + "tag_id": 0, + "tag_size_m": 0.016, + "pose_source": "current_image", + "corners_xy": [ + [ + 651.1066894531251, + 849.4465332031251 + ], + [ + 708.6222534179689, + 846.8680419921876 + ], + [ + 701.619140625, + 787.6516723632811 + ], + [ + 644.7723388671875, + 794.01220703125 + ] + ], + "selected_pose": null, + "reprojection_valid_candidates": [ + { + "quaternion_xyzw": [ + 0.07416052126135776, + 0.09401877076851597, + -0.05184184633608467, + 0.9914500041847227 + ], + "translation_xyz_m": [ + 0.019326739165784403, + 0.06824498811606222, + 1.0400731042057596 + ], + "reprojection_error_px": 0.9209144087717472 + }, + { + "quaternion_xyzw": [ + -0.13983752470383345, + -0.07905290919387553, + -0.04421050999107296, + 0.9860230905202062 + ], + "translation_xyz_m": [ + 0.019343352432883133, + 0.06821053937063642, + 1.0399448592973592 + ], + "reprojection_error_px": 0.9939407301090831 + } + ], + "candidate_diagnostics": { + "solved_candidate_count": 2, + "reprojection_candidate_count": 2, + "independent_tilt_candidate_count": 2, + "maximum_reprojection_error_px": 1.5, + "maximum_independent_tilt_deg": 75.0, + "minimum_reprojection_error_px": 0.9209144087717472, + "minimum_candidate_tilt_deg": 13.754987577665794, + "maximum_candidate_tilt_deg": 18.487617322056742, + "pose_constraint": "fixed_reference", + "fixed_reference_reprojection_error_px": 1.5736869605739345, + "fixed_reference_verified": false, + "observation_stamp_ns": 1789373674803781247, + "branch_revision": 2, + "branch_status": "fixed_reference_pending", + "branch_reason": "pose_branch_unconfirmed", + "branch_confirmation_count": 4, + "branch_frozen": true + }, + "branch_corrections_total": 1 + } + }, + { + "kind": "pnp_branch_event", + "view": "front", + "image_stamp_ns": 1789373674837065731, + "tag_role": "front_base", + "tag_id": 0, + "reason": "pose_branch_conflict", + "pnp_observation_evidence": { + "tag_role": "front_base", + "tag_id": 0, + "tag_size_m": 0.016, + "pose_source": "current_image", + "corners_xy": [ + [ + 650.91162109375, + 849.4388427734373 + ], + [ + 708.4180297851561, + 846.9037475585938 + ], + [ + 701.4273681640625, + 787.6770629882815 + ], + [ + 644.5805053710939, + 794.0317382812501 + ] + ], + "selected_pose": null, + "reprojection_valid_candidates": [ + { + "quaternion_xyzw": [ + 0.0745974181611575, + 0.09433986520903168, + -0.051717641242694276, + 0.9913932119095445 + ], + "translation_xyz_m": [ + 0.019272532256405614, + 0.06824822341509994, + 1.040046126872147 + ], + "reprojection_error_px": 0.9295222058852461 + }, + { + "quaternion_xyzw": [ + -0.14027036228299425, + -0.07941984067154015, + -0.04406108200999516, + 0.9859388091686081 + ], + "translation_xyz_m": [ + 0.019289181266401056, + 0.068213529376978, + 1.0399155768327384 + ], + "reprojection_error_px": 1.0031381095366472 + } + ], + "candidate_diagnostics": { + "solved_candidate_count": 2, + "reprojection_candidate_count": 2, + "independent_tilt_candidate_count": 2, + "maximum_reprojection_error_px": 1.5, + "maximum_independent_tilt_deg": 75.0, + "minimum_reprojection_error_px": 0.9295222058852461, + "minimum_candidate_tilt_deg": 13.81532839490434, + "maximum_candidate_tilt_deg": 18.55233483567943, + "pose_constraint": "fixed_reference", + "fixed_reference_reprojection_error_px": 1.6233466461226242, + "fixed_reference_verified": false, + "observation_stamp_ns": 1789373674837065731, + "branch_revision": 2, + "branch_status": "frozen_conflict", + "branch_reason": "pose_branch_conflict", + "branch_confirmation_count": 5, + "branch_frozen": true + }, + "branch_corrections_total": 1 + } + } + ] +} diff --git a/src/linkerhand_calibration/test/fixtures/o6_front_base_20260914_branch_events.json b/src/linkerhand_calibration/test/fixtures/o6_front_base_20260914_branch_events.json new file mode 100644 index 0000000..910924c --- /dev/null +++ b/src/linkerhand_calibration/test/fixtures/o6_front_base_20260914_branch_events.json @@ -0,0 +1,459 @@ +{ + "source_session": "O6_RIGHT_001/20260914_160325", + "source_raw_sha256": "e08be16c6c77f031cdc524c89fdeb103f2a5cf2c3c8bb67187f6b5db570f640f", + "scope": "Recorded frozen reference and five conflicting front ID0 images; successful preparation images were not recorded. Not a full session or accuracy replay.", + "view": "front", + "tag_role": "front_base", + "tag_id": 0, + "tag_size_m": 0.016, + "reference_corners_xy": [ + [ + 651.3810424804688, + 852.2985229492188 + ], + [ + 708.4353942871094, + 845.8116455078125 + ], + [ + 701.5877380371094, + 787.6119079589844 + ], + [ + 644.7322692871093, + 793.9474182128906 + ] + ], + "reference_pose": { + "rotation_xyzw": [ + 0.051657382830113, + 0.07233818005965809, + 0.053787910495170524, + -0.9945881374666417 + ], + "translation_xyz_m": [ + 0.019443198546650248, + 0.06867587597205813, + 1.04523215512729 + ] + }, + "camera_matrix": [ + [ + 3792.732421875, + 0.0, + 606.0140166893543 + ], + [ + 0.0, + 3825.36962890625, + 568.5244182036549 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ], + "events": [ + { + "kind": "pnp_branch_event", + "view": "front", + "image_stamp_ns": 1789373017897501233, + "tag_role": "front_base", + "tag_id": 0, + "reason": "pose_branch_unconfirmed", + "pnp_observation_evidence": { + "tag_role": "front_base", + "tag_id": 0, + "tag_size_m": 0.016, + "pose_source": "current_image", + "corners_xy": [ + [ + 651.2639160156251, + 851.8523559570312 + ], + [ + 708.3605346679686, + 845.7075195312501 + ], + [ + 701.5051879882811, + 787.6116333007814 + ], + [ + 644.6477050781251, + 793.9578247070312 + ] + ], + "selected_pose": null, + "reprojection_valid_candidates": [ + { + "quaternion_xyzw": [ + -0.0017637893722219381, + 0.0741246018891116, + -0.058492815624425876, + 0.9955305233704078 + ], + "translation_xyz_m": [ + 0.019470355602835353, + 0.06889911011030934, + 1.0489767358449047 + ], + "reprojection_error_px": 0.05631830982299966 + }, + { + "quaternion_xyzw": [ + -0.06454724733730362, + -0.05935707358630751, + -0.05353332191340803, + 0.9947082859418444 + ], + "translation_xyz_m": [ + 0.01948586951276129, + 0.06889041042895316, + 1.0489728093762585 + ], + "reprojection_error_px": 0.08873372663232251 + } + ], + "candidate_diagnostics": { + "solved_candidate_count": 2, + "reprojection_candidate_count": 2, + "independent_tilt_candidate_count": 2, + "maximum_reprojection_error_px": 1.5, + "maximum_independent_tilt_deg": 75.0, + "minimum_reprojection_error_px": 0.05631830982299966, + "minimum_candidate_tilt_deg": 8.504262310381518, + "maximum_candidate_tilt_deg": 10.061504700361334, + "observation_stamp_ns": 1789373017897501233, + "branch_revision": 1, + "branch_status": "conflict_pending", + "branch_reason": "pose_branch_unconfirmed", + "branch_confirmation_count": 1, + "branch_frozen": true + }, + "branch_corrections_total": 0 + } + }, + { + "kind": "pnp_branch_event", + "view": "front", + "image_stamp_ns": 1789373017930878824, + "tag_role": "front_base", + "tag_id": 0, + "reason": "pose_branch_unconfirmed", + "pnp_observation_evidence": { + "tag_role": "front_base", + "tag_id": 0, + "tag_size_m": 0.016, + "pose_source": "current_image", + "corners_xy": [ + [ + 651.2874145507811, + 851.705810546875 + ], + [ + 708.3966674804688, + 845.7094116210938 + ], + [ + 701.5343017578125, + 787.6038208007812 + ], + [ + 644.6768798828125, + 793.9531250000001 + ] + ], + "selected_pose": null, + "reprojection_valid_candidates": [ + { + "quaternion_xyzw": [ + 0.003890939029632483, + 0.0723875846158807, + -0.0582059228393556, + 0.99566910604546 + ], + "translation_xyz_m": [ + 0.019486955902090344, + 0.06891798815024941, + 1.0494180790291907 + ], + "reprojection_error_px": 0.08163328008782858 + }, + { + "quaternion_xyzw": [ + -0.07018371470015455, + -0.05760421277028976, + -0.05325691004080239, + 0.994444418957092 + ], + "translation_xyz_m": [ + 0.019501897842959144, + 0.06890773159582059, + 1.049410573675387 + ], + "reprojection_error_px": 0.12399867839301451 + } + ], + "candidate_diagnostics": { + "solved_candidate_count": 2, + "reprojection_candidate_count": 2, + "independent_tilt_candidate_count": 2, + "maximum_reprojection_error_px": 1.5, + "maximum_independent_tilt_deg": 75.0, + "minimum_reprojection_error_px": 0.08163328008782858, + "minimum_candidate_tilt_deg": 8.314273514007814, + "maximum_candidate_tilt_deg": 10.418858251220792, + "observation_stamp_ns": 1789373017930878824, + "branch_revision": 1, + "branch_status": "conflict_pending", + "branch_reason": "pose_branch_unconfirmed", + "branch_confirmation_count": 2, + "branch_frozen": true + }, + "branch_corrections_total": 0 + } + }, + { + "kind": "pnp_branch_event", + "view": "front", + "image_stamp_ns": 1789373017964203500, + "tag_role": "front_base", + "tag_id": 0, + "reason": "pose_branch_unconfirmed", + "pnp_observation_evidence": { + "tag_role": "front_base", + "tag_id": 0, + "tag_size_m": 0.016, + "pose_source": "current_image", + "corners_xy": [ + [ + 651.2019653320314, + 851.4394531249999 + ], + [ + 708.349365234375, + 845.7827758789062 + ], + [ + 701.4730834960938, + 787.6358032226564 + ], + [ + 644.6278686523438, + 793.9785156249999 + ] + ], + "selected_pose": null, + "reprojection_valid_candidates": [ + { + "quaternion_xyzw": [ + 0.015547389407951088, + 0.07097620670743443, + -0.05751186024699874, + 0.9956972645814344 + ], + "translation_xyz_m": [ + 0.019476327145696467, + 0.06893103415003812, + 1.0497373401099155 + ], + "reprojection_error_px": 0.15922816946232976 + }, + { + "quaternion_xyzw": [ + -0.08179270149397419, + -0.0561873622981887, + -0.05244234274300198, + 0.993681405173668 + ], + "translation_xyz_m": [ + 0.019490603849034997, + 0.0689175370110156, + 1.049722140431075 + ], + "reprojection_error_px": 0.2064577346482914 + } + ], + "candidate_diagnostics": { + "solved_candidate_count": 2, + "reprojection_candidate_count": 2, + "independent_tilt_candidate_count": 2, + "maximum_reprojection_error_px": 1.5, + "maximum_independent_tilt_deg": 75.0, + "minimum_reprojection_error_px": 0.15922816946232976, + "minimum_candidate_tilt_deg": 8.333461835216385, + "maximum_candidate_tilt_deg": 11.38993872662255, + "observation_stamp_ns": 1789373017964203500, + "branch_revision": 1, + "branch_status": "conflict_pending", + "branch_reason": "pose_branch_unconfirmed", + "branch_confirmation_count": 3, + "branch_frozen": true + }, + "branch_corrections_total": 0 + } + }, + { + "kind": "pnp_branch_event", + "view": "front", + "image_stamp_ns": 1789373017997253983, + "tag_role": "front_base", + "tag_id": 0, + "reason": "pose_branch_unconfirmed", + "pnp_observation_evidence": { + "tag_role": "front_base", + "tag_id": 0, + "tag_size_m": 0.016, + "pose_source": "current_image", + "corners_xy": [ + [ + 651.0892944335936, + 851.2770996093753 + ], + [ + 708.2705688476564, + 845.88818359375 + ], + [ + 701.3729858398438, + 787.6652832031248 + ], + [ + 644.5391845703123, + 794.0059814453125 + ] + ], + "selected_pose": null, + "reprojection_valid_candidates": [ + { + "quaternion_xyzw": [ + 0.023056471882254036, + 0.07254119338325239, + -0.05704579812544366, + 0.9954656956837376 + ], + "translation_xyz_m": [ + 0.019440361452008358, + 0.06889820121116787, + 1.0492236406616988 + ], + "reprojection_error_px": 0.22416292552376008 + }, + { + "quaternion_xyzw": [ + -0.0892589805271742, + -0.05776484561035559, + -0.05173711763178937, + 0.9929850591353404 + ], + "translation_xyz_m": [ + 0.019454762778121373, + 0.06888246787152717, + 1.0492014615765388 + ], + "reprojection_error_px": 0.27421207829911554 + } + ], + "candidate_diagnostics": { + "solved_candidate_count": 2, + "reprojection_candidate_count": 2, + "independent_tilt_candidate_count": 2, + "maximum_reprojection_error_px": 1.5, + "maximum_independent_tilt_deg": 75.0, + "minimum_reprojection_error_px": 0.22416292552376008, + "minimum_candidate_tilt_deg": 8.7308314332582, + "maximum_candidate_tilt_deg": 12.206445788097424, + "observation_stamp_ns": 1789373017997253983, + "branch_revision": 1, + "branch_status": "conflict_pending", + "branch_reason": "pose_branch_unconfirmed", + "branch_confirmation_count": 4, + "branch_frozen": true + }, + "branch_corrections_total": 0 + } + }, + { + "kind": "pnp_branch_event", + "view": "front", + "image_stamp_ns": 1789373018030961996, + "tag_role": "front_base", + "tag_id": 0, + "reason": "pose_branch_conflict", + "pnp_observation_evidence": { + "tag_role": "front_base", + "tag_id": 0, + "tag_size_m": 0.016, + "pose_source": "current_image", + "corners_xy": [ + [ + 651.0183715820312, + 851.0774536132814 + ], + [ + 708.2586669921877, + 846.02001953125 + ], + [ + 701.3419189453124, + 787.6656494140623 + ], + [ + 644.4960327148436, + 794.0057373046875 + ] + ], + "selected_pose": null, + "reprojection_valid_candidates": [ + { + "quaternion_xyzw": [ + 0.030940978354305437, + 0.07516598998187739, + -0.05644175675597389, + 0.9950914821778057 + ], + "translation_xyz_m": [ + 0.019406210239914932, + 0.06881287778423942, + 1.0479804729911537 + ], + "reprojection_error_px": 0.3049068030194769 + }, + { + "quaternion_xyzw": [ + -0.0970848585178656, + -0.06038259334163152, + -0.05081850378140816, + 0.9921421028972232 + ], + "translation_xyz_m": [ + 0.019420932377868627, + 0.06879463727498322, + 1.04794854719117 + ], + "reprojection_error_px": 0.3585928916271403 + } + ], + "candidate_diagnostics": { + "solved_candidate_count": 2, + "reprojection_candidate_count": 2, + "independent_tilt_candidate_count": 2, + "maximum_reprojection_error_px": 1.5, + "maximum_independent_tilt_deg": 75.0, + "minimum_reprojection_error_px": 0.3049068030194769, + "minimum_candidate_tilt_deg": 9.324876196298892, + "maximum_candidate_tilt_deg": 13.130053999046243, + "observation_stamp_ns": 1789373018030961996, + "branch_revision": 1, + "branch_status": "frozen_conflict", + "branch_reason": "pose_branch_conflict", + "branch_confirmation_count": 5, + "branch_frozen": true + }, + "branch_corrections_total": 0 + } + } + ] +} diff --git a/src/linkerhand_calibration/test/fixtures/o6_front_border_121857.json b/src/linkerhand_calibration/test/fixtures/o6_front_border_121857.json new file mode 100644 index 0000000..7de054f --- /dev/null +++ b/src/linkerhand_calibration/test/fixtures/o6_front_border_121857.json @@ -0,0 +1,60 @@ +{ + "source": "calibration_output/O6_RIGHT_001/20260915_121857/raw_samples.jsonl", + "source_sha256": "3f89b5010ff5b225a49233bc6020467f8f3445f844348d4fd0db2ddd1d51957c", + "image_topic": "/o6_calibration/front/camera/image_rect", + "crop_origin_xy": [ + 600, + 730 + ], + "purpose": "fixed Tag border occlusion regression, no geometry certification", + "commands": [ + [ + 255.0, + 255.0, + 255.0, + 255.0, + 255.0, + 255.0 + ], + [ + 255.0, + 254.0, + 255.0, + 255.0, + 255.0, + 255.0 + ], + [ + 255.0, + 23.0, + 255.0, + 255.0, + 255.0, + 255.0 + ], + [ + 255.0, + 20.0, + 255.0, + 255.0, + 255.0, + 255.0 + ], + [ + 255.0, + 15.0, + 255.0, + 255.0, + 255.0, + 255.0 + ], + [ + 255.0, + 12.0, + 255.0, + 255.0, + 255.0, + 255.0 + ] + ] +} \ No newline at end of file diff --git a/src/linkerhand_calibration/test/fixtures/o6_front_border_121857.npz b/src/linkerhand_calibration/test/fixtures/o6_front_border_121857.npz new file mode 100644 index 0000000000000000000000000000000000000000..71fe30a833d22fc9b4457be2f22eb055c9e79e16 GIT binary patch literal 216787 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%0DM-xhH!cTw@JNBsxmtPdbFaI)$MCR@o>_hsN0sf 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

% zak+p^50sVy2;b*J7^2^pabo{%fEzTSe6wW8!5_*(BX=S3gfJ!^jyKr)BZ7SMgcWv$ zhPNT*ED0s-K8u-6bvoWI#%6rI!s(A5%0K%T15xnU; z=r3uHglnDz_Oxq(7^yIq^_iQ-*v2bdhcQ~{dUbh)>6=5YReClg$So+8aJWzkhW9?DiylCQ83WG_)8e*yrS*u=bFnJx=b@l3d>^N*+)eAml3A} zE_va{CwlTTh74Zi?x`IHIQgEqcF9)^^^j5;D;fiw`~c}h?Th<^k|zQ#*~Iujq_W^V z6IA(8L(_|a$jc$$ept&zqZrqd!&WzRN#V2!&Y)gq0|_kv96gMkpdJik>u>PJdzOkz znTj4@#!jn;M<<*e7!nu{t8}{UgeuwxBGCg9a*I6ARd8Vw<3qUr`}f~}{~rxFNGjyW F4FFW&)d>Iq literal 0 HcmV?d00001 diff --git a/src/linkerhand_calibration/test/fixtures/o6_image_motion_200044.json.gz b/src/linkerhand_calibration/test/fixtures/o6_image_motion_200044.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..df5a5407c235d0ff51c5a551808d5814b4cf9a12 GIT binary patch literal 140307 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

    >aFGkoDJFyojMvFUW&)A-8 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⪻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%0Al4kny6`mgWY?r3 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

    XdI}_^%Qjgx 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 F1OQ+v_LKks literal 0 HcmV?d00001 diff --git a/src/linkerhand_calibration/test/fixtures/o6_pinky_preparation_160040.json.gz b/src/linkerhand_calibration/test/fixtures/o6_pinky_preparation_160040.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..59f03667a7f188b752da19ec4a9ab1293ce81601 GIT binary patch literal 56080 zcmV)5K*_%!iwFP!000021MI!)k|fD-rTHq9{+$f5`-QJUp+Ndln87ZI#umWY8B*rm z_k5~m9#J65BFT*TvDMYXo^DiUM!30~n;um?dd|Q9@TY(M{HI_4{ON~(_~G-XpMU-Q z%co!d?oXdT{r$gv`uywP{o!AJ{BNKB{PuT${PpjC|Lebg{`=3r{_7uq`MaNg{O3RX z^!dlX{NdL>{@u@?KL6zpzyHI}_P>63`{B>}v7bNv?$7`I$5fj>v-VFveagSYIr^tq ze*UqH8RO66m!IMip%$-g^J(r{A}ksNpG(~r7?Z$JF};~#(d!!JMnW&One^RN2X&wtdN z`Q88gU;p<1>d(S0}8g>zt^9(RATNa_S$Mnu{QhD5Nhl(rZQ6wbG9`2 z<2ifPFXkEBXf4fudi&uopMU)0pa0n&0$=sJfAQ~%v4#*sZCyV)+ME8U)G%@%Ii_BU z78Gthgf{haDU4C{-}G^xKmF^(sdhp=|fjM zLo9a1W7J$*(esY2x1m33v$vw3NIk@G3wL?Ze^`d_a&F12R-yDWv{SQ z?JRw62=lZ=ZB_06k?KgT#xm>Uga7@H|NQ6w_~d{2!=K*!+^ao+rl+YFA=Lclf5@KK zrHzpEtl0~5roaOkor?&QpNl+BHhR-do&9iQN*O_SS)ZYA(#ywv(M?YJHEo2PgLatR zX+4Xmm$Ye{>tP*ml7)B&h>DW{Nafx5jZ z6|GL^VhwdKP=B0D*S^krnNKV1L+?q}bJFecC$D31R&8mWI9(rU+lwXEsx4A_(_Sp+ zS3Wnr@Ig0Edu62m_~8HPfAg6C<YKUlw_IzyvCJ8R4nO^Li{-}a%q7mKgJJz7f9r$)NmudfPk;XO`G43= z`09!LmmmMz@BaLUfBVE8ve!VD`s?REe){}p-8ZgNZ{&@y7_;lDW*%>2q??XmodmT8 z-A}$*C-bf71=kJG{hE4KV`wFno7448E|d|%UP zp=z3OFcv*`6xL@D!4(dVX9LvMM| z3m6B>u?0N?4OthS_1ob$4;nZdt4@)`#?LAJp8ySPW_@&u*)c;=2yJ@w`j?U%GvvU! zI?=o*#|&NfGYiuc&YfdO)&2;&quL*A>J?InJK9g$82aZ14P$6S>hCc688qlai~iCR z;UDn?8uS(momAmkJ^c+DbWJg9oi&Ap8T1ET8%5hs@ypKn0@M&#YI-8(2*5;$EP(Mv96v<%o*(tAv-t*{T<;&KE-l?QGrpp^Fp@EE_TipK7Hu zbe80hLBWm(H=543;t^FyQW0N}Mu)YIgd=9i+9$%iN#J5bh9LZ_qfr195Mtm=pQ0G9 z_|W3{ZP2#RjS~>ifux&VPnbbFORvt*|LJ)b#|(l)T3@fiih8ySGiWh^zk11Qfr3fe z8$AA?GxG^3(lzPLC_3aeWav8Z6}I#tDTK0?n0fkn4j{`JGO#_g3H7sCyZ2j!47_TS z7^pMFA;aJ-_CVhj7|{K!b}H!s6^9I?0OJWa7CsF!6dlcB1Opom85#$O5Fc;g0uA~o z9W91FdUe#G*gJH4f@1OfC~8p5Q|7I=hUo98ffGze2Z&&9)S!DkR-kW=8ic2ThTMv@ zPy?^Lp1XmdXsAIbbN%nML4$BGNt1n6G|-?lI|+i@Z7m-I4SY5Sgh2#>27S09fifSS zPMI4t=n6be#zBK}ah-BH$91Jq*sQ-CG~@st;r6sx9W*o!@7@3et$R#W z${ZCBb@1vv?tTy!fqq>WX<|9tldR)!Wa<+7;OY5h)InYJP6>JFjhoFwYShKF0S;9f zdj<`&>vc>?c?g;8snQU%rS#tE*w#i|l?LL7z%&3s|7liRqTZEMix97ZSje`-u4q|% z=WDcv;}W&sJCH|@yY{x$m=)rP&`O~F=CViyP#povv-BdKYYka3o8(lfsRHGAF>r|N zJtYSmaLV}?a!6Xd;6oe)=IPZ2J*%#0t7Inlj|oTU;V=-VMatV( zTx|rzaW?8|*Al0msWt$$jl`LLSfXBgYGgY7b4|X)s`FlOuT$cRyPl%`F?2HO zMKZ8;Kn_AYUTr8kZ%X+Fp+nP?PAjf@wLzJx!pg8p!UhF~JN0-3x1&RcnsoF050d|s zC&w1PbZ}3H4tjV(=9c!BU2jO9QakN0yUSh}iS4FjQlZuBJVFlPR_rZEJltWIsk^Kfx~|ZZOCEVbm-`O_KgT~5U^`XuyV5}UEYfvDm4owJ^8ne9O9(X8AIZ>=u=)^Zs^2s z9cel=vUZ9?2ZOds#C2wMI|?cBWp=K`u{b=g>c8u6y|a3o(=o2<_|@S|g^}&&aWxbL zR$+2|^jO)r?i8g&rLx*+=e$#AKXu?l_BdNUKnIqm$DApOma(9No}~^)a)dZNcIllI z98tK}P1d_;nXpcjOyJO787cSU61BIelqhv>x$h-vv2=V`cO83;QBm8##>nTA15Zbw ztbpSCOs_=_dTn*1mG!5_BX!`Qupyk)Il`v`dvV|}b(i!4NBUUFeo)&v(7Ip zk`a3$1;m?za-Nj$gc}Vz6gE*%))WDMYmHiD(^m*t>GhAtH3r3lG^n9bD_x6BdhmRN zg4kGXFTG{D8<{gR*C)+MY9qAqh>PDMcF>EW6W&wG=4A(+aJy7ccG)3t`gsIPtL$*$ zixAgO>E#yIjaKk4U+Gln=u*z?rHQzT4%!vo5cnu^hyWj|gkHB-9Pc;b5^X1|FVqDb zgvY6T+U+i0P0-%St5DE3aG2}ib>RacCFN$#3pDlv>84X*D1KQ1g|>)pio&;I`>Mr! z05g=^Ra>xfQ14nAr=p^oEyX}Xzumh`)L3)~-#E0p-e}uOh8nVt(1&J&P(#0Ihw4|m z6?&{mG4%eeXCPcrCO-khww^_OFJfra8U}Ivx89(jryi>E4Whcq zq}p9c>=V1`%Gl}?z@R9WU|kHON}Os9v=cda1#YL~1z~z+{JNWja4z ztf~x1qkumtJf?PDVAc7g*C0|l^aYXyknYe)NiD4vc73K|iB6691TJKba|IaPW1;7l zq6O`xG^t@}`%^P|^FLI2tQ#Z1mMNUOlr_~Pl~M*9?{6(AXKRO{Js-xFEi+@i4=U&1F0dwa`Lvq^fH6)Y1*BQU1k7s zuG0K)s9-fe4>)a?8G1^)x8Zmy(V7#i9q#aRI#A~ZB%xZRl06}EYU zT&QhL_cnWHV$c%OWqMhq2`P-5VqUuV8io|wt#UED=k&(n%EL1-WHhwlC>9FSTkAt; zq1|St26DB2$z(YL4 z1^S0NXLQi(LV7*H1?w8JLb?JZ#ph< z!UYo2(1BL+UL#d;(%EAD^S#EZ?XB>ulf0b)14SNzm|=NXs4qqgy35MCp&~Zwd46Lk zkO1F`{t=4T7xd=qHUlQWjQ;jy_>wd$c+ux6o|Xn*VP-IaZluT43SLlx;sC|tDd`E3 zR(mbR^Nkt7>1%>5!g+_z1OSZ+hmfWI_U+B7#exLIDy&B^1GGcvszyTDODvRFlEt2I zETJ?V@l)qz9d*pGP!Yv9z)DkKD0_)|^?||#P#4C(wD2 zb<+}eV`07Ey;c=fIakP zHfvVU2jSAXa~_}$d;~T0n*b{tb&*7RQ>>>;TUYhOaKpSkkWY)i4NOd3CtYA?VuBll zIP~QFnZ8jv(X-JVs9wM@(9mwg=Si-X$x@VI-E`~d3tHDDz85r%)SDb5??zgde+w@?UwIGs(YSJFC-d`~WSjHum4 z3li1YSyx2OP+{OJ!P8CN>t~NBpg!3m4XK+{uxCruky;)JI z@LpC*9>W7}+#zu}^6RR%pKy`1e#X8^(kVP%EgXgwRPkVrp)RQig3Uttvl zDOSCCwnDwOt=Qy*!d81NX3%5Q8O=aTwVmpJm`N$>Fs8H-Q}qtO2~?(Iz_EMpf;J-< zQ=7OEqD}#|Gxou4)ZaQlJ)$NOm!Yu4Sl5oIgfB(z3QzQWXQFmu)+Yoa@}bVqnEe@r zvmC;R9gu4|CMVMDrB1BXMnL-{$Q zTlfC|e;GE68wcKXcLW=XZl7Rr+pPj!LJBu+9gYf%_6{3#d4|w;;UsF?BqdFob|K(U zZoJ@?1Z5Rq1j}!**rLMkDkv3*u~l%z2@dbBHqd$$64m+ro1eJUX=KT)&eRrOKX5s< z+w>qAit%$sCpl$8;b$9*uwn!7Lg=>gWLF3zv8P)KcI8wiYz=C~nRIZ;nV z>AkhRyG@3WDs$QS42>8Gs74&BIhD1>%sWl>f=5`^B1x~|LK;u#6de_5r~)VeC7K^E z#tn?LG@VQ|@|$-Zh!)!M47KUxGLC^>fu_f%2$OT1>^T2b5Jnm;Cl2s6u%$GIpXP z;H3t|fUIZ6(5d!Vz4Gvs;ZV(<^E1>im_g>Mbm|^ZLngoExUxWMd)vuf9IcIf$4~qTwpK4!c zLgZU)PUr^r)xEaS2_eeGy5AIr2XaDPeFRv8|KJ+7gs|k} zdcwC%B>Jg=CI&77hXt^#&?m#C<>q$Z7piBW0x5O4z7_> z@It#?GpHV^=Z&_SG7@Xh6oG|)gDhR*?&^F1EKK+xA@7QU%|lRzT$~AKS}?Anb8bF> z1>t}p0N=b$WW|t}g$*{kvnm6$hB?=q2tP7me=t?PDkOpmgkP9t3oE=AD$GRfQepdh zV?u|G4xPzB+)z+{9aiuJyx1_gf^=pA+9Rp{WZ6V+w$g zlI$4u6iAJDmI&5-%mNFP8FEP$%}3m5fh-{jE7V@+6SSZ#7YzZSnN&jm+aY5$U#PUh z&ex^RRYoDr3lRUBkIw^KgyQw)GgR+rsBnFW1s8i&PAgO_C}gv>t`Y;(q|Qhq#O5-0 zi9sj2r{%5Fi>Ehsf4hXrXa<@+Itp zwuUY9pyQ^S*0JA+p;R6+2 zuU*^qjdvmj!E#-uom#*7Axn~NV5rT*rPl%mMXd>ukz_`%;yNok1*55OGHmz`zyN{S zB&BM0Dr6T}Zs;+=F^}hpJ#Q~lvo^}>i8uSM(_+@YhKtESBRJ4B7Z`bxd6T?Sdx9}{xkcp;H^>Ri>0L^YRLGI@K zJ}r=(wr9oq#2a!g5UO3!DgBJ#N41}t5`%(zAzq6YfN&Ka10#~@o>3QcGEid3&TiGl z7iv<(R1zRG;k>}4qn#=M!xKaI@Q4^FiZQaOPskoiBl=N=Ir3TZn%1={CK3nfj1wcG z5e(Vu3K9Z9Uq=gt+6+%bD7CsA2;_Nf;0Oqu!526W{d^DX8*Fd-iEKAO7{&1BTbXW7%8# z>MBEUp)fpLuGx@}z=d?nRzP0b3D#@B`_FjNQfMSOm#|`d{){it+|$ zQz!O?aSSf>8{^#@bwr~&C?<}L7uvhw0^gz+>^oct#Bp7F)M>**d2xZEK})0pZ}l9H z8_5Kl0$gKe=}obqF{EAvU8ou_z`z+Bfx-F@#{vVmfT|X|#g8%lTB1FlQU8FZvBGI9 z2=G)RU7o{fXs9S?pQ8Ac&-Dd?8pZUS)8ZFQU4>3mB+bZ7Y<&s-WwG(m&| zC_v_8eW6zCpbmoI!b5BqW_=Xvb()pEz6!xuNNL9Yg!R}=HKA(5K`D7LTrf#Yz|tTd z`eSEz>hwZAfnuto1-%TVAV@XQsjy(GXUF`h5+E(3oKkP(SOj|*YOSleUrh)>2Z=yK zShWR|#tJ3eSh&}kzs35>Sz0?}+sO)@G0jnt%ct+g^2!=c&i^z56w z5N`KTOrLXsEitU}?5!xTKj_=~9uj@8@*|qHjOvH)wQQJpc?-Wm&hhQ?I!xQ)OJ3<*|_0e*T zz9K5xO1avo#Iu5ZH2;kkVd~fUG!5mbITM3_5;_buZ|v*^o`( z0h7Qm%F95(1shOxWDLAN15*f=>K&sNqJ;75xPk4>P)N1DinAUwErhHcq1U@RZh)Gq z6GDsDtG1U2meQSI3REchyu^k&A5!W$E*%E$ueKF3UO8=Gq$t7s3l)iH8cs~3>LUT; zy?mQG3p{h8WuIVmsPcCEN_X9$T7e;RQrg?`l*X)?&Mt63WI4y5MW%;Z4Ww7n<2t zflzDrs z^27x!(1hQG9)Z3@(V?WpS1=7pKnlv&ZL=8cr#gAXF0#BEGQfAt`nKPEjX{rvaW^(U zS@o+)3>;yMI_m$6OYyFlC4f+y;-Kz}PfF-y(OW@cs4eZv0;HJeAwbKk<0$-SvDw-J zIto1rH)Ueffg{z1vYHZl?_ijbZHiUbONJ)R%~si3}3Pe(U0EXYtPhY6dI zUYb@D^$nz+U@0?!VQZu?Q7rC3aXzmRNh#iVrt^J`9p%2s@&?G|zDDX2s1csWD`UiA zLU#fQ&KO?{8nhoHEv61%eXQ2KvzQX&h!Y`90}T`mp^ajhVdCL|zT;rV1?eV1zEeP@ zW`V>*Y=+t_pcdM_-8?<0ZMLP&opt{!v#qn9rL+^Js6oXGMc%J#ckv`5^RakW2;?}A zfstV*X(70l;-p7Y_A&vC*p1_hL`X$bp+~ERm3&;IPIv_Xd^nQpOQJ-MX7pUrqicx? z{|;WR+S;>G32J8w+<39XZ&H%MNXR=kU$YWXNr18Cug1i<%QvlA31zoExt`HgY{(ll zTzv`7G}nf@3qS5~T;>|F)JMcMzBxXI9O^CKzcFPScvP&o+b|e*=#0KSDjuW>!<8xO zFR;VFPAGWuMQMCQ3W}^Qk95%=%Z-y{E&;6g)=*T`8pA_^{$IupkRlRND)v#p0s#fo z_Oi=d%-tQKpP0Qrp$jeglLyg-u^1PEAgL~nX?)}NU{Mdw+co;ZHxB045~J@(_5hA_ zq+Uu+@2)pMX(rqK%}XEttrbs9HJw^5saoCB-r9~Tbf9@dy;CYw?o9ZXm-ZS-Kx0EQ3BS-8Mr0PqHh0lMx7 z^w4yL+Nk>61}DVp)sZPL zA*_GB=AcMJ5?7j;MJ+-LT3U!I!Md6YK2**&y|x)|4O?RxRvH*kVS72Q&~j&pk5EAB zS|VYYu*8A8sw${h7l0GP0OwW(9Ux4UOqC^ni|P!*&vi>P?N&wKk*IDKBy*{FoPnQ*WH!~UZds!c`(%T63I2W zN2Po2Nq>9L3_{kehAjk1TNi=9qBvR4`SrxC0lCeHMZ&8K56Cqo6bzO>6Em(9>9g^^ zmrCM9M1c!IYPnlXP2JSLZGeUH#NoD>go8HkFt-J#qGl83W^$%lEz5%{g@N8NlbmTh z;X@MYYOc0X=Bem_e+Wbxy*ktBkbzShjj@C=r-O!qs0V3bCtE(E}c$vp$$v&2)v>jT!2rRP^L1zY5{h8nzwM2U`>jOU6y}XXw!?2rFu* z3n<)moF-(2enZG1?7`CAqG-kB@|^FX-EwO%XICKI(+ZFNTl&IWZ0PX@!?(gKpZm^c-^4zn~$xRfE*NB77!fU zS|Bcjlq&Rc*HQNhu{t1uZ}|y2s0=CrJIIH^i~AA`x0>exJ>{_MJqs~}*-B<1T;7vh zIb!(-6*f7zDp+=z#}+5nF?<5L=m;qgHP=f5O8F5-!nj=?skzcVis)xQ=}1M6DHOp- zYQ7E+FmUgt^VHoYm0zXM!R5m&Snz;Eb?69JhUd5r)Axo4m~IbKHplhpKuKo0rES>O z^01u^lwkQU8y+O;ve7CbXWQD8c;ocVHgsRVN#|?y?k_Pwo2=AXTkm>-R#S+VSOYdx z(4RKb?+SK$~mP^EZc~Q6kNk9g>w3;GL{Q_hTR--Pk8=JvV)(p zU_!4Co>```koS<~B@w;o3%c14*TS}2RiY0yBQj`C^TPd=hs0n*=a>7B@+ zBrD{7AEl&TqhpxiRB-dF#TQbv>(Z%-eE$v-R_vp#%-l=2M!yxb1PlY?f-4hN6v0S{ zAdheKA$le}nS3ahGQKkxQe|#H4>{R@Q(NJ)DS1cX3`NE>K#1tBW;3IpFLMG27z~lV z6*$CK0l_wEH4Q*V2nmm>j)HX3f#m=QU1ouM3Y%`d{q;pckVM~6;Bqp(phPM>uA>ZZzu(p*Om|{{K4o`u*S7uJ8Y2l>I5rR4IN9ZiJ9?%P(`q; z8%+Xb8-vNGNrm7LLnB>Gnm>3-3^x&hNYatupaG;HxZ3(AF@maSEB%?p23i^j8n9cw zuE4-HNjkopS#UaOW*{(uQER2T<0xn_;)+K>b^0%^KciU{t!^mU3~P%at)l3gyPE zcUdz8o3_xdxlcex57;^8)#bnB6}9Ga7^LC*J?k;y6&40J{0uhm@D@g z027&P5QX^YA;(BL$#Y#7Y{+UW>=CXK^N@yA*6nQb79Ts0K$Z!R3bEQ}bS6u8ejG z9Scp3jTCeaGJvcNezuUvNO*KOoXJK}zeS{wl3~#0Nn}_-8!>N6+v+%&tJ%_sULA*i zcZ1#?hsML2k3JIhA};Kz0%Gk;&bVxjW8CHp9~`F)w_@$lxTP10B0gxbr%P1Doe*0PF1r+L+!;H8$wJ`f5ZIJ4`4Q?;h#0FmgJibN~=0|{nqI_ZyYpF^blY(Sv zS4`|O%Pn85MFSH!OO89q)9^w8*Ih~(LVB^^5ITXq3&M$p7ZN_qmj>h;c2!scZ&m4c zi!adTt%@z-J6r&9b327yb$+~hmNF4^DM3?q+U$fjO^~_KR`8ht<+qGawCDoDEP^eB zFR@M1<`eCY^HlF-Q5o8ouF%}<*VdB~$!6UT(k zlnNmtud#@Z)mH81&uO8gp(~;Hj`v=HtT`eIQ0TLwOn!ohP^e_8RLMim3od9Ip?lIz zy49_~P3e)efZ+7R(A;Q-38f_MNhMD&h6}juBpQ&>D&x!UJ2rD+&KLr%{zKLKZ0s*a zdF|;bhLR!Ev(j&z44-w3k=dgn5z<&A1L!3wZBA4sy!X=?tz|aBNw!Z6O7VaksBHx$ zGxO`TMwA!2^`{^jdeLC;5`tk8O{wXoQ=OrYYEukLLxC;G27?DN`iZ$oMIzX&F4ILz ztjUbagG~WdCeZg1p~*&?TW668W^qV?4NnPXeT#tMfx5RXt>ZNyccma>ROVY^%7}_;hxNRyQM3F851|VF%Cied8h(Q6f+idc#ryZG?exf^5N;T>CilFmp@w!Fh{z=O0HNbR=cVbeZ8nE#tVT!- z@Z2xAZ~U-xzMF}?7d7A+hw|U}?mIQ8b0&Nn^w{F7Cn6M^bi3(%=s;N|hO7`7r|FO& z{6I)mKvbY0RKNl=&@j8n>vr={I`RFmUqN3t>L11}sYI+}(WxWJy+Sq%_JB|WBT%`EFiOH=I7l-8j6km8*>KI z+-ZfJ`iksalYXQ_S$U$SVK;pM;=S6fX^D z??+tIkauV6w#_W#l7q$?Y+e(1K1PWJ&gxn$$Z((LdtfeHt_!Qipyxh4D)#;{$e=vj ztbLqwF->rB_Gpt)cWdi$WB%*%Vt8}B<6c=8ae<=UH54Gc5O1SI3`~6F)bIjxj^%C> zsYhINXHciD^`kTf?SXVTavKdZ&cA*(FcfGcm&G`B*Uh_|ru|Gs&+#s8Eg%r{?LYJ+ zm<72OJNqp*Mwi@g>)peXIP4&R^Ifb7ZF}WYFvXD)eKk}_CE<&spr)9=ph76-ii3qj z@Hh&o-}1499S=)jwhlu%CgNH=mKP`vl94h(>pF+rrU);?Z7`F|`~@t46~9pdZ}Ukz z0xPV_XfPDVV>?1Q|HRe-QNg%o=U`hlXz(^nWlO9~>&8K=+ba&;LJTz!$Rq8%#D>6S zqk7$!RuegOr-xJ6$6*mT8v;{0vXBBeiwjf)Eg**CVl-Zj74$9(1mNqd-5*wULH`O< zLRa2FIgS;WdsPaM!K}tc)b6e@OOZh2#5h&!aqUW$Ej6=G_fU#jo!$XE(M#RgH%%Kv zGMgPFXBDevwIc3gJg6>EwPoxTg@x)fpBC4-p3b=3aIka#VOyio&U(JZNotWl@6fSOq^6!Nd?zH@mIY%gP)=jFc1`|*g z-SQD*!-R3?A4G!*D596LufUN)gj&1ub0zG-reVhA2^lNN`NJS7&o(?*P?wLP1nqbX z-?QIL<~6sqbTSc4E|Mb2ysn{`0|r{PTcX?G4Bi=vSj&}?vO+{osw(XO8nN7o*kj6< z?j~3v>s`FB@7=SK3er%a#`m&9r3Xv76tVmKYO-T6GwR_Cf?ZaCE}jV`aYReBv>Od6 zbeOZtewZ==U2Z`Q=wzozs@sfBa5S}p4HhsHw$3+JWP4Je>I73N%lv(NLSA6VGNaK% z`WKGY5r|mpAQT=T)o`@HgrM0GDz{BG4Qd5KJOho}Y*G?>w==fWdWRvqAJ^D$rQ$r| zX0A2r^+^_HrwtYFHR5Jqe7h3QaA80yG1V*+vVJXIutWr%0vLH=*B0<*GDjq)1y*Sx z@qPqcCW^I^*yJ=c%_2vk?{H&C2{TSk>|*4qY!(qF1BHuVp5`mpkA zgVCTu&`ItIb=kcLEX=-~mc(==I(;y1xPV@$8$R~7dl6-uSA2}UV1kVL>#A<({A3{T zvVU$1F~DEFrYn02H?%m)(zlGO!awy>qb?tehO@L#6gn=u0mBFZTDiN?0cMDFLom_F z;iLS8daL=tYFwD1Ah{668m5d3Gq?bm&qcZm1{H}tjAA^CiUOqaq$R}JjLTT_eGtYN z4Ejwty<4nXpEYgFZsrScy%#kY@vNryn@0_$(4m8yr}wIq7b`3T#fMJx#W!s%t7oJ% z(y-q3m=^4+%^@&$pC`{#e>9Llj28N{7If@JKxM@F(PqP3eE@kj#SA7B2flzSqEhxY zy&ye>BWi%Yz&U`%s}6$GfP@}!Ft3kjhKI@xzFMwRm*P;cpgX zru9zrHDXwF+6^WO3e>|V!c2%2;b1l4MU(?*2#71@!o_Bn^c)y)q{DL%-|~4KwWY-W8q}X3!COi3Zwi z1Xb0$3E~GZLxplOAv|*x=IAht`@A)u7G$`S5SWx4^ufO9tpFJ?9&k^x9x0@Aaw-RH5K}yIs@KPI;*$dfsJPf%io`sF%z_(p%G_)4#gBI^TUnr z{8VEYlu_jEdoA2?n~t(IqRhc>^mu}*T z6E$oWGcgh>g}<;&rpIzN@Vjq z3FkC7W5B{(?pPB9rhE&dZI-it1Ox!xkUp%xSMpJkN!L8hF@j~TkxHall6hb>>tk2U zC}1mKr|vUw=w1f^1MLOv=;I1U#_lr0mdkmqu+DB1ahNEVxXTO-mZC=a#HcWWu@o;9 z!zHc;*E^Au2V3)F9B z)BSQyC$t-|ppBM*?06cfTTscG`I)l@BIZbOLCelx9jL^i0$uZjDqUIoZn&|ZVq&KP z(rty*e%O#k#xYb>V94M^&RkME&QmgbrY+pS_F~3T zoMu>NE+Wice2bW2c()s|m$)zkDgRxu(3KY;zK41j2A(NjExd=ylcU zFL|cc@9Io_p$5vC-sAX~_(Co?Te#qWyFe+}^uAbw8#AC6ehE4oXb@meeNi4aXvo*h z03Su6{vN;`G8h@OUu}@sZ+$;+p8ZDvL%$)DyUG=iEEMEzT-?LHHBU?zGv7@!OloSF z!>jKj`>B@kzR-T`c|Vp+&5|S7yv7YKjGI|6U&SOnoEwt2m(9ex;er7grET9E6N9{* zRG$LqYiBB}dA}k=U?WCp;RWPs%<(`uxc(xV(u^&bwgwcVESl2YZ;enua(2QC7+>gz za5l=`gRpsJgatD5n@a5#UcjFO`6McA=Mn?*Ndu)|1WV#Pxkr!%2AD{AAcs9wNkYJ= zfD(^x`M-gRri@yFl-h$KhQYsL64kJ&IIfZP#7ai6nD*SghN_i$BQ6a`f)tabwLZzD z@w@=QB<{$_uVpc$X{;&=BY1RnM+?fQ8*Lb7I(l0T{sG=HwAE}bl+uT}(lWZC-#wv) z0%0KFNJ$;aW>*6>43j3nRH2o*W)98GJaNcst!KOd4Yvs2;nUGa|2ke^j#rOZU&aC8 zeF=0N=|G__;(k-YoEWJg_@(%6aDhyXp<5nd$tU?SS~b+-7|5MI9Lx%X{sdPnR6MtH zL=x-*B7;fKNO79~x3*@)w=;Z`LlO-CM5E{AH6-z1_PpmMX)C#X0W-TcZe; zfXRF~&{;zg<%THv7M_TL3>fH^>qZ!6K&t?axwPn#_Uw3}FJFp}poYOVi0jT+cVxor zx%vqX8?w1cge5iQiV~H&ak-gwapy)&`zZm2* zO%~)Ky177}l%KIG(~7a<;2dyWWsZa4zdm=bKlpftz*G)Th>vZWe-xk(t;Vl|2Eh}| zJcQ&1gwq8YTs{&fE&pGKGTx)Cu26iixTtx!nMPIx7< zA@#jPQ_(|l?1=`)Ogf8^@9E4I&2MtD66G#)pWdoXm%iV9u5!mMbBJ>3=S(Bi)PSzR zO3GJ{%8d*GV{1MZ5zpLp*WOe!B-oj&F^mC-T=7rQ2MHs46pQngKOg;$k9N6Y2W zD9liAmq258Pbx4&nU|E2vN&yA1S_qzTKAwt=x_-}-Oky1bopj;LrZS4eGD^j%s-Sd zX1!sqlUyNUwI*Ox=!+iYDjCQe*G#BgXh-43h}8BRn~g%$_P#c`qsqG((xrr|^3nz_MHVHOHP{w(Ch4$PF0b*duc+nA;jFW&^(W<53g_0@BH-w9iOH8;*QTcGM zW|Ue`&4wbJDV>bHLV*&-_)tJTLk5JUX}vS#QC~}cWJ(0eAuL3u)ZHzDacI=bz64s;TFtPr5T9Y8*%lF#NT!EoD>VB68xlr_(+Uk#)KKE+cva7Gnyjoy zs5e-dodatvk?NN&QrpP8F{{qNOld&-;r!q;mgNbm2PWHKOoxk&%3&M?CMg9|f& z>h5OLW-)bVNnT}!4gq@Rhk1!sa=oIGYd7KS<-gZfQ{vv=Xdguk;Zk}4+?dmJY}Zg> zy804$#O%rdh=CBWHLf>eP*AvnGl-$za{ViTW(*zMg_E?2^-A3mBibrM!RIgaARfview{t@E?l#eFdJ!e>{g6JhK7^ami+8Z|C4s} zuBp5eE>JcO)MCERR+0!YRzMNIzgC8UDp^601EbZns?*BFDOo23|Eq4DF&{Y(;YgxS zJ?#J-R&<@5sK9`D3Z-RnQUc`w52dA;CUu1-um=y5kvtizej;wew1g=d!+b>h?u*Cc8SY@1@;juV7bz;UqTPo)e5Y94%HIe z+v@7OV4~U0AkzWXAAhG{(XT`Ys(f4R>$cLM@akBSz>+ zuRS^Qo3+@x6=+yrp2tXvH5Vjn1)`iil2(%sr9V$@()ZjU?RuN)o%Q_Wr_E7OFPf&y z`E{^RnBt$1#F&jK4i>;@sB8&@_vu}Ugb1|VSb7asM)mb2q6q{PX2Q7c zC7P5pTMs?dy+XtwVcF(0Lr0s&luTjx;4$)Tnn!GJWP;tIFtkgm=VCCp$}wDy#bD5; zThspYl3_??htkbp*piC7A<-aOMVplv3oGCa>NSXuO;)B!%a+cYL2BK`Ja2Hw>)ikX zsL(iT7ZtLadsMv}Sh#zX*)q|tbbJIPF^q2_cb`QyK4fh8mPq$b1M_>YqjqS4Y zD*Ae%60*o8k~uClCVb(Xl~|08ai67Fy_GJEbEk0_PKeAobxxeEOd$+(GfU#(eGJ`} zI=>L*e0OnSV$6|nhWpKh)_J zgQsppCQ+mw&2b(VS}-4j%%E1Z^)YfcdFSxms@V ze`N~O3l%IcWc*c88GOQE(C#D2uGl5&eRwTWu!g7(ENBDEgA$4Tnh?f7W6*~ecxM75 z8cN~yOjwsTY-6@1(2GweschX*HoW$g1tg!4(5sEi7aYzSx5-pN?T8M;qyzfIxSW1y zpjL~I*Vlo9poup?U~3v8Avg?LV&pc-3tXU{fF1y-(>G?~edu2ux*ud5tzx0#xk4AR&J z65aKJ`-2PvX0%)T$;t}zDq37K63U9Vr9;_;h0EROV{FGXAb-bQymzpG_0%O(coi1H zvW9Req;#{%=_R?mof+UgM%=PiDyw<5BS++2bQx=SeW>J?t{%^yPy0={?o zC0#H;wb@O zw@iZqX;urTP|8fZhX77MvA==2BOMS7P%XFW@C7`c^6nrZ$V=4Y5@X;$bhnR@xll_Yz`!b8!*o@_5wACX^KU96|8bC%o2>L#@XHGvAhte z{lG3B2vjUg6qq^BAeM$G!&+nKl`-e0C$VENaGDXAWo*2?n7@DyLgKlyN0Yx6+Ha#$ z3Bt^}wd(DCl-kkKs|;MS3cWW%iVXTz^o~#62t>a?2>LeKIxDD}Arwa_V&ijLNlf!@ zf^I#Y$vnbpBd35#TfdGQ_|B0yv9KhKuK7%t4T~d8tc{(jAQS2MB+RFSDLgSKH}#aL zNFfB;j!UFOlg)9x`+$s*7feLZqW;!-3^oOjD9${gJ$79FP*iO;+5An?9u2X3_kLFx z)@3i5Ag4X{vAGCk4_1b-eGD$+slwZx?*7yhoIU?a>CvU(*C^lu4tutueiSzdf?Ntt zO}K$5UN8HmxN!r$IFH7~aYLkxv?fCwHzbI-SJWnGGg1OJE!P0a2)ou<7UOr=Do^kvJIoQ2enE4)PRR_8B`i_Xr^4bwIoc& zscJRY4=!c!Cgh;~z;V41$NR7Yah1s^WKn+eECwbHIRmSgZeNZa^zPuH)*FPag&n$C zXitJI18D?Swys%FR7@qUZV!>GlpzpG-~ZT{FcB5kTOkrzU{&s~=WQ~B1?s>Gtlod& z1b^mgoe?e^Iuo#%2;&4ExM*ew4@s zsU8=}z+gfM34!ar7Fn4cf^bRsA?`(T&J6gB@hs(pSryKe2zlv;UpzE{D7>y1@u`l^ zl(#IHR)WoHA`Ct?(FGh3gw|id%kRmU7{kPmtdw+$t!xw>6#{S|PK&Kj`(7a>YTa#J!FZzLg=Rs+kR!rW|0ZP_cyoP( zVUJi|-SNw|(%N`^$7COxc#tA?EvK07L`r8rK3F^&lVLvc^jye^gU5U7AM8#bm^?0S zU@_oe!!b*NdTvz)VVkRuVg}Z_s26~hb*!O7UD3ny28@W5 zJ(;=AG|FCe9bLvHOxMOBgD%U+jJUqE6J%hHv74o~j)UYr(?OpGSKkDc1rS%gA+$D` zThHh&KcPNmL{UN<%+};ny@9qjrrP?lGChYj6jpI~2y9;)n%73>i?L(WBCF$~SP{ck z9050!<02tCuReI6_tth7Sn(D<9g>clr7%jr%>u=)3Z-Duj z`z3+An2-IU3bsqb={^;?vBE{YA9uKLi2=0TqS%I3cf^3KxcfPjZE+pq4#OWr4DH4k z!yfI(LJXbe1FULqG;G9>rWG)Kc2axehI#Des<044qC}UrTPNYVJ_=3&7{ZM@=1%_U zP*hV4?H?bnoHyk0$5Om%`hHX*t~!cy6<{I6C7a2dhoPT_Hu0YQFucHTW@z6Fdguj5 zFneod*nEhB{SCg>STfrBsZwv|3b#zq+q*FX9y1k&^>3chfFyu_{$ZIi! zo*68el*|cox{i~+g`+2#JXVMvQlUh3)V4D2KTA&k(RDXC3`5otwE1i^-WT%pN?Lv4jAvleLVg$VN21@^r{VHg-K@EgytLHQ{v z*T(kHJNHd88?Kqe&4Cm^*fsFG7gcW$)`u_~QZ`YSJh#}Bksy1oTddGoko2$aGLqJz zsP|$2jT_e2Sx@9cxPdr_dstYOli-HC9@8B(T;dJQKtr&TX-gae8g%XHej~{7J4XSP z)XQ%gZ37|tvzs;BA#@TF?S60tYY;x2`mY z-kq@Nbq1J_cV(y{+z`I7&@kB0Zo?+({1>NS1LD(5G17)l-w7L{*-X^(n^$uZE<>px z)7j2~-TB3^p-^G&vXGz;LLM-8_VJzF<}@G7SE*yV&F{GgaX!-Wg@0yy=gOy zw#ESiZkkiI_qPY0mnf;20>rV;?dg z4DBhW&+WCifwQ^b?u{n2UQUM%+>A-IOJ<@}y>!nY81n(l8W>-&+NJeuN{qc6`~F&i zIX98LPaiBm?MUZ7M`-g=1&R&Gp%a8*5ODrI78g)^9cgXj zPmK&O39qd%HWMcU-3Ht6IsmYq7zt+r$mGWr>SF++EY;}~W__-kgjgBYS7k3TQIOAQ zpgeIH(IJae7E{cO_I!&LmXN5IMv>!&duRI&8ydNR88hVVdTouqOL5p?Lw&G?TEi2O z{$E_a2o4)A?Zqt`-L3>ZeiSue1JrwPhNq#1s5fW}RNajx7Rk?vf5F&-rPaJhj1C+i z<8IccXZw#-00$2LK|Uem+rCr1eh@X(O9+BbZp=5pYkNcukum1W=DV7*v{84K!jzFT z#uVf+GwQH7lLA4ET(>W14FmXeSGmeVKf}?!3P|q;4Hy*R4fmU`HR$TG&ecOCH3~0o zP}G6ZbcQL;-wQM}#Kd}o=U~B;WEVF>b`qjHh8ct);MG?i)XAF4e1XxuH8Wt3rU89w zy4MK#r8S3W?prjGg`v-dhJiP=cHSIJQ5|I&lJO<*tqg?UH9S!OCHleuNVWNW% zp){;j;-YKPxnfo*^TKMU_2UW=u8!8Dr9Zyb$Yd(FY}hHf)<~Nt;blI{nazMm@*u1X zT*=Be$%WMR-gWE6W^eNuW<+e6Oe)H%A3E?p)|CczO3#M%uQ)Pcl7S=NSGC%{gPvtwO5p(Z7(Xp zkDhNp^j)|xrpIgw3?w*hKfB;#!&`sxgOrAgjA59e+~U~%k>ILRl|BkJY4_DiCN7;G z?x%icCJeLk_RDUj?a09Ch6C20Xt*NW2W_LdT;Tw_M>XerF@p`PA*lHsGmr*WsQneO zvzJ4L05rozAwp2(9#r$30)N|yV&^CXF}M|aZ>cR<_10gHM6 zS-sznia!p}=rs4Z)Q$oZ2o+K4J;kLRo7}Oejo@kW$-A>_&}D^NI8)_g{a* zM8yO9wWtALkV}Esp`Ew}C}OS1I0c-U@WC+9V1qKceFzsB0X89Hr|k&M1AFUJdxG$=ecv9DwnDJ|tifkb#o zK!|>szSEXy0VlHID9E$nQg%Y39o^*AkUU@xE#wyMMf|(5!yV zZ~VBmb*C!vg#w;#);Eb6w&q{mCKGjr9r!Op%WC~6XF6=}>+P0&#|$lPzQW~yy|@Z< zUjEnAO2@9=FU-&{bBGVJ8W?9l+0%KD)j$KZylwBj8x-0NE=_T{ zAwOmdqRya8eb9LFIzzn$E3odHQ;}nSR8IIH4^nx`)>zWgZt+3lsV&f;6SQ7)o?1-C zyz-Kbw#CeHV;p~beiYMp9867b4yVBeJsmQ}OLd!x6g|_sP3gN~1EY5X4D*|xy@aoZ zD9EJO|6*AuSUA%-@OWCZ&1yh<0P-v{;m+l%$wDBZW2}@4A0y#i=njcBoif%LC`%I-{wecFj+C^N-&{Ztbr0)Fg2gf zf44QF#C$Kq*tl(Th72jn{lKSdxM%-a2+LVQX6Rq~(UO@Xv_&jA%#DLwGX*5pE00%)Cga zu*xZhJ(u3UY{LoxCS*59fQnP_3agMpQ09cbc%GdYGy=2cxejEfXSOFQO|OBr-D;9wbC*}9y10;L zA(Ot)c4CxvWzTU&`~>-5`#j~k8+8W2n$-m#d9vyExPwy(=* z0BPPB!feZg8dgKN6>Mj>g|~r*Vm>F$C7N{_*Ewz&i*Gf|`9aX|;15;a8hsTk$+x$K z!3GE>dnkflPoYk`w6hE`v>U?f_43{9ZQSn8Tg5<<0<1Z9Zhq@D&kEe~(l!ozH(~&g zBg6gO&ay_cfkarBLiuYU10pL?SzBUea)w=LPthI z%Iac$GntqSV~oLqSjDA#;!w;dx)3BoB;6!aOyu#nKvb-wc~l^Nv=$i5*^1qa9`c3@ zfNaQllsC?Bfg*P{PXxuD*D5t2?1a$=1X%LqH9Vo!cC@gR%H`pL6?z*<65Dz zG$xBOHne^06{3M+MXa+FWM+eau`}x2-(+v%(fxVpJDKx{U(BWNbT5i8Hp5+^`O*md6Gx01P(fE+B)%_sQ2#IaTGY zi1s0U6&N5@>fEMl)m)RSe@nmQMZxM=I@NOgiu3rTUky&BJLL9Cb zSQs4)>_S}rZt7gpQzASvD&~Thlj&c~PY1?uMhTDED!jjYq%3YM-B7(*Y7`4Cplo|t zcMC0a-5f@57ql|8pmZM2?XuNsLJRFSO>exfiG`2JN4E#L zzcpcl2c0P=Jk0s!eHLEGw@VUp(IRHUjO*ezAkToIH)qq_b1&}&4B7{D^uBi`biKWr z+ze!s7Xt?7{yOvOj6lr30Rtn4dhv#~fOQMVmUWPpl&7gbS~!J(#)jYqo|J^sM1Zso z_6~BrK5U6kuqlr-v)-55Q9%C?o6G9v4hO`LNP-0x64qKd9T0;yDYK(#D2haD&@-(^ zwSyM4UesU;(vTyJ!4n}YJ18R7mwkF78)P0pL$ZcFumSnQ_FC9LCkhP?!6LnO!}q@e9{GRb;#zY>r-~04ZXu{G*dJvi zv8YdAW*gfvsG5lcR@*vWhW8|NkkHP8;6OoV7wrjn4cX;!4y%NIWUi6SRfs$EB*JXy zh0@Ja)}I_U==`%Hut^{o$rE#ctm`!4QhRALtVuGchD5+g%ZosO__Y9cLRq`56%O8Z zWNKmyya?l6ljRdf?abk-k1PA&C8H#n5<;F zEGOO%8Tco)$M29K7`ZqX6BKSQCP3m3(or@7Jj~sdz)#OOhzRrto3v!OAhK%(2VLxk z0Xlmz6XF{=o+9di>t0|x_UxPms}dl~4n3Er-FKZKY7;Sw*2;60g)b4I38dWVu?ELQjaD}Bc_?O{RtOglng)SyriB@}G8lyg z&7i(3Df)jD;3xR$3>lP;*@PvMa=pB`&=45^PAG>XyRa$^IM_~8gAlxOn@>U3SslW& zN37Bi>8o;xb(=ppl^XQu(GAaf+svt6o50N0l5-fAo?UVHk=ylHYM4e+ai_{rH zgwC!*v4Qum>YQR=(Pw5rNGzK+Hda)&MkJfv5iC0!mJSKN6Id=M{g-}RBTNmZBrF82 zC1#6j0Zbe>2QpziqSx7GDbBE=q87!w(Ws4llgdumeDCqW1sfWLz`2EzodkI|NWNeL zRMm%BOC~F`shY++ouNFG*DqVkcqv3KBW!)8iKdy%^M`Q5xFPkD*Ti5k++c~p(pqW@ zH?(YH-o8k~4I$sPpUc7y4n1*WnAveW%kW6w=U&QOWodJY7 zA`W1NKviFBL||C|&vYWfE~~H&hfzS&lRgDXiD>4Uc|m$D4bq0);RYzEqf+kp!5Cx= z3`}}EK0!eq0cW%@1!K2QXC$6XP+oMvo!$r-l=!WgU>C}79$Pcc9`1(LF7=0z}Mi4{wW}I_Zc(b&;S#`2JqY`V`fArP|>|;8dKP)J&04eCzS$A%!^gI zfx~}s?=Ova0|zf+=kT4{KywR12}9%Q#q$$9cg21j4izTWJ<#`V zcy7W4g2iyq>7aGMh5-k9)Fx42$~C8$TH&4%PAB7u9#a*esHm8|_1sl_O4SOb> zq04~4KWJ26KTfa#WKKGZVs+lIL5ELLrozcljjbUROmilhbCUk)rIpAus+ms=ZRX<| zQPkEAMOMRQC0QiXQr!dxTMv7cS~WV@LT2&V=w*ydi*eI&27PVi0sAnt8XBYEyHOR{ z5%@C{8>{rBoT{?&>ZCj7P93^@;ht(W$5`j7l+WZsvgU8F89O_xK&cptG2uE_A>TwC z^xDHKJi~?_Y;2WuhuiXX%S(y?z)(;s0=8A;&Fi{po(W7b%nB4NR(nqy(qJq*9u{epk0=ppkl;)soomX?;G{T9{HZ5FK=v$ zNo9*NE$6DM+rPah^yc?3RG{?euEPCXBNT{ic=MuMjL156FJaXj=FXQth8nPpBA;>r zB%y|U)1lILX<_#%%msDR85A4SZ9s=|)n-O}4K*lGUQ>zG8`6!IRez9j7DQ#jacl4ozSl^-0fNKb zRLo&Rr}cu#tOE`QRB(}(7R9?|A*mGVwaecL4&t4NO=Tyc&f8W?Hbp zW{8+1OmqbeHVhQ%5Iu)vZ({<`3E?ziub@4aPa#zwPeWV3fpPK*si-dw9iwOmT1!Z zGEd)eY{+D@&B^qmoq?y14)&`Uk2T@4?LUD?FmiZLOtMiY#&iUt1e1`jtu;W=(yKrO z8)NHqkWgU0)$NW&8wr>Qc?Dc5q7{}1sfEc3MoT&_H7Du^jUT)_DiWO8+guz33RHlT zO6_GCWhbV_2o6pEavq~-*59p6yHvEbz788&h1EGxaqh#Iw>th>4(1DzOoqeOh<$`L z&XSN{w>6@v9dcvBqUK|-qGG2oF>6^fsjLvC{YvkvEeSIg2=Zrl)h85=Y1Zv4$c6qb zib7J&8J8@RiAmq54$37aePz_kuu8k|9{Vp<$a$A1UaF9LqSD0$3^nLX^J7;>K?6l(=l?>SrWEwmgzmB zda0wUH^1xYtryddY}@E`7;2ceM}O~PY>Dk9?kX1VMGfd=LZABHrJ!Dk+7DYq2j7cP z1EvE5d0o^R>+Vt%kOP_xZ2IW1fnyp6Ohy@PToE5Dy@t#xj;{Ci6jLL7m4F5$P-8{a zgisHafQeO9+agaH<{)1apSmTRz#|))BJ;UTp^v3N84#~_*kGfU(`}UP&yWIAENcL@ z*_u~WAW`6|GR#ltxJK-cD;*#9ab0UHplux%$ZV#z7pW{5E&*PO@kyVe3?jhrt9`A~ zQ^00#nn)K8bY)=&bPXUZ>g>>|ZKJXRaoYNND6BD9@W)JDZxAMY42m4<4T`<3y97t7 zE@!~bVbE4-R##vjJFs%1&^_wsblimFpdmz9q7DBokw*g$_xQgH4n#*CMP}<*ZDbK?l*_xqphLYq_--_#1Kib| zyhlMbBbK{(wGz79!*Iigtg9MhoJ-(QaoZQt+afzG)PSTVX1(5CQKTR3M(uXfT@4So zGuQp9%#(@%t}RR*7iz#7aO_%!P(u<3A`-h?#Jf>LqqBnB?03Ni2C?vsrkbo@d?{+c z)O{ASXCMe{%)oQcIP^iyBAj-ZU&_vSJXV86>HxQq)Qkml2&S|^z@FfZw!|9h#3T?i zi-x`OZu!I2L(dIS;U{@Zwx)#_n$az$Nj4Z;4&_GF0-U9fm`SVG0E|V8**CJYy2CbO znm|O1(hRag0}MSG%?0UV{_VU(q?}0e@XbkUi9E5COr>Wo=EgD%&X@t&x^dG`qj-MAiE z>q{8wwQynBY1goA8q1~UgH9i)726aXVZwutUCVn!BY2Wz4~!7Q1lOka(sZa%EM zFevA09#2QKQ9GN7(xbi?8BAN6jds?}ix!g$;=_YU!UJf)bSdce!GG*-Ts_U=k%*N| zZNV1FlsLs;6P$KfRHzwXo1y2IPeq17O%7o)nj69ED8}S~P={d2+>#B$G`fW^blc8~ zkOiY9sQrwVwEj9Oq{SqN|7Oy-%}agAjeBo17S`Hlihx_ERiKA$qnD^+<44NR)RVln zP1}MDN;T&w52i+~1Fkh}!YxIG4arGx+tCu>#0u7g2(`rtX z{%E#sh@s8!Fp}Yjq4@h4K3Zc)M7}Gb9)=jo<8xTRLFdifxFVa-fp*U}>7I@pG8~P2 z*FwmlfTjdz?==$~Bg^9#2s+R&D1Fb4d7sdso~wg0ahFz^_|qf+10HB z0R8O6b%tuz2(4x}arZ(4rpA%UFG%xNw?~6faKE7arTpGv>L6}Yb)r{TkGP>>lt>Sm zj``AA)&Yx>5qLXB=bu@z0es0wXm}!@NA;!BByM_E+Nh^ugJKlvNq`rU(&9Ampz(Il zt7cp^1@~r3*CW{BZny!j#6&4P);^E520E?H>{)eJ;#wlUI}sIO`aK&Op%l+PLu>K) z=y_wR(LgGbnB}#&!A2Mu5v1+)=Ay{hKgNlFP&E1iEf-X(pvHDv6#_}Ra?~{xc$Li^ z18M`<=`%Qle0stNE13IXWLCTTPtY$_+>UtjJW&ugxid_>gH=HC2{uGUQGx=7QlmLz z2OVd9&t!B2Sg^1W;*PW83+Jj0GxTIW7n}t3$b_8W(aJaw?c26i84GHzn2EYCIEysH zjm9xrp43u|_zKUQQMzxD2>FH4wPOZe{`^;*hGO_`mk_MZ(E1)#C>N*UL49Er8uGjg z1ufW69%}9jHlQ4yzgo}#Pr(K=?_ESkFEj}4Vfb!WRTgY0@Q2nPO@j@TlGX&}1sk%? zsqHu+)L>>^`B7!N&znN(E}NhwJsRVyNP{hxh!4O9P>Mp|@Y;2UJC(b%pah3MaK;7R#CNWJ}ldW848vgdoaT3k{E1!;n~?6?nXKeTs`~UbC=y3 zjmY@_)*%Bl-06mI)b3tfYaq856Kw zq4U)us=}?M%P2~pD6rM$CQP-ODD9A!e7QY(YoA1_DzwT?aonN~As^8oZGd)MC2nUJq?;7? z!VUa|HWpP2h2gbKZ4-`qc$iN(8+bo5kkf74CtlY-Oz#Zq5}7K9}4mqkSwG1WZ|#H4jpaah(GjTzuqw%e2;cax)3y0*#GsL-K_o~|YyruO z;f!@Mmv&H=MjvMI66#1amqx=5Om0^5FcL^L#sf5LoET_{_ho}QPzOMT4(Jxc5g`$2 zhN(~xMOLc}D)4MrgEG#2Z+9$yj zLrrbm;ws!N+^oz^&WNyjcN*!G7FXM;MTOHyWm+Y8W!762=zlaMzXok;D`Oz{nGk?r zy*h%AQMKr(<;ipXv;w{k9*S8cqARX#j)2@^bz}bOju^b>*PNb!BIlA?6tW@0M8RvAqv3+t^XhTs@$f#NZ!hnVwCVuu`8e}Ni+ zu^W2Yl!Uv1)ms8`|^x58BM z>4X0=cu3~=m8KOnX4J+ozmUQdy_Ida&=zhAc6B#r8{ z_Vz6?jJ$?g(gF|4@)T0GtOf?%CJnSFJl~5SM#C&tZ_W2^g&8`d7o)4GFNO~~EOSJY zi+)3h8$Mty$1X&6i!99O+_riK)4V6nOePsNtqAVSA+G9-w~ntRtV_57kKsxBY7FSI z!2`wZ#6Fzy3CRJG!$1*T2(})<14=$auc6*KaPym4A;Iq)=9_>FkF626wGOn#QQ3%* zbYK}DOiU@B92l4pDY&m8@Hf4-fmD;_@pxG2aNaYfIxvmMQJ>%=#kOqb9fNBO?WMrM zup68Lj^*HL7Kn?R#t=3i-dz}g3!p+__`w=I1t}!FOpvriC-W3knZ;;k9uoWJw@f&; zsL22pn%5m7^lhCjgNJc8ra)G5(tHx^Ly!9FVv!IiM@1_wB%4R^H#}kZfhEA~j)mxu z6%4Y8s;;?Fvbl&6fiufIt}#*_Bsm@qf>|Pz+DTd%p%Cpg>M1g&1FhysOGa^#1RSlz zFw?i$uDEjMvf34O&+ny+;&8bcxe_()ii_nx=hd!QQ<@t!Y-`MJ*4s{TX!@U4N#CSM zb6?qQ`wyXpPQzX1Y)Q^A$E*-zsZ5+}I^8>it*Bxz01N;gp(ygFt;KeWiI0rZkE2Qxwi33h0LS9HW!06bE6r7E?Zy2cE%LJftIOO~UF+gk4PHd)m z@lCo}F;qNP^1Mn7`6_2$hyk_Oi=XGD$otg9MQ{L^uGzpnNO8?8UV{`+m)Fc@*|bQv zUa-ph2QkB)ZQv7&+E>X4RBKMdF#|DoET^q%m?6=*Tuxh7{*M!9r?G{jDK#*%$l$K? zI=s|??8UO10z3&d7zqIjAn#m!1T|2PMdBMc9eE)PM{tDVm<_Fz#Am%-k|{6mnc#8ciPN+{gdd-n;Hd5+vDq zFI1cG`{ii}L0W(mD2gEX{*Zmg+#|AQw>)b%^N(tpAeXZ}-Bsz49&X3Y&INV*QJ~lx z{}=vxhBUCMYJ*2i3_?MAa2Y7_W(|OY^U+SoAyIA^ltDH=xjhxiV<%n)(+S57cCp5| z7sHk5)gqJ4)u-g9!-I*CSYu7VNi@fCi6n{>x!m9b_%Md3zoFNi-bTu6g<^zaR*+xV zJ=vqR+eL@C$CdSyxPcaU=87ED{w=;yaeKmZNe&&W*APkkW-ftSY4N@jc?~ct5jXa8 zuK_}V{X5*UT(w~bX@Y(b+15MhDKZ$N8$|=+Jp0FpkwhZeFQh|#AA(6tw_S8#o)m{| zL8*bDWILnmE?8tFZC^5j0Mc3P*q+{Y8ik1swi155IxG>hF?*Fmx9y$w60>A+7)G=) z_8OUd=v2=yS`GFVaIhBURHOY$i%66vbAL_;4pHo?=g{n@80{UJy?u&rdINWW5}xH0 zx&o|%wjCQdxwkv=PP3Y0PV8Nj(yA)Mb3AXHrDRqz`E|L)RHzQb<_^Pzm+z9Y@;g7)ahv{z?lcN-)~ zTeOF%v4Z`tr9FWqd(R*llN+~GosdWi>i#uO+583s-u44I6 z^&3Xj=o>kpw>yfEnZ1U{!3R*Is1x7lBR8!R0UXk6gSun4OD2jKQC_qWKcNO+K#gmA})sxQp^zZ5QZ5fd$B#! zhgA1X@n7efYA5nJpb_YD5z%-XRVU?R$qv7#IkO zQwIZd?*wFYw&LkxqPDQnz+{jNy}+xMoa=(?22t;5ijEYHgb@y-K0_l58g)gkb`A{; zF7jTZT`PM-tTE3{W-FG*j!`07#&2vm*i*=HH5_(;Hnv^fWWSuA>;T$@c1Kd6_Yp(z zinD@Yz4{Gt@glDVgz_NVmNL|x^|XM4y^G6y@jci;BOynZ61cCTPLpN_Mw_jJ|#lS_$Fww5?2Q8X;fR*-ihgtRcnCZ6Jdk7M;ZbDR?~aq$Tg^v!iWO@MG$6mXGcqm#M}e5R zGDcQ2=qI4TP6j)=?fv@OY>CAnvl*1WF+4uv+j1Id$s_hBaDET{8FUkBjf=UvO-pM)^BV8K@FFMB;CRIe zhvB^45Dzh!*)tAt8DR$}#|#Og62ORPYheaEXABpSQAk71``5y+AdQ5V;c<;HK1fDJ z#$VG~V`ch3fxyMyrQJ^<6N{F1@tgVqHQ-BS1%Rdw@cg5&f%1#@lr7P)ld2u_oT02G z0w=~Y>6o0swTlU{ygYaj60z9!zT|VWp&<-QvRGo3L$%eq*Yz@)op0)5wFI-p%5a!E{?=I z+1a5{vF4o~hHW*3=GazZsW3x&aPD7HS6Nv?UCnFGBqc?{-qJqyEyE0nLCx)awaJhH z$J~n}k#WgE8tk9jZ?Y%PiQTqB5x%_y9_j3B5HTGX z%CnGY@BkX3R8A9zA~YHf8VZCz`p;;7RSJ`2g*CXft&`+XBI5u+Qukqjjk-$c3jFuh z7G!`Ag-~CXSb3R5Vit_8v|ZoFIXk=2p6*RIiqt;}7~mNzLkcm8)lLBh`)mk4*b~_F zx(e0qE?ouDo`7R3j4jY#7b@F}K7*Z%3B%?xoG5`y7dA}{Co)IEx`Xq~>{5s}^9-Y) zvLwZ$+MTf*wD}e>*t@Q#H5|l**3MHYA<5Bu0D5G1^^dLFSNgH9c8R~-!;+}nOLiwBz=VUFCf$&0k4B4%?J++nf z-XF|pwixoQK0|rT%S8bLN~;1})af4*ykG}@UG_{x{6(ngJLK9K4fC;m9+xR*P#lq) zR_zW$&etYz)oMlBwhSWbkFf#~gJkagWiw3V3&>)bYpgJ8j<9+68js(5S?yqM~ejsGirnCWSdJa$Yz zp{c&$2og_{)$zASJ?i&N$c(VkP9ISVaQt?-fXbGsGOnTBjl(0nMd)Ohe>ab6qtilI zWdYQ@T4ExMr;!qx_Znvv5H1Xc+g@XXzY{Tgda=f0|DN55vnqM3zfyB)zvS{$T#nM? zk__Hi*}IWn=I$`$`W(sN85Eay@yeih9#!nYgu-vVli#=Ia`{a5n%>0>;r4!@FJR?T z9Px15Gu8ZH%D5*CO^^Y#P>$|fSr%l#V+POkZY; zoktU;iR?rju-qXc2J0p zl@L!a!+ud>N|0WxvJ<0*_gL$FFET=8q1RxC#$F?5EB^Zx4p!$kkIFW2CA8uziT*w9(LGn+F?}-HIQ*wx9OIl2B@WY;V+wPp$5`8 zZ|Bb_635+asmmd|uMthzvc6It9&5W^;FsE$tZE3q3(^9&aP$%HhI9qS(8f~J7>RR=pG=L)+VG6|@n2Q$_1MNZ9~u>gjj z=&nCgu7TK4)NzCh_OI|t{^4LwqOyU z^iVPcuLAjv%ST*T0cK33w1!s^(pZ(YETJq&;2@mfyQ8CIXSNz8dLuFPDnL4@zxtvE zZqQ^{7o<3vpTKEk-ex<-9!ol|5mE?t*jBMT%ag+v|6;OI{a(D681!9c`}?mv8qlo~ zTSQdDjK7k}U>j@2RC^3Xms_gJ&E+V~lyG@P?mojUlejDz?zrYHxMZw$kIOZeFuZOz z+3qBNCo(*<2;Kk-G~Ejx@t6A{V8O0YJNII@86e88v?}i9V<<1++2?ZLqQtax8DG(6 zD8$(1=a?%ybCEIHWvfgd|67H6NS(H)Bma58Fy>S;%VmZkFw)1}E!fkE2LCy727(A4kTmj-q`i`L(Pyubr&+1fn#0|W4!1ffp$3S!yc>)A1A8nl=vB0q-lEz+b zKUR}1iu)Hr_U?B&7!NSoC|>0}oG6?ejaJs+0`fg%5UZ|8G(ld4Q|B3c0ShVxEOlQX z<3fYejY?lUHaWs~$clx|=`?_0COMi&`i6p5;fPZnlw6#r`yqDX3G7|u`Vd~?MFUze z=z7t^`zU0vcY#Y-#XLI^>NH{ekw}Tk($ISQ*MHG<9L=fSZkSIDM5mO~yadiP0~mI7uGsieqXmoW83Gy%l3?5$^SAMxKL!n*i1#aBYT6GE4^GG8kJNdZbQvNJ zCCbi*rip4IUmOP?O_T>j;vAL_^_P7EkNix;SSi^^gKjJCP`NM9?dtX8ZUgnm?mM&o zb(7{7^CeQBd;BP9U@Sntw&RmzA90&I;i-?#Ql7z{SVRfsjes{dR=0u4uLlqZ=emDe zRR{72oRbXy#JJk_L21XC20lU53pa3l1Wu2V`U~X1{9U1p1SuWNolrIUigLYifIhAg-do3CdqNgjtE_TNRD$ zzvWscv5oLT+N%I7g8A&dvS(Du!uaTL!mhDYMobtqc`551V4=3>NQcBSK@SYZV8fmR z*<#o8BAplGszK+WALkQ9{o3Ea&j7WY7gt^hdnPC}YVj9iqR7Mt&VFin(Qz0U591$< z(P#dCI)R@9yl<*f*h%T?1t8|eFLJtDSt05i8xp!=@^q;7DS}+>@gN)Ryut>@7V=N; z%SsAXsi>HVwfl5}HKs=XdSj~`*NA`+KnGZHkcxCY zKzT#ZyVx?;AHxUn?ZK9RIgy?5oV4*6?R^$LOyuKM76t{OjfEa?4WzW#XV%CG_)rqo zyvhLTp{>L~Uf04_u^a43cFDM<$Zv0`mG5Rd(_VhcG#J22NZ*Ck4m^QgpMV_jgH?op zWFK~8IE5!;5XjqVWfEvR96n^_Sm7jDK zHB4p!2fHS8GDvI%RZS=usrHx}Y#Yq4PG!|j2fiSTtCw8@zLfSov-hn8#O{0#ys#pP zk@Ajw(a6W#V`e+tz+m%W5Q&$RZLTD*40q|EAAmo$#t=a)Pdk1gcf*Q=(NnZUZvCXq z(iS@rVyoPs7?~s2UuF%;b1%eR7z8uq+4malxiiN$5OqdJRsrpZLh;(*3I58)LoV}* zbX>@RKGwEGUdZ98eynK)={(+}cL}0cktEiG0sYFqyhNQnzlR+v zAzXgDmiM(4j|VK@3-_qtV{z~tM?I`ckF^-hDlk|YG8z>z!!ZaE>{o=;ntM=(_qU9S zB0h(J)pvkHMia6JL~|H$C_o7$(fZTfbwGT*R1*?FbgUugWdJVVV9Sf;cFy>+%fsW2 zFLl_^>3OExKC%+ekqLlFBCP({s{toE?xTN46^QTbxfra3qxM-xX}s{%w1f7BYUpKCGxr?;64ig}w_j*nw}m6hk>&B)S75aDOJ9 zGaQqe2${kVcfg9H!LnQkC%INh-nRBv7Q+FXfx>Obe*6pJB`gB^ANE4@_*qA3D!u_B zxa4i;LN-{YQ1-|-pU!ZA(8Pu&cE@v14OyUkObYgkXUsrD0Yw-Fzj1#3OMM`m=}P4e zbYRy|#Ew>baNsF6)(fG5U3_HrDSKvG1>eJls_c`AlX#TZuP8iLxQXLLQ_Bz)1EvqDoTXhf5_7tHd4|z=%`o*T3|K3~@R?mLmPU$QX4EsBRZ_fDIKp3Cb!- zE&W%rAuIW#-VtA7ifhV!jVsXVxS>N{;|e4y>2ekHcEc0;P_rRljf5+^>WY)LAGf*< ziMaFffnkMRs_mF7OGi0`7zV+8D?lSI#9(h-y*(C~_RlJww$ECG8r%|bc<7Y|A00l~ zHk4kd!4`?{=}w9OG(>u@%jxnh)PNo`EJ=u?h6uFD$dh*i2h_mOXm@^!EEdosZ8JYn@zH!y>xz4+Iz0X8<+i|edy zy3YcK3K0>Zw0&!K5!cP7+V&=X9=c(47wpu>eDGRJ5>xI;1F@o96l<+;J-7fVTRnr z{#1?tpKrpAbZA660qqCdDCI={tni-g1_|wArBty(yMT%tCsuS>K@=nf51AjtbJj*u zil*&Ah??V1a?AUnm()xy;~DpYl?cfn;N%U}p8!z72CL(C!Laiw10N}7HP*C zQ}qPr@wma^h*G$D!{A8%M%*wjAHfi}%Tq6V-f-JI*=BLifU(*QbE_u8W%Fb^XEX3O zJMv;tT+rc;qHf#dY4Zf$3$Ow@1WC+R$fZMvxXb@;HRN)M3>`PvZX28Ps9SvKwEwUA zPq(2x8YcVulj}#}L-rIXB_09}Qd{vE-vJK9S>EE*_GMqlgVS+ml9ufaiD-ShOdD{> zcxU9YqbnD1fN64Bhhrd#VZ$?T)wTi5$jg<&kjk*9U>kMoOMXm^#9mgav%l0x`*@>p zYr|t;ei$|&3&aW8V1MO4DA<7EK*hrgXMHIP7+FnKnqkO3_>=;95zB0tDW0mTl}I>u zcDrEY;wDJU)}?}!4byrou@-m&k$~;=%DRDJ8?-yRFzpB&nl64s(9&3O+>n{?Lj}y! zJ*FT-7X%Q!F+TBr0|Pt~VeuP28UCO{Buf!OzM<-|mIy7fB$!&GxhbaF4G;mtgtGZ! zNKv_3+mQ}0wS3g*i31xQXu=k{ELYG-RQfEL!NOc728_o79uIb@HV~nHgWYOyIFvA< z{Uu8)%>_zTlXVpARDPR{2fM}7G zw2w(_Z$n~Wls;kiXq9#S@!C;6BHZ>BO8W=tgJd6o7e!-bnH+h@KVo%@(ifS7$+^R>RSv3tx!hv~ZmHd_5Amcs8xErC-_m}}!2oNaq z4}iwP4E8PCtt4YGHHC>`x1qFw>BY{wawx1a?;^T1 z70)-^ZQ5{~KX`amyQUrQ0fqz_0V`Ks#hU|$P_NO+ZV%Ka<;OI@fI_kMEe-WnLj_B< zRWONUNadl36G5pO#P}$ku0`4{m?qeO`Ch$zD&Bw%9q)ww2?QJPbs8d|Z5=4Mp@`Dv z0LT|jY)e=~4f#LjP7u}}ND!)lBUI64^y( z4;(yj6)GZ1l~4B?qBM4V2ofE(q9RLW+<+^idQk;V%t#v+sG+LL1P*Ut0@UraKUG!R z=(gY#6qU8C0x%n{5C>XS)3#q@3WQ1aorSZAALd-o)6M-za6DlLoJs_pP@QY|V zzR1g_b2023fJN8CXOTlEq?|9iL|wjw94MybN1;LqYTskQ%p?+D4H-Rg_!+dqYdY!< z^?g014b%~D7ea{YDyV~=J?s*2>p~4yAsJ(;iG1d@1oR&*>)!|)IRARpt#gN7Vxq0uf1w7lX16MFrB_|$yT(%tutJc3ep|A(po+c71qDbKO^?$b-QhM^G5^?|mmxXB5 z`w!)T%m3A;u%DIWU3HmW2|3iq6Ut&p(T1-3P7nC^^S^(L9IDlwK|6M}Z)%z3hTTJh zg&b(Gu7QN6DfO|Hr?v}2T*v|BeQmMIuiTu4HKK?Q%wd-AgqRxWEien(aiBkh4X~2} z#$WTYTDM-2Pc$-8Y&g>oI{3w8qq59&0Xi|=@GLJjoESbr=Q1`7R+BJbTQNku6 z&AmhvjOb$Oxl7IanJ{1-0j^#CsJnE-SVr9xp1{)JS|vx68ZmAmOj}US{?`ti2+HK( zNi~#g2Jc{m1IA8OH4)Z@7&3sw^o|wWlpsnCOk~zgyx|v8z*niiphUuQZB0?Fnas)W zzj_~6_WMx*w`@hbx${st!okJZiRMp8}OGMCEDOYLATu{4TI4Zu*I9jS)sdS|uhbKd2Y zuB3?d)aFM$pQJu%s`%)@)_|hP_mNCfJ1MeHR0iQN9KHcP1mB%Kb@?e`9+~<{TT*2= z*})fsX2mL{&;vW82@-~upMhMnY182C{Q@wEav`z|KI;cQ3pg%QSeU}4yzDd@))Wz? zB%61w(!O2@El_;yGia4|0uY(b>6LmsuM+QZL!KRMxmU><-#nkqn`^9+8M+-Muc(kj zXfPFw(lq5?>Dd5Hb^c-c)bC+_d{R+_t2#OrG9m?cyT=PITzLoE^od|GFJR>|ed1gh z-cVm~;VEJBEwo_RV6hkG+-RXOVXEm;D}om60+PZNCJAQ9>5hl;5d|*G_w2+9a-5GR zK1TANWw-os&j+TJ+V-%QD)eRK@QzPpw_ixA-%boQ;1K?lye#AZ-T3MT*az(oyBWl> z^Q{+h=yq0b(}^aDW`pX}Z+kEM?*5>1e%aTFM>V$Yi&*(X-~fl2_=Uf0NK9xtI0~{& z`Lv1xvnF_8LvzO57Ipw2r^-}G{yaE(RQuhHq8D`A#O}$~gP|RAm(wLY;|SZ)ZZhd# zZa5=ITVo|abjW=PZ^S{tbDQy%io$@6ByTg&@;KlIN(AQC9e<~=8w~_pVWNbE-_RN% zo{@^l5Jle;*;7{bF%afmJT9>z#kR8wH$dlm!jl-#x{w~?*u9oW0vCinuBERU4R$!< z6ls^S^6%;I-T)5CKGlTcB!-Ijm=t!3LVgg3>9r1F7@-_!ABu<#R)(126UfCTKKURK ze>SU|J17XmeIYP~z?~82(T%5+t{IW**R!)YFD z$avgQTT3&N=YtE%8s@+}AUC_;wpedrhHgLVvY@2dkdbxNZ9(bM%Su~?n~$>}3=PEk z$bV=?)N9yJe0g4GZf8su^ZrS8Iy|_6Z2f5y`;x$I#1LZvpr1hom`Eh{{v|a`cFzoU z2WN!)Svn+=K&TQp+1O60?HW(`H;uqSrz$S`piU@q1PO}OpzAfKQ>eC@BWfT+5^fnv z#c`5&?qiusM%34UFJwV zoSOFyog`HY+*TL2fgzOr0x02?2cySQz*D3!*p7eYpeUA2!(kFl!hsG8nIK4LBdcle zH}LW|&ZP{4O6UkH$v}cO{vOrW*mQfvnrWPp(pe&TEjVY3`RGf8(TKtlE3vu8Z>4xo zw<>+*42u(H+wgXnakbw_ZP@0-ay4+|02O|f$gAOCCvUey7jicLifOP?+6R6yeU6)J zX#WfJY;QsMtiy^j`*Wvj>u==-73*s5XG0HGa08*M&bln`pa&wr?IyLb0_cHYkD&9= z<0wH7<59NYHOSs7hJO>GA!@^BKZyJQwX3ZUDTQWDk9ZSU;88R#4H{}HzYZLb)EOS=vME0C z8n8={{>YBky4wvH{E8el3??pRT2wF)iV=*c&&O)F4-a>bYeZ_NZUYhuq;=>??M~>h zVR7G(so*KBMbf2*w{E-D{=4Gn2tO=ZBZX1kX^~hWlTu2UXD@q|^k@?IR$@l?q=M; zW%EVi5e8|rSNe8zrnSLd!q24_CrK4!@nevqa>0^pGSDZx8JNz>DE3;{5 zQ$Vwh&0U9Fe7SGvA>86i9PIr5yR;PD%J9mDesfS!9`wiIQ!jU91{IunY;;uE3pTW< zun|`~D5pVf`grP;ptZza)>A? zI!ttVUi}z3kZuYY`7d%Hh((T_m4STwFmy->B?Lo#4mLdl;6a3#GdZ7=7!HXSuG65v z1HnBM3v91O3Fg&171e}^GwsLX#Z%-udoQAXX~Ahyk0o)lZAc;QMXxVTR!rWQU)u#a z#dbsvB!bg1!QC)Aa$tzB5e2cIajlVCFkx!2EVHAdK)5r;J2*}s*BD44V*HSaU0SOw zH0lkTWweSb92`jpw#6(qyhzQ)43{1u-rRPl`7CmP7eWv!w_5M7qa>X87?R-^r;jS| z`N{=BN5pfQqFS*{Aa2Wmr@E)#SJehHME`P*9e&}~&;z@toYq}Du-8y<_3(lbsmBrM zjBvx4r$I#*XN67%wh`|1#g`P(_$v(Ce|=?I2Ya2dt~8lkLK7pfggf0uCHRl(|igoel{wZIUwXAk3b2xb}NNnD((IO59aN-R!9i=)kM= zE#M$WcZWF11D@hP3NCO~9s-@dBKuNMklSz*V4v`>d4nRV35AocynPxuj3mSxI8Lkzuckv483}xAHv*+vvDa|` zEt(AYbsRc6*R9FC-+S{K>ixcB!}Rmow%dm44;E9`(k(GJ%v zcdG9Th}~K^iS4_m^Hr*QOxwM$+CR57RbNq#ax`F6!9>Q<5jmQ;$=D^Q9Z3!-5+DLJ zJLzg|dy#geaf>Gvx?RY@mRl7*V;AvP$N^hiv?yFSJ_;PFC`9nb3RzQ*9F+Qt=mZB( zx3*NuM%|X+;zl-%FG%^I84*s_%T-nd7=n^Wz1sMb%lME(1gvm96gn%7NfEQ#^OQ>Z zv`S^OPh`04fExlR0ADm`WYD;Cd>FyC&gR*PQNDb{qDvNyx%M0abbrm%3;2X9ReHj@-32q1Cz!-v=r?z zZtLbb3_DOpQM1R}=3!Rw#GUbJUrTz3v^?fjxUGugW}0_X0~uz1G=mbCrsN&$U`SzL zQM(lr_UYvIv-7`1$*ApNTiPna{(cOKKk_#1?i)EU$IS~tRV~Ke{ctCV0Ug>S(%~!4 z+ZYBN(5I$5BmKnCrq0;B%f19-`_n zC|TZcB&_%5m(f{qr>^8;hT+}UZ_8>XViF0b)$6g0pGc?Xia`sk;6>?m6%50O&yKEk zK3(zI;k9W>t~m@WqP<`c-|g?wPxM4B7NU?uR1hWkB5_Zwou7=8qfce{@@r5YX+4$6 zgfDC%kbnfM728GNk`;iAd;n*f1(1kl&`sRlrx!lccAv1HTR9tTTYeNgbh1o4a`;9d zh=Yeg{>YSc!9bPAf7_K>LUDVsiSnbe5=D+mv5;gjPIq$lFLtq%(6U_2@lGg;;Wp@C zh+#dJ+BEcGwiU|@A4O6n>BEsQG7lsd!-`!g-FPXGLHV^ssMtVp)3IHwMRLAP+8O*^ z$xW@ofR+(9vCof-EK^+1&VPtyuW}5C#t=(!4qL0N5(F~_8|{oAG6^7wfXIhsj8683*~ug5gljyKsA^RlcO3 z^9d*|b!s*FIb+1i>*MyQw4W611Aw0E(rEq^J8)6tKmBD%Li1NOr`nDBVd#LRbfYbf zyV+dxic#D$hl`ZXTB!A0lp&WTx9_(vDUkGDLF>#!+jg7q>E=Cc}kDszN%{4$AHzsEekuhaa+y%Roqvn`76Z&e3V#Fh-Vk z_B#!f90pQ_?1^wIKCZE0^3x!;)fE;^x50j(vYm$AO4k}0h+5H}cF%pK$OMj%BR5r* zJ_{TMri~0H`{X5X;D9>BJ}h3ahyc=ZEDDPHjoA*xh42X4rZQ#%tq(O7Sf;)|R$gT; zf2q#E0*+|4qkJ1;O+2&kWY*aEII+OGsbk^`Q70by4l$y28F-74W|4eiK}&`GYbY-{ z&84H94YGQ`bzMvcd@ki`iM`{<=S7m}5(f!hIqpRQy(1LaMBwpSWVat@p9RIlxw*m? zrAwE(;@5toX(R?JbzJVyvKMZGY!}&~ z?#SwXIZc;4(fJ+d0N3%kHL#Mj)73NSf)3?DjCY4B<@BzkPvw*DZXJ;$91n{0Cn%wbs&2SE6UD*qPqPP4SC#_ z?e5H>(S4D&BzvdrHq1p|YeaU8r{EwCIvv+Y&J5jX+drb`vqr3plEjB2h1Hx-*i9q6 zva8eC0*I{f>9JUk3nykfB4GGplwq}|ov?!eACOv8 zn5#!r*uok`rR(ggwEHR|Wa2f-Np;LBaa^!3X^^M77YQ<}=x!oZ z^0mm6732w(=)Av&vzKL4R(~f?(!QArj+R@ok1Q}ATbJldT>&j;u_C_}X6D=Nc_9aU zaGxX#!YYXg+tOexE4@E2W$0$&UDxHp@e+ z9v$~(JAW}{cm82P3*DdMpn77D%(PRw4LG=KI&H63{(Yh8Pk}=js2$>svaAr`x3?%m z$$g-z1lso4qgV@u8*J~4XCo>gK_pJ-;(YSA?6J2!s>0sV{+6=B>u8Ji2~wfiGz@pG z4DKHT2aW{%!T!2AOsFG>*iiQO4|5#?j(LQ+O(_f0f)3pJ4H8z4lrIHy;#{NcK#qeL zgwP!5_#2KWZJs*$_U|g4Xj{oZSK(C^ih=ex=WE|Q021O7%Io26@WZ^KH>FS+qLPB{ zP(Ct-OokAc%T(v{6H5?$pq#NboxaT35O_gfB#Mbuc6BW52{D~HF*>3o_*VW}#3Fb# zSBgvlz|O=##JN@}%>i9&t7b0-7KAOuEVf+CAH@z+ZJU7KG|G^L9S~L<@+SoZLW&yq zmELK#7jxilq4qw+8>A2b$CJ8-5OM5fi6K)o<*KWO*kCOfN}zkZ1glV_V7l1ba=wy7 z!{;Z;+p&Zb(IrV*V^EN`f&+&_Q`fePnjE5ij_I*Pe#rZjhK-BMoqB8Iyh87h!lub- zFOU<^HUnc_cP7;WBU5ZmBF@`;Vryye#&)CK^~SHHNs8`Tycn*g!)>vUE=LF34?7}q zXJP`${?uhQWYw2DgvJ%OWMTO^T)7x45mU3)Mb>){HlWGe+)X(bSSci(eV6gHU_*YM z`S{)*!_=4V6nZ9vm7s;l5KEeCi##!a=?6$kgm!+TB}T4uQ~ENA=utmfveE5AhhZ~ zj;n+r87G%;`?Bgc%VFMK{a1$Rjt=T-- zv0!;8JeZVz${3iaji_89O|QB*Ay|qc4m&zQ*-i?WiP?4`_1ueUg-c;~7wjA8%lJ!y zpp~o#g98197qk6xcv)QE4ZWUF13L>x@rj4AZ#_Go&``)jjQ3r@K}MR~P}6XH(G#~{ z6hvf{je1;T3UmGboNuxjUTgbnSv@;Pjv z#piy^7i@U8hTJTNIp?!56P5#^>OPWR9LdIeyn6{dOuJw5&A2-bWoK@`mx|d7XNA4& zjuX7&;8a1kl$^^h8WPcF5juE~5&zt&(SV1-g60nX9-ty9gj(TAoM6S8v*a3 zRaPOt!^j-+a5-TIZgcdr?2^L8_r#QA>`CU_q6!diKO2ZprhrRawf>9x^AXLI4oSjP*Vq@13uZi9Y${%GG} zgpB$H6X-&~l>>;5qbz7HxD>!O6+8!SA$Hgo7hc$*JF%!qW5yE`z+N-BjAEy8iC;$7 zxXUBX#DP35g(iPQye@gL;e|aj4qN!`k&bkUJ_9E@!(3mM&Zdr>|lwy&==-EcpzJbu)ygL1Z zf9&rOXp)oT5{EUjnDmVnwOc>?d=dQ6QwQD3}t&Tj*IS)Ygr0_rLvf! zLOUnoEd&8S;EALl1kn+ot**gF5cYKMKQja&#aG&VQKviLav}P9T%^ko9#$;E_Q%w- zv2QBMX~z|&MXt^5NzMAs)FRU$c+RD;+6g;;$Dq4Hv$RLEw`!Ys8!{-5f`n`_NOoU= zO`n%xSCn%V*W$|}l)n`T>azM%y*V_$3Km;*Kd|4o-8t%y0R)A?PO!gx2W~@lNJ0U^ zV&uaJf;lkE@D4smaUqC~N>kCt#A*vckaR}*O>}78pcw1K;els^4Rk85+%w|T!!F+V z#(fN>6;_6L3o)qj;)6fwwIeI}G{*=>bPcU<|`BMiI#|1kuY=W~-iY zWWO{G+_;nY@meJA$bFEmC<~Lr8evu#AVRnh_HZWRYB8l5;{VmU)<}Fgzzu%ri@60h z7D@-RXI(yvA9P_x90C>?3qNo!bNtvjEv$vYgy?YtVPvSNt}+M!7@^atC-eZu;Mv)^ zVVA*0mLy6WO&TmK%q#t!;PY6SfCrz?TmX98FuAG3K#n7Lu)_~iKLf+?y!5>{j45Qb zhJtb_GL>tkiA5z?j0dPD&ZxXb z7k1OxLxRnf%r{fwJ@k+se8QcC?W2pQ0gKZV_z-zVXyEwN=EJIL-pmET0{Z{yLJAaR zyZduK&nL~PJto_>yRV--@xi|IxqAWcbRTkktQiHr-l`$;7pPm_8W3=(cwxt?eG%=7Aer9!CrHk2fH84?}LWs1BVEe2gcjRiir=VYR84bvG z)t>2ikn19UGP0Ih``3R78#V-mN)#;q`wRJ67iF1|?i<&w^KTqk+J5#=fW(Qco1Fjb_la9kxdFSgKy9ooRm z5d$?T#&h!lGz^VLOh2WDIe6Fxtc6 znhpD`of`YtheDR%=9KpEVi1wRH=}Gy52r*B)8!lty(GPehDw|gIOZc>t|$1wt!GHE z*Ey#3{dZ&*kyJTC-LyuAIJg?xmsiz{g2o%+#&je>C*W8`B)0dI2WD5Uy+%cO2bgdO zK-U^cgM|k}AMZ?)j^qfIF1wZS@40*Y{~tc&UUr9m@g*k^H3%5o@S%p?4UY>xh(#23 z=;C5MY&B%vK^A!z?sB1rdpy!t@8LFdd@sqO!&bc6312NUf|6CtyrwZlbF#dNhz2pp z#zug>Gr9O%9T;FAd+S7`YQ#e|FLMYrTmr0vk*}a_gAao=?zSVW-!XVFXUom2;8 zg?ukN$+u&)loll$$naph)%|E4E(?Rt%Yb7N5`J5L19Qp)zx(nOWf48Qc~5obm)%UD zYPZ_mZY%U3BM4BW9SwiuuZrEWY+vDQ4ArH$x?0|{P zPR=e1J1}zTw~4c12mHkln_Xj=J|0i}MC1vREC{Sjw#g#c!TwgdLotT1Lr-^j`CHhb z!(X^O7>Z$sh8+j`C^y9vc1YOM5{B&4Ax$Ly%(qG?T=He|Nxl_6e6Z>k3gK^s4s+~k z(%7%-w%0HnVt)P~f(`}$cr{Z0>4z3l3{s{8s)q7e><|dP*61S2v1X@PRJso^p@&Cg z+vx=}k$_FC_1&)-Rv84$4gX~NuJtwN60u371$H+iFs~s}&t>%9L50nv4 ztAumc3AxwxW9(%XstVSeP$`zp1C9^&op$AIy`nu>mBVI%%kPI-5B3P*o`d0*DK-8x z^gSU?Hvgi@sGdW6#m+DVQX|B%6TZq;s;9$uUo;g$X3;RTtvhN9w?HOFxE>W3N)84} zv#{+M=>$PM!9Aq7!N=sBf^jz37FwjV_zC~ZIlAX?<68yw zpj1)E4{J>FQ)7ZQ7dm44&Q`$J1oA{(Ys||3E{dKzw;k~$;VM?O&R@xS2!P@BF+I+r z_jcJLZn1@3qE|WcsRgt2G4Ax|J+-)ug_hQ$Y`>*Vnyi_ivkR)o8j1qtu;jfAtkxco zy2{dN7Q+ryzm@vENt1azp#G#q7Nqac9(KaCRqkD;38irRPitlYoiHr|#KKfKINHr? zXHXR(2O?X?N~wBBZxP!=X$(V&Fpvj_|L%uT@UoQfBO!^R%0U`)&2S?cCbxQ6qFGa zs+=}ujL9N8 zUoWoMZV^bKh*F9ws8Dv3W_OS(X zJc2EmQ8njqPU29rA>$x^|8a$YsUQVVG@!JXSY(|d*Y&uU*l_!(I(YI6wi7%Zrp2G}4&Vp)M$HAB4j(E`b0hyjdg$98>Vgl{(%ZYxrxr+`Gz%zeXz^@t z_$GQFQhs}5N1+E2SjUn?2p7OZB*AZY5DY!kKa)0u9@4`NLKay<_#r);LkK^FhwWm5 z^5_b3?XR#L0lWS;--*zS`N8mbvoWLL2ZKiyqLABn6^$(n2eg&4>7nC>64>RJZMfqH z0?cyWos^+GP6+ufkr5J^5NIc!gv-vu{(JBkA&f3R{V{r&RpG#Yy(O_|mXyt2DcqVr z3m^JK9L&9Q5`olqrUh>%&dFFKX>1XpJ!sC%hc4cFV0?>5qLs^XLJ!0dVh=QEkdL|V zvzMfdrXrD<1AL>ELuvIEKzY|3o18IGY4 z&9%_t&I6Ftx=$`TX^POHv2Q;y=g^IJ2$#)=JiEz4^BK_}GKEGS-dZUJ2V6w#=blFw z1EWIhQ;3#pAI1+wR8FOJ?uJRJ+yOETgGzilFp9=1@m~0slLu~pL>r24jwfVmJ`g7h zf=&Cf4KncW7l43*0@GyJHGgvbKt{_wZv$ro zU*w@dA4MXEPsJ?1(P9!k|GXR-|3P!HU8hT^%Rz|>wc<3l)#B|FWhaf*oi%=U*5tN! za4RBLZHB9CZPg~>Xe+_rx*gI zwXE*`b|9uw743--u@*lJA#xi!&PbDFtmw=KM&AkkUrKILjJ*QU_wOB5Fs2~7oG9?+ z@QiBOFaeb}y>cjz_P5dA_|N}z1ELaB0k$>P%2hZ+&Mk<9MOjYMt~#Vac^TDG=9k{x<5 zKWtOjJJJ_|An<#7Py0m+LhrrXqi`4dmE?!|hleE0$>3)4^Hyy>hhr_{2WY(!MfUtG`X*Ag{F~ge^i~_L`=X8B6??E)h>^hxu;RhU)x7o!P{|Yy_ zYmOuM5bXL-P}{OZF!;bvA`IrqH*Z0ZO?x= zd>V9k;E{4oX})T~T(hI_zw$|Dm=C7oJLA+oX|gRgxB2u6_r~f%3T~%F)Gs@CAN0Zd z3hn&y1OwD>x5~e>e6x6f7(yFLHj9oPM{;6n-?&-|8@+6Lw}I~Fd3@^GNJ8x zAK-)BiAY=y3=HTpuK~tTIvAN&i~ay39*!hI)3wOp2{&t4#`dwM?TPb3UE(p+vs~tl zEVRj#$^Mwwz2*CW@&ot+3*d!p6A?G|tM*axNcb#(;O+q5phD(b+)0G$GblQGx^)&H z1LCPI1iGzCYm_7RWGqCo`l+^pzKA5i)_O;H@;waEA8B7*e3~+Pnhb!-Wg>1mj`?)D0hsT=Ad884NF@dn~;P9%$Uxh+~QS8UP;faQ!0!G&eLO zJ*{MGhY#t=d{IN2HXoSdj+rm`5FSqw1P=_uzO*5CjKyg2%uv~Iw`p2{U3 z!K(15$f1$lOtR)*;$*@>mwio_HGS4>GQeA7z&r4T#wf?Z15*Y}Tv%+7M8PUaR03xu zRU$U$6v8+pv;<0EKh{*Mo7`FLenxCcS`S;90#*$pd(=->UCQ}I8U9*NFJu=kX|SD$ z^6#7GgUAm&NGzec(LH9$z8{-iIJY?Y8S)AL?gQ|7VN^x>Pgz{$5nFJ+KWr- zQyd#2HIN^?M?3tvUtAmyj18Nefb)gntqcaEK?X(GIznvl^DoGU6ESQDl|0LCjI(L6Sc@!BDmZ98%KQjZAc+Fpf*) zktJN#kpk@%5(JJVmYp>YL|GzPE2!M2;j#1RVtQnKmIG1volezu2wdatopAwA+lTiH zI@mR4!-<`7VMn)ZzOiWgE9An!xnGOd@Zuj-0-`^V^16($H~-;|e0ndIyCrm$gp!Cj zI1N6ehwB@kP)@G)A@UVI-oAhx83Wp#6)O199(Q79Rp?ZHA{GieWw&mPDY{*0DHsGGvTIFU_W5oS3nO z7&`(~KZDOclQ02G{Il>u(_oyrD5+&7 zA1W#ut7w9_d6mBu#L^hmqiCn-QC8^@M*7|;p-vai*s(K3Uxe}@#?5}{@X*QCMhZHY z>qEvv3iD_pV_t+0;xt2210;^v59py_+8FV6$OJ}?MSZ|=fI#}RkV4c=ls>A0(E9>O zgWCjgli_G?fu|RwFNBztvbL8=d1QV)gQA$%GGQ#@!_@4IJ5wi-Fec=Yy5fHpJyeWa zFj|-}uy)1?=~cLu8}e!Le6R(e)rBmHACBNd7Jr>*<(}WqP8SL!JROLIp(rgkJ#8}3jJmaw6Y4~s% z5N+j!Us^+0!-nM-h4@03>D70bj8fgW`Sy#t1EhA$MJIYvmGp9aXjj~3-{4_9kq+Jh z56r$8T{zvM3OvxU+78xEp0p~7$);_~x|k0MyahF2dr>$M7L3N_&6!V(_6nCLxDwdQ!xENcM;J2RpnYei_szH?wUA z3s+Sb_6avMu)h^54Ty4)Rrp)2KmM?mvj6tMyuSQLGqM0#Y9jw_Kj58$&$;~+DwcI|aWR4OrcxC|6%yncBc0R+03ics!h#Pd<%?DEJ z?dt|$B6cVnJ{W8vThL&{nL5YNe99OVU(6GFK%#*GaqpUPZuEem(}Xl=Cx|a{@}VTO zNbYKl0fYf8?2_6%ng(H0NU`7$gRp-sl3AYImz86_7D?IyZpFAvoO=(DKqiLVGFT`d zraj<;4u;gAbPcYo6k>bTq{bC^up_Y|VQi{MJS+0RCTW|5r}d-dL&n<&4`(u3^HwDg zizY%3)^xGDsu9jO&cwjDa@3>1%YwMWoQTQ9X>7qH32IqVcSz{L4tF($Ab~acR}&p5 z+$5@`_gCaK0B2EG#eQ6)6^YdJEsf<>jdLl@!$3|Z^slwVj3IX>p!9|0rqG=$3YSsN z?XPS;%*))l?3k=SoA^?0T8Zpe>bCI1j{^m_yCQt97 zhdLkEOa$;_=pjFzCoXfL2gS>}ucBHH@iF_|Q!teMxQ!+m=TxO_W)ZJ9Rn&HdE!lwg zWpd%rfo@lQmowSb>9~JkH|T&6L;8LPZ9)$DA)QO`|C6N0q)oTH99&S*9w<&BExWu% ziCZ*%v-2gofI}9!ZmC)Ew@l+vDSdN!W&apC*l7|tuK(#N50Fo=8&Dk3KlshP2OX9o zfD{@aExp3N{h~51P-BEHx<8jN%Ks!jGNQ@O3T&9l?uOYEu;M?_CME3Pz2(9++peoX z=E!v%`b2Th2izg-94ivuoMoGcLr4JNu(Yb$z6TF2NH=K+wnziA1{XnzGBYAFzg9_7 z1)1d06@F99kB%JAKKwW>GN8h=$AR~@A&zU26W!`i`^dS0tdheJZBEGFO7BN8qjoqi(Jqb1B$p+-UO1PKnG;gYh$qaUzXL0VA&E zg-+5(6y1wU#aMPxsO*RW!os|wP-XggFUUYG5o zV}}ZUuMEY2J=L+pD!tygc>_Bnu$V4%U?GDJSYJK0KB)I`J{lC{i>E3nX2UeCr7gEW zjA=lK&A!YviOb;P(4MK6wHh=TWGE%4o0gO?_sSO%`6+fF6{y&Y`!|_8#@MhlmCX=Ha|2j$p-#Es0W zSZmO4XRehLnUNT=WUUaIqSbB&xZyaphzu(ja@bjE-}qi*Bj$qZq3yoBmS_bb)!K!?73 zE3dXgVR9;Q<`uH}#NB%fJrF1TtbPDJXyjR*ye17j6uG9jO9O2OC(-WwadAY6tLP0U zowS+?0}IErFYk^Xn1AHKXUj0y>uH)FtbQR&IYH~fPI=ij2_@{59#lffJhl5fs4gwe9Mnc8Rt0~6 zV>`cjW}3VhC!vj7LTS(0AUEfG%j-rSH9Q$G;a{)kO!ue#?G4HzanR^CdA+0t(t~BflWa7PvTe_``3tZ}v2^ zpc6VsDIq`EGr!2tKo*R@bWpKpkx(8IGphn|Q;Wy)7iq_C zW!{K^*kVlDX25VHbCjH2Msd;}G#>2PDmbe4F6jUE|Fi!Zs|SjQ2ziTuqBIq&_CT7dzN=* zTKODS-xzhs*>#OA96L0;CNJkHE6KL#9SK$z^Rf;7ta$-2CrmywKi&Wjq=k+`c&%8D z&A>x@Aareg**^$76b=mg1NY*th8+Mo7gb|PFd-X-{Y8IS&66SJHjUV68P|`I@Wys8 z8%#mDfywy0b$A_^4@rD7pguQ3zI#&6xudEimQbW1fu_$8@ zh9OO)sjAx1Urf$q!j}&ZrQ$JZJBlc-ynhMZ6p2KwxRRu~ifScBDcj>uFUSrBC3Jy0 zz)g633mnL*AfFU%39gjOWs*K6R`ZSCX7VNSEh$NwdgeU`E-uV(CNYcWHPY8+;sVn% zUQ6r=%XzFT4UTMhgijmnx>xB~)4PTg?L-ItvnJK}#Ohdv3%K&py5B1*#>=R1D5|X} z$A~07l8pr&h90Mq?>oj9TuIU? zxEFN^hRED#8AG*mEG(m^E>(at=3TC@w) zc*s3sVC!o5s8CINt$Or+I)$e1x@s8eOUFp5IBYNw_>>^^7wEQY}Qsq1|M-1+y< z8x5kv0-|}UG?kZh;4U(XzJJ>`a*5_M&2akuIVy0&rpan3)MGrQv;!0kZdug{0fj88 zp->Wys!s@_L!-gEt4-UAM*tCU-XHSo2x%dRN#2iA&ke=!ofOW2Qbfm4i)b*r89j>Z z+Cx7s5H@WjF@%Q3TA|fDY%A*LI4pK78r zU7VLMsbm#8+t$W~xnhzlZY|-H&O@NJ&H$K4l0FU{sQ&bmTaf+Gx_=_8 zwlCWb`XCNGz4#*xk%hqxiTAi_jdHB0fxT6viR*P2R0Bl~m1Kqrkkf@5>^6@4 zK5O`QoLOXoW$0(o4f!56FqL8KNNawqU`-m{OyRk{CUHMw63Gbj0?2Hh%ssfd;CL3H z@*S5*swN7i1kLDLB4o#jgEAOvt&o}P#E8C*{DSH*Xy6u2Fh3j0Z3hrqR6CQhT%P=u z;44QYWeBIa_ykWYP3}<&+S3wK5(>NBERe3+E#yE1u#4fSGNw+dDV5mS;US-6f}l0kKWYys3Taw^{M##i1%5Ot!f_bOa*n)j^t*LN$eEC z2K&MjP(vy7S?-13gRV1pgn{VF=%bs$>MeVEEMvly3?!kSs&M!)Hj z@4*J!Mg}i8t1}8V47g)H5m_I#gTd=g3N_S#4#93s!8`iG7NtKB2H#&{?UO##&ffC8 zuP#q@v3OiuLO@m;nzqV<`yG#mG6B+tg&p3MFFS)s8P6BhlMjkPwrq#zT64wK8s9*w zB#C-$sVFi#ZIMqzy^P`h>`t9PYC)3imWA4{!ff?OJ2q(LZFdo=Fn zNGk0rt#+<*|C;b9daGA$1r$`wpkH(yT$vRr_}Z?t-@}JOdylL*^6wDgJL3Tk7PIr2 z>sqC~1i%E2ldx79)ESWXnfi{0RR%llt={9zSi^;pIs!gq9Z`Ski^P+r5HS|}t8mRC z@}&weZ>oFtSBd;+K*RMNurH)Be%<^-87HRl4LXT0wP+-ZcL33hL#WH z#_F67h7}XAM}U0x^jO@vWesUNqKESuamljx2hwp}?qC@e(UezZ#Oh;#q=^4oWH9%_ zA*v{n%w7({Q4DAP%Bh7t-tJ1uEw!)`yj&7CU6K(O((rVfJhXn97tL#Gv2nR_-Bci#s5kjQunSHAj>@k2ryZzs#& zu?1lRL)XHfEAfW`1VUje-UKJu_T0S(wuHUHmAdYMPGlCbdzf(`ToCVJvcJT-q%CoJ zEO|OYXW68ipI zAPC0a*+aZ9&SWol%JoJN@gn%W1t6?Q<|jwzmJLy0I_E4vUrj+Ty4wRxHfljtx$T3h8uHX4(XMh#HI&WKxAHs%= z@f;V0zZMUOwBTlAtX$)#Q9~ip0x55q^ffkW5VfBZJp~_H;MR-O5~;kwbWi3cWt&_J z&hU03+cWaZjj#$JWa|4$p1DrJxN8{~)a5Q&#Fr}pPo}d&P@|#}&CEF1%JuiSft`w- z0Dzd3(iLj~l@A^#gNE$OguWMXZpWA>J310&n}Ibp565NVGLz_|WV~*#Wipcqm<15G zwWc!#mwFd4#`6WV6H{I>sAR~H%4dfc2GC?KOiffW=s zrfZ3X*g0A_@rBB!D2N4PF5=|JZ}g_l40X2m@J0^1uJ67BMsBOAursl?zTonBAhC9h z#<#WPQYLJs1N(f3IIgC{*rh7HY4A|$gO246!DM7Hx!2^7+~?6}F!^z(P{5`%(C}Pp zUB@}`y^9y4(x3xo4&`cAICO{)tbeXYCFmeCkWB-513IMsAoFLFEux@<%+EHTMAaT! z$$Up*ec7E1A!mV323vI%XfZHzzsfsF*X*^eObv<3i zH2^bA((O`)$bQvSjC8Pm!Aznp24NZU$;LFYosk0*4+9x$=i3KO-y#Q;6A&2fc0Wn$ zSPQc1l*yo&SQl)!$tjUwC$7OSo!k%&HqZiO+;Utb_yE@nbY|gRB~jn_Ame0JcVh(~ zgVr(D8n0q4a467xh-8_M$~5^t3Q15HUyOB82K_KoX1CjwS;)a2u>l}#JaH-#7lbrc zl0Kz;JrN~IDGd_2jBtri#DnIVPWV-wKDNl@p5m0pa^nkoD6%2Qbd82fPskw?3lYTS z;@w=(VPZ96dmI|221&HKy67(rez3YtymAenA##SwX@w0pV3`ATrX#*Y?p5~P8xBHN zv6e^-@gPWu2Fr^;ZtkY2P&EtrrKuLR-RcORSy+wn!Tx1T&FQyp(_5vPG2$4{N(&j> zZr7-tsJXokE~aFp$C`JOLlzJ9X7Rm;AJU`FTa`J!_=O?d!4n$_*-?@A^Zr7g5$~ex zyoiiM5)*%Syc~ufxc8_2>@_9i(dr#P`c<2PZi&#z-^osYiXS5GOm?`AZ8%*@5U#sC zA{G6Kk}{C2xJ49`E>3{wB+E+|WTf`EqR;Sdm^A(HhSju4=8{-(Uv3rl-PrGW-l?~ zMZy8fb~0*zJ2m84VgDv$E@)6;LXvO3PCI{q6OB?3y}ts|w7GssnRy`&}>k4!nnCT}GOQAH@!wI)e?HvSO%nSQo;;RB z5^}RkI6f1IOnEHyXHZk40Sf(8T6v)jg%B}7x{-hTnY)Pd1X$WGt%}r(5E|l{D@%+~ z##O8)E9V9is&}UHj#wwL#(JF5dqhW`%Qsvi0T&o$3?QGk+>ryoA zkp7^M)c-toAnKcVFL!VHvQzp|lwZ)EOu6jWje2`a_OF78mFupAUOe|A%*VX4H@-@Q zb~)=NF{nFXmzW2kH;Y$lCsFRe!6&lAPoV>y)WS6QuR>FCO*7IBrziSh>;MI@5c$Zg ztuIF-q>K0p(#nsn&_fPC#JESmS+he2e1*7D!kihWD>=>q867CS>o_(EfN&Lgq2t%w zb|RKo00z{RvT>eSG~5CB{CukZJ=Xzu2K(5^;_RJWsRni*v;&ii0(D;ZQTJ6LV%qMH zhLya%f@vC3RPHC^xJrQ0BzWEqy6vBdIKA@@5MuyqVXZR3@yrsXzi@eISYO$PqIdIA zq5~_!4~vp+Ki)quz4wd>t~!=NS>!LMr)njP!Iefiu+EwRThvhV^im|!aWcVaa8~D{ z$|``Hc)p09VIc=77mU%MF2+|)r|QF3x~u(QOU>qnu;cyeKHVK^`maS@F`B*;RC^?p z!M`B($JfZ*M)`a(p1?7wnN8kjlasVwYpm!wl*GO)9x{AgXvf&1o-IWs6t*3exGT@} zE1^SzVG?Ca7#DWvD@^m6N7#Z2%xxtvF6e-sJYKm89%(_<+F6X=cX)Cx6mH13^;BB8 z!CMgTL58Ah)Na)S8Jg|Td;u4+B<-m_gA{`dWzsCUXrLNvL?R@MX7)}Fu=3 zWkKKAE@L=DU`-ZxNu}GMn1UAj3xgoEglKsgxtH&I4p^~EkcU!l4!?FHuV9%Hcx=le zCf*S4d+w`0VEDMAo4_ELXr$vZ18sf7JzZ|Hdzl^gW_fD@zL%H;!yDV_GvmH}ZaP?r zkv{>tNc%aT#13|5*`>c@RgY%dzikpcZ24GD4gi^qvosvS7MtvBXGzMhV-Q{qb>P&E zNflNH%Fa;c4%5ua=>S)dm0VY0RaEbkpH9v%T+<5Z$AnP4Y8CLtTcoMf2 zo)V!EA>Zuk2iPXE+YnYzXpc*BXODuC9TEeywBr)(JV$UeV5f|{mzapNfQ~_QOxbHB zs}+QtUkEiJ&{U>f3eWuFTMN5J>Atq=V8Y?CbLPCi94^y z!>WxEl06wat?rEL)?s1x?awF(Vc%##)UX_!;#alHdb}eg(i>r#s6;T;4O@gF(gT13 zA3O@b>>YZ{7-R0Fkm}wIZOK89y$cc<*pX&^drjF-=8g*WUcj-7!(m6GoxOSCxnvlk z(2$uZwo+2|JQ%{cfM&J*}wUj{@DKfY5!1l|LxD?AK54V{{ZiuF=zi*0{|L+p$q^3 literal 0 HcmV?d00001 diff --git a/src/linkerhand_calibration/test/fixtures/o6_raw_replay_sources.md b/src/linkerhand_calibration/test/fixtures/o6_raw_replay_sources.md new file mode 100644 index 0000000..2a21c02 --- /dev/null +++ b/src/linkerhand_calibration/test/fixtures/o6_raw_replay_sources.md @@ -0,0 +1,22 @@ +# O6 原始观测回归切片 + +两个 `o6_front_20260914_*_raw_replay.json.gz` 文件分别来自不同会话;不拼接零位、安装关系或图像,不作为整只手精度验收。 +Gzip 只压缩 JSON,`mtime=0`;`records` 中每条记录与原始日志完全一致,未删字段或重算候选。 + +| 来源会话 | 保存内容 | 未提供的证据 | +| --- | --- | --- | +| `20260914_175815` | 正面拇指 pitch/IP 两个方向的 395 条原始观测、会话头、相机模型 | 新版归零准备运动;第四轮独立精度验收 | +| `20260914_192637` | 正面归零准备运动最终选用的 120 条原始帧、原初始化报告、会话头、相机模型 | 暂停后未发生的 pitch/IP 扫描;完整诊断采集;第四轮独立精度验收 | + +原始日志 SHA-256: + +- `175815`:`8bdbf216bd4135f1bac16dee0535a8b72ea0e6b0f31ab2b9a2e8cc4a83be9c0d` +- `192637`:`757ba1d7cccae4bdb93553b2735e94862bc74f57f153198e1a9d3eff76d45c8d` + +`192637` 的切片按原 `motion_branch_initialization.source_image_stamps` 选择,并保留每帧原始哈希。 +`175815` 的切片按 `kind=diagnostic_observation_frame`、`view=front`、`task_name=thumb_pitch_ip_front` 选择。 +测试重新计算原角点的 IPPE 候选,分别验证原始观测覆盖和已接受关节样本覆盖;原零位不被重拟合。 +完整本地日志存在时,还比较每条切片记录与原记录,并校验原日志哈希;缺少完整日志时,该额外核验显式跳过。 + +运行 `test_recorded_diagnostic_replay.py` 通过,只说明这些明确边界成立。 +新版完整采集流程使用另行标识的模拟时钟集成测试;不会把模拟完成解释为这两个真实会话完整通过。 diff --git a/src/linkerhand_calibration/test/fixtures/o6_side_base_20260914_190619.json b/src/linkerhand_calibration/test/fixtures/o6_side_base_20260914_190619.json new file mode 100644 index 0000000..42d0e79 --- /dev/null +++ b/src/linkerhand_calibration/test/fixtures/o6_side_base_20260914_190619.json @@ -0,0 +1,182 @@ +{ + "scope": "stationary_fixed_tag_ambiguity_regression_not_accuracy_certificate", + "source_raw_sha256": "43f7aa037caf28decf2e729735f26ffabe5fd6369179b130fa59d884ce54e4b6", + "camera_model": { + "kind": "rectified_camera_model", + "view": "side", + "matrix_source": "CameraInfo.P[:3,:3]", + "width": 1624, + "height": 1240, + "raw_k": [ + 3701.9669310172562, + 0.0, + 780.38448956403, + 0.0, + 3708.0764820855165, + 543.1934166854517, + 0.0, + 0.0, + 1.0 + ], + "raw_d": [ + -0.0855955821613016, + 1.7771811866066576, + -0.009882528169757824, + -0.0018744565873629446, + 0.0 + ], + "rectification_r": [ + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0 + ], + "projection_p": [ + 3719.39794921875, + 0.0, + 779.2527216851595, + 0.0, + 0.0, + 3715.812255859375, + 538.8889945124574, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0 + ], + "camera_matrix": [ + [ + 3719.39794921875, + 0.0, + 779.2527216851595 + ], + [ + 0.0, + 3715.812255859375, + 538.8889945124574 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ], + "input_is_rectified": true + }, + "observation": { + "kind": "pnp_branch_event", + "view": "side", + "image_stamp_ns": 1789384038002424192, + "tag_role": "side_base", + "tag_id": 3, + "reason": "pose_branch_unconfirmed", + "task_name": null, + "motion_phase": null, + "command_unit": "u8", + "command_vector": [ + 255.0, + 255.0, + 255.0, + 255.0, + 255.0, + 255.0 + ], + "feedback_vector": [ + 254.0, + 255.0, + 254.0, + 254.0, + 254.0, + 254.0 + ], + "state_image_sync_error_ms": 7.491799, + "timing": { + "received_stamp_ns": 1789384038027697599, + "history_snapshot_stamp_ns": 1789384038027907158, + "latest_feedback_stamp_ns": 1789384038009915991, + "latest_command_stamp_ns": 1789384038027461363 + }, + "pnp_observation_evidence": { + "tag_role": "side_base", + "tag_id": 3, + "tag_size_m": 0.016, + "pose_source": "current_image", + "corners_xy": [ + [ + 635.7402954101564, + 871.7483520507814 + ], + [ + 715.5008544921875, + 872.2428588867189 + ], + [ + 716.2201538085936, + 792.0438842773436 + ], + [ + 636.2169799804688, + 791.7636718749999 + ] + ], + "selected_pose": null, + "reprojection_valid_candidates": [ + { + "quaternion_xyzw": [ + -0.044633764301794464, + 0.0076389447486132234, + 0.0021611953202176335, + 0.9989718728984152 + ], + "translation_xyz_m": [ + -0.020686883190194915, + 0.058714895217251775, + 0.744562855591804 + ], + "reprojection_error_px": 0.10935170446797024 + }, + { + "quaternion_xyzw": [ + -0.0341108338913104, + -0.03511406600381762, + 0.003990533332137572, + 0.9987930361309315 + ], + "translation_xyz_m": [ + -0.02067953829092587, + 0.05871663804033328, + 0.7445618482013543 + ], + "reprojection_error_px": 0.12294261238764193 + } + ], + "candidate_diagnostics": { + "motion_managed": false, + "motion_selection_committed": false, + "solved_candidate_count": 2, + "reprojection_candidate_count": 2, + "independent_tilt_candidate_count": 2, + "maximum_reprojection_error_px": 1.5, + "maximum_independent_tilt_deg": 75.0, + "minimum_reprojection_error_px": 0.10935170446797024, + "minimum_candidate_tilt_deg": 5.190794593307594, + "maximum_candidate_tilt_deg": 5.612019732803669, + "observation_stamp_ns": 1789384038002424192, + "branch_revision": 0, + "branch_status": "initializing", + "branch_reason": "pose_branch_unconfirmed", + "branch_confirmation_count": 0, + "branch_frozen": false, + "motion_evidence_verified": false, + "motion_evidence_id": "" + }, + "branch_corrections_total": 0 + } + } +} diff --git a/src/linkerhand_calibration/test/fixtures/o6_side_preparation_132102.json.gz b/src/linkerhand_calibration/test/fixtures/o6_side_preparation_132102.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..4db35914a59c160c31b55809c7f012820c3ec5dc GIT binary patch literal 177258 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

    nxURO*HSQ%;$H1wwUP@O9q!C-9x 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