# 算法模板合同

本文档定义自动化标定车间的算法接入模板边界。它面向两类角色：

- 模板维护者：维护 ROS 2 接口、service/action 节点、算法模板分发、报告汇总和 smoke test。
- 算法实现者：只在指定算法文件中补充具体标定算法、质量评估和产物保存逻辑。

当前模板状态按 `v0.9` 处理：调度框架、输入输出结构和结果回填路径已经可以作为算法开发基线；真实现场数据接入后，如果发现原始数据窗口或产物字段不够，可以由模板维护者小范围扩展 `Input` 结构和接口文档。

## 总原则

算法实现者默认只改各模块 `src/*_algorithm.cpp` 文件，不改 node、launch、gateway、orchestrator、report 和 smoke 主流程。

算法文件必须满足这些约束：

- 保留 `run(const Input &, Output &, std::string & failure_reason)` 入口签名。
- 失败时返回 `false`，同时写清楚 `failure_reason`。
- 成功时返回 `true`，并完整填写 `output.response.result`。
- 不直接发布 ROS topic，不直接创建 ROS service/client，不绕过模板写报告。
- 不把 Isaac、仿真车辆、真实车辆 SDK、PLC/CAN 代码写进算法文件。
- 需要新增输入字段时，先由模板维护者扩展对应 `Input` 结构，再由 node 层统一填充。
- 需要新增输出字段时，先评估是否已有 `validation_summary`、`estimated_*`、`artifacts` 可承载；确实不够时再改接口包。

## 统一执行链路

四个算法域都遵循同一个形态：

```text
orchestrator / gateway
  -> service node 组装 Input
  -> AlgorithmTemplate 做通用校验和分发
  -> 具体 Algorithm::run(...) 执行
  -> Output.response.result 回填
  -> orchestrator 汇总 stage result
  -> report 输出
```

通用校验只检查请求是否完整、任务类型是否明确、必要 ID 是否存在。业务层更细的判断，例如采样数量、图像质量、轨迹长度、时间同步、真值源质量、目标板检测质量，应写在具体算法文件里。

## 稳定边界

这些部分视为模板边界，算法实现者不应修改：

- `src/core/agv_calib_core/workshop_orchestrator/`
- `src/communication/win_ubuntu_bridge/`
- `src/core/agv_calib_core/*_service/src/*_service_node.cpp`
- `src/core/agv_calib_core/*_service/src/*_algorithm_template.cpp`
- `src/core/agv_calib_core/*_service/include/*/*_algorithm_template.hpp`
- `run_isaac_real_sim_test.sh`
- `src/simulation/tools/smoke_test_workshop_orchestrator.py`

这些部分是算法实现者的主要填充位置：

- `src/core/agv_calib_core/external_localization_service/src/reference_pose_collection_algorithm.cpp`
- `src/core/agv_calib_core/external_localization_service/src/marker_alignment_algorithm.cpp`
- `src/core/agv_calib_core/external_localization_service/src/truth_source_validation_algorithm.cpp`
- `src/core/agv_calib_core/chassis_calibration_service/src/ackermann_chassis_algorithm.cpp`
- `src/core/agv_calib_core/chassis_calibration_service/src/differential_chassis_algorithm.cpp`
- `src/core/agv_calib_core/chassis_calibration_service/src/single_steer_wheel_chassis_algorithm.cpp`
- `src/core/agv_calib_core/chassis_calibration_service/src/multi_steer_wheel_chassis_algorithm.cpp`
- `src/core/agv_calib_core/control_calibration_service/src/pid_control_calibration_algorithm.cpp`
- `src/core/agv_calib_core/control_calibration_service/src/mpc_control_calibration_algorithm.cpp`
- `src/core/agv_calib_core/control_calibration_service/src/lqr_control_calibration_algorithm.cpp`
- `src/core/agv_calib_core/control_calibration_service/src/pure_pursuit_control_calibration_algorithm.cpp`
- `src/core/agv_calib_core/sensor_calibration_service/src/camera_intrinsic_calibration_algorithm.cpp`
- `src/core/agv_calib_core/sensor_calibration_service/src/imu_intrinsic_calibration_algorithm.cpp`
- `src/core/agv_calib_core/sensor_calibration_service/src/sensor_to_base_extrinsic_calibration_algorithm.cpp`
- `src/core/agv_calib_core/sensor_calibration_service/src/hand_eye_calibration_algorithm.cpp`

## 统一输出要求

每个算法最终都要写入对应 action result。通用字段含义如下：

- `success`：算法任务是否执行成功。
- `error_code`：失败时必须写非 OK 错误码；成功时写 OK。
- `message`：面向总控、日志和报告的简短结果说明。
- `job_id`：通常沿用请求里的 `request_id`。
- `data_quality_passed`：采集数据或执行数据是否满足算法输入质量。
- `suitable_for_commit`：是否建议把本轮结果进入参数提交或人工确认环节。
- `recommended_parameter_version`：本轮推荐结果版本号，必须能用于追踪。
- `validation_summary`：自动验收摘要，是 report 和 stage result 的核心依据。
- `estimated_params` / `estimated_parameter_set` / `result`：本轮估计出的参数或外部定位结果。
- `artifacts`：算法产物引用，例如 YAML、JSON、CSV、图片、波形图、拟合日志和报告片段。

成功但不建议提交时，允许 `success=true`、`data_quality_passed=true`、`suitable_for_commit=false`。例如算法完成了评估，但质量指标未达到自动写入阈值。

采集数据不足、真值源不可用、车辆状态不满足、安全条件不满足时，应返回 `false` 并写明 `failure_reason`。

## 统一数据输入合同

算法模板的数据输入分为五类。算法实现者只能从这些入口读取数据，不应自己订阅 ROS topic、连接 TCP、读取未声明的临时目录，或绕过 service node 访问仿真/真实设备。

- 任务请求：`input.request` 以及已经展开的 `task_type`、`task_subtype`、`required_*`、`reference_*` 字段，表达“本次要做什么”。
- 静态上下文：`input.workshop_session`、`input.vehicle_profile`、目标传感器 ID、底盘类型、控制器类型、frame ID，表达“在哪台车、哪个工位、哪个坐标系下做”。
- 轻量在线数据：各模块的 `latest_*_telemetry` 和 `*_telemetry_history`，表达“最近一段时间的状态、质量和趋势”。
- 大文件数据：各模块新增的 `*data_files`、`*sample_files`、`*trajectory_files`、`synchronized_dataset_files`，通过 `calibration_common_interfaces/FileReference` 指向图片、点云、IMU 包、CSV、JSON、rosbag、mcap 或数据集索引。
- 诊断与有效性：`*_diagnostics`、`*_history_available`、`data_window_start_timestamp_us`、`data_window_end_timestamp_us`，表达“这些数据是否可信、采样窗口是什么、缺什么”。

大文件数据不直接塞进 action result 或 `Input` 内存对象。模板统一使用 `FileReference` 传递文件引用：

- `file_name`：文件名或数据集逻辑名。
- `file_uri`：本机绝对路径、挂载路径或约定 URI。
- `size_bytes`：可选，未知时填 0。
- `description`：说明数据来源、传感器 ID、时间窗口、格式。
- `digest`：可选，现场归档或验收时建议填写。

算法实现者应按这个顺序选择输入：

1. 优先使用 `synchronized_dataset_files` 或对应模块的数据集索引文件，因为它能表达多传感器、真值、检测结果和时间同步关系。
2. 没有数据集索引时，使用领域内专用文件列表，例如 `image_sample_files`、`imu_sample_files`、`chassis_motion_data_files`、`control_evaluation_data_files`。
3. 只做 readiness、链路、轻量统计或 smoke 时，可以使用 `*_telemetry_history`。
4. 如果本算法必须使用原始数据，而对应文件列表为空，应返回 `false`，并把缺失项写入 `failure_reason`。

数据窗口约定：

- `data_window_start_timestamp_us` 和 `data_window_end_timestamp_us` 使用微秒 Unix 时间戳。
- 两者都为 0 表示上层尚未提供明确窗口，算法可以从文件或 telemetry 自行推断。
- 如果只给了 telemetry history，没有文件引用，算法只能做轻量统计或模板级验证，不能宣称完成真实高精度标定。
- 如果文件引用和 telemetry history 同时存在，算法应优先用文件数据求解，用 telemetry history 做质量和安全状态交叉检查。

当前数据填充责任：

- `external_localization_service` 已经缓存外部定位 telemetry history，并将其填入 `ExternalLocalizationInput`。
- `external_localization_service`、`chassis_calibration_service`、`control_calibration_service`、`sensor_calibration_service` 都支持通过 ROS 参数 `data_input.*` 传入数据窗口和数据文件路径，并在 node 层转换成 `FileReference` 填入 `Input`。
- 真实采集链路接入后，应由采集/ingest 节点负责落盘并把这些参数指向本轮任务的数据集；算法实现者不应自己寻找临时目录。
- gateway 模式下，chassis/control 的真实执行可能在车端 agent 完成；这种情况下车端返回的 `ChassisJobResult` / `ControlJobResult` 也必须按同一套数据输入和产物追溯规则生成。

当前已支持的 ROS 参数如下。所有文件列表参数类型都是 `string[]`，窗口参数类型是 `int64`。

External:

- `data_input.data_window_start_timestamp_us`
- `data_input.data_window_end_timestamp_us`
- `data_input.external_observation_files`
- `data_input.marker_observation_files`
- `data_input.time_sync_diagnostic_files`

Chassis:

- `data_input.data_window_start_timestamp_us`
- `data_input.data_window_end_timestamp_us`
- `data_input.chassis_motion_data_files`
- `data_input.actuator_command_files`
- `data_input.truth_trajectory_files`

Control:

- `data_input.data_window_start_timestamp_us`
- `data_input.data_window_end_timestamp_us`
- `data_input.control_evaluation_data_files`
- `data_input.reference_signal_files`
- `data_input.chassis_response_files`
- `data_input.truth_trajectory_files`

Sensor:

- `data_input.data_window_start_timestamp_us`
- `data_input.data_window_end_timestamp_us`
- `data_input.image_sample_files`
- `data_input.imu_sample_files`
- `data_input.pointcloud_sample_files`
- `data_input.laser_scan_sample_files`
- `data_input.target_detection_files`
- `data_input.robot_pose_sample_files`
- `data_input.synchronized_dataset_files`

配置示例：

```yaml
sensor_calibration_service:
  ros__parameters:
    data_input.data_window_start_timestamp_us: 0
    data_input.data_window_end_timestamp_us: 0
    data_input.image_sample_files:
      - /data/calib/session_001/front_camera/images.yaml
    data_input.target_detection_files:
      - /data/calib/session_001/front_camera/charuco_detections.json
    data_input.synchronized_dataset_files:
      - /data/calib/session_001/dataset_index.yaml
```

现场启动时，推荐把这些参数放到一个 session-specific ROS 参数文件里，然后通过 launch 注入：

```bash
ros2 launch win_ubuntu_bridge minimal_workshop_demo.launch.py \
  use_gateway:=true \
  chassis_host:=192.168.10.42 \
  control_host:=192.168.10.42 \
  data_input_params_file:=/data/agv_calib/session_001/site_data_input.yaml \
  dataset_index_file:=/data/agv_calib/session_001/dataset_index.yaml
```

仓库提供了模板文件：

- `src/deployment/profiles/site_data_input_template.yaml`

真实采集/ingest 程序应为每次 session 生成一份副本，并把本轮采集产物路径写入其中。

### 数据集索引到算法输入参数

真实采集/ingest 的推荐输出是 `dataset_index.yaml`，再由工具转换成
`site_data_input.yaml`。这样采集侧只维护一份数据索引，算法服务仍然只读取
稳定的 `data_input.*` ROS 参数。

数据集索引样例：

- `src/deployment/profiles/example_dataset_index.yaml`

转换工具：

- `src/deployment/tools/dataset_index_to_site_data_input.py`
- `src/deployment/tools/finalize_site_session.py`

真实采集侧脚手架：

- `src/site_deployment/workshop_sensor_ingest_real/site_session_capture.py`

校验与本地回归：

- `src/deployment/tools/validate_dataset_index.py`
- `run_local_data_input_smoke.sh`

真实采集/ingest 在本次会话结束时推荐调用收尾工具。它会读取现场
`site_template.yaml`，按 `algorithm_data_inputs.dataset_root`、
`dataset_index_name` 和 `session_id` 找到数据集索引，并生成
`site_data_input.yaml`：

```bash
python3 src/deployment/tools/finalize_site_session.py \
  --site-profile src/deployment/profiles/site_template.yaml \
  --session-id session_001
```

底层转换命令也可以直接使用：

```bash
python3 src/deployment/tools/dataset_index_to_site_data_input.py \
  /data/agv_calib/session_001/dataset_index.yaml \
  -o /data/agv_calib/session_001/site_data_input.yaml \
  --strict
```

数据集索引校验命令：

```bash
python3 src/deployment/tools/validate_dataset_index.py \
  /data/agv_calib/session_001/dataset_index.yaml \
  --tasks external,chassis,control,sensor_intrinsic
```

生成参数文件后，可以用现有 smoke 脚本验证注入链路。若要同时验证
external、chassis、control 和 sensor 四个本地算法服务都读取了同一份
`site_data_input.yaml`，并验证最终 report 记录本次 `site_data_input.yaml`
和 `dataset_index.yaml`，需要使用本地标定服务模式：

```bash
./run_isaac_real_sim_test.sh \
  --headless \
  --local-calibration-services \
  --data-input-params-file /data/agv_calib/session_001/site_data_input.yaml \
  --dataset-index-file /data/agv_calib/session_001/dataset_index.yaml \
  --tasks external,chassis,control,sensor_intrinsic
```

不依赖 Isaac 的推荐回归命令：

```bash
./run_local_data_input_smoke.sh \
  --data-input-params-file /data/agv_calib/session_001/site_data_input.yaml
```

`dataset_index.yaml` 顶层字段约定如下：

- `schema_version`：索引格式版本，当前为 `1`。
- `session.session_id`：本次标定会话 ID。
- `session.vehicle_id`：本次会话对应车辆 ID。
- `session.dataset_root`：本次数据集根目录；相对路径都会基于它解析。
- `session.data_window_start_timestamp_us`：本次数据窗口开始时间，微秒 Unix 时间戳。
- `session.data_window_end_timestamp_us`：本次数据窗口结束时间，微秒 Unix 时间戳。
- `data_inputs.external`：外部定位相关数据。
- `data_inputs.chassis`：底盘标定相关数据。
- `data_inputs.control`：运控标定相关数据。
- `data_inputs.sensor`：传感器标定相关数据。

文件引用字段可以写字符串路径，也可以写成包含 `path`、`uri` 或 `file_uri`
的 map。转换工具会保留 URI，保留绝对路径，并把相对路径解析到
`session.dataset_root` 下。默认情况下，工具还会把 `dataset_index.yaml`
自身写入 `sensor_calibration_service` 的
`data_input.synchronized_dataset_files`，便于算法实现者追溯完整数据关系。

## 外部定位模板

模板入口：

- `src/core/agv_calib_core/external_localization_service/include/external_localization_service/external_localization_algorithms.hpp`
- `src/core/agv_calib_core/external_localization_service/src/external_localization_algorithm_template.cpp`

分发关系：

- `REFERENCE_POSE_COLLECTION` -> `ReferencePoseCollectionAlgorithm`
- `MARKER_ALIGNMENT` -> `MarkerAlignmentAlgorithm`
- `TRUTH_SOURCE_VALIDATION` -> `TruthSourceValidationAlgorithm`

算法实现者优先读取：

- `input.request`
- `input.task_type`
- `input.reference_pose_collection_task`
- `input.marker_alignment_task`
- `input.truth_source_validation_task`
- `input.localization_source_id`
- `input.workcell_zone_id`
- `input.external_localization_readiness`
- `input.latest_external_localization_telemetry`
- `input.external_localization_telemetry_history`
- `input.chassis_telemetry_history`
- `input.control_telemetry_history`
- `input.sensor_telemetry_history`
- `input.data_window_start_timestamp_us`
- `input.data_window_end_timestamp_us`
- `input.external_observation_files`
- `input.marker_observation_files`
- `input.time_sync_diagnostic_files`

数据输入要求：

- `TRUTH_SOURCE_VALIDATION` 至少需要 `external_localization_telemetry_history` 或 `external_observation_files`。
- `REFERENCE_POSE_COLLECTION` 至少需要满足采样数量的外部定位观测窗口。
- `MARKER_ALIGNMENT` 至少需要 `marker_observation_files`，或 telemetry 中已经包含可用于对齐的标靶观测结果。
- 如果任务要求时间同步验收，应使用 telemetry 中的 `time_sync_offset_ms`，或使用 `time_sync_diagnostic_files` 中的统计结果。

必须回填：

- `output.response.result.validation_summary.time_sync_ok`
- `output.response.result.validation_summary.coverage_ok`
- `output.response.result.validation_summary.quality_ok`
- `output.response.result.validation_summary.tracking_stable`
- `output.response.result.validation_summary.recommended_as_truth_source`
- `output.response.result.validation_summary.position_stddev_m`
- `output.response.result.validation_summary.yaw_stddev_rad`
- `output.response.result.validation_summary.tracking_loss_ratio`
- `output.response.result.validation_summary.time_sync_offset_ms`
- `output.response.result.result`
- `output.response.result.suitable_for_commit`
- `output.response.result.recommended_parameter_version`

典型失败原因：

- 没有外部定位观测窗口。
- `localization_source_id` 或 `workcell_zone_id` 不匹配。
- 时间同步偏差超阈值。
- 位置或航向标准差超阈值。
- 跟踪丢失率过高。
- 标靶观测数量不足。

## 底盘标定模板

模板入口：

- `src/core/agv_calib_core/chassis_calibration_service/include/chassis_calibration_service/chassis_calibration_algorithms.hpp`
- `src/core/agv_calib_core/chassis_calibration_service/src/chassis_calibration_algorithm_template.cpp`

分发关系：

- `ACKERMANN` -> `AckermannChassisAlgorithm`
- `DIFFERENTIAL` -> `DifferentialChassisAlgorithm`
- `SINGLE_STEER_WHEEL` -> `SingleSteerWheelChassisAlgorithm`
- `MULTI_STEER_WHEEL` -> `MultiSteerWheelChassisAlgorithm`

当前本地模板的通用校验只演示 `STRAIGHT_LINE` 动作原语。后续要支持转弯、原地旋转、横移、舵角扫描等原语时，应由模板维护者扩展分发规则和输入展开字段。

算法实现者优先读取：

- `input.request`
- `input.vehicle_profile`
- `input.chassis_type`
- `input.chassis_capability`
- `input.chassis_readiness`
- `input.applied_chassis_parameters`
- `input.latest_chassis_telemetry`
- `input.chassis_telemetry_history`
- `input.latest_external_localization_telemetry`
- `input.external_localization_telemetry_history`
- `input.latest_control_telemetry`
- `input.control_telemetry_history`
- `input.data_window_start_timestamp_us`
- `input.data_window_end_timestamp_us`
- `input.chassis_motion_data_files`
- `input.actuator_command_files`
- `input.truth_trajectory_files`

数据输入要求：

- 底盘真实标定至少需要 `chassis_telemetry_history` 或 `chassis_motion_data_files`。
- 需要计算真实跑偏、曲率、轮径、轴距、舵角零位时，应同时提供 `truth_trajectory_files` 或 `external_localization_telemetry_history`。
- 需要分析执行器响应时，应提供 `actuator_command_files` 或控制/驱动遥测历史。
- 如果只有动作原语请求，没有任何运动反馈数据，只能返回模板 smoke 结果，不能作为真实标定结果。

必须回填：

- `output.response.result.estimated_straight_line_bias`
- `output.response.result.validation_summary.max_lateral_error_m`
- `output.response.result.validation_summary.max_yaw_error_rad`
- `output.response.result.validation_summary.rms_lateral_error_m`
- `output.response.result.validation_summary.rms_yaw_error_rad`
- `output.response.result.validation_summary.repeatability_error_m`
- `output.response.result.validation_summary.curvature_error`
- `output.response.result.validation_summary.module_consistency_error`
- `output.response.result.validation_summary.auto_acceptance_passed`
- `output.response.result.estimated_params`
- `output.response.result.suitable_for_commit`
- `output.response.result.recommended_parameter_version`

典型失败原因：

- 底盘未就绪或急停未释放。
- 动作原语不被当前底盘支持。
- 有效运动距离或有效轨迹时间不足。
- 轮速、舵角、里程计、驱动状态缺失。
- 真值源不可用或时间同步不达标。
- 执行过程中触发安全限幅或人工中断。

注意：在当前完整 smoke 的 gateway 模式下，`chassis` 阶段主要验证 `orchestrator -> vehicle_agent_gateway -> 车端 agent -> result` 链路，不等价于已经运行本地 `chassis_calibration_service` 内的算法模板。若现场设计为车端执行底盘算法，应保证车端返回的结果语义与这里的 `ChassisJobResult` 一致。

## 运控标定模板

模板入口：

- `src/core/agv_calib_core/control_calibration_service/include/control_calibration_service/control_calibration_algorithms.hpp`
- `src/core/agv_calib_core/control_calibration_service/src/control_calibration_algorithm_template.cpp`

分发关系：

- `PID` -> `PidControlCalibrationAlgorithm`
- `MPC` -> `MpcControlCalibrationAlgorithm`
- `LQR` -> `LqrControlCalibrationAlgorithm`
- `PURE_PURSUIT` -> `PurePursuitControlCalibrationAlgorithm`

算法实现者优先读取：

- `input.request`
- `input.controller_algorithm`
- `input.task_type`
- `input.trajectory_tracking_task`
- `input.velocity_step_task`
- `input.acceleration_deceleration_task`
- `input.stop_accuracy_task`
- `input.reference_trajectory`
- `input.reference_target_velocity_ms`
- `input.reference_hold_time_sec`
- `input.active_controller_parameters`
- `input.latest_control_telemetry`
- `input.control_telemetry_history`
- `input.latest_chassis_telemetry`
- `input.chassis_telemetry_history`
- `input.latest_external_localization_telemetry`
- `input.external_localization_telemetry_history`
- `input.data_window_start_timestamp_us`
- `input.data_window_end_timestamp_us`
- `input.control_evaluation_data_files`
- `input.reference_signal_files`
- `input.chassis_response_files`
- `input.truth_trajectory_files`

数据输入要求：

- 控制真实评估至少需要 `control_telemetry_history` 或 `control_evaluation_data_files`。
- 轨迹跟踪类任务必须有 `reference_trajectory` 或 `reference_signal_files`。
- 速度阶跃、加减速、停车精度任务必须能从请求 payload 或 `reference_signal_files` 还原参考信号。
- 如果要计算横向/航向/速度误差，应提供底盘响应和真值轨迹；没有真值时只能做车端反馈一致性评估。

必须回填：

- `output.response.result.validation_summary.rms_lateral_error_m`
- `output.response.result.validation_summary.rms_heading_error_rad`
- `output.response.result.validation_summary.rms_speed_error_ms`
- `output.response.result.validation_summary.overshoot_ratio`
- `output.response.result.validation_summary.settle_time_sec`
- `output.response.result.validation_summary.stop_position_error_m`
- `output.response.result.validation_summary.max_jerk`
- `output.response.result.validation_summary.saturation_ratio`
- `output.response.result.validation_summary.auto_acceptance_passed`
- `output.response.result.estimated_parameter_set`
- `output.response.result.suitable_for_commit`
- `output.response.result.recommended_parameter_version`

典型失败原因：

- 参考轨迹或速度阶跃任务参数缺失。
- 控制历史窗口不足。
- 底盘执行器受限或长时间饱和。
- 车辆实际响应和控制输出无法对齐。
- 真值定位质量不足，无法计算可靠误差。
- 安全状态不允许继续执行。

注意：在当前完整 smoke 的 gateway 模式下，`control` 阶段主要验证 `orchestrator -> vehicle_agent_gateway -> 车端 agent -> result` 链路，不等价于已经运行本地 `control_calibration_service` 内的算法模板。若现场设计为车端执行控制评估，应保证车端返回的结果语义与这里的 `ControlJobResult` 一致。

## 传感器标定模板

模板入口：

- `src/core/agv_calib_core/sensor_calibration_service/include/sensor_calibration_service/sensor_calibration_algorithms.hpp`
- `src/core/agv_calib_core/sensor_calibration_service/src/sensor_calibration_algorithm_template.cpp`

分发关系：

- `CAMERA_INTRINSIC + FRONT_CAMERA_INTRINSIC` -> `FrontCameraIntrinsicCalibrationAlgorithm`
- `CAMERA_INTRINSIC + DOWNWARD_CAMERA_INTRINSIC` -> `DownwardCameraIntrinsicCalibrationAlgorithm`
- `IMU_INTRINSIC + IMU_INTRINSIC` -> `ImuIntrinsicCalibrationAlgorithm`
- `SENSOR_TO_BASE_EXTRINSIC + FRONT_CAMERA_EXTRINSIC` -> `FrontCameraExtrinsicCalibrationAlgorithm`
- `SENSOR_TO_BASE_EXTRINSIC + DOWNWARD_CAMERA_EXTRINSIC` -> `DownwardCameraExtrinsicCalibrationAlgorithm`
- `SENSOR_TO_BASE_EXTRINSIC + IMU_EXTRINSIC` -> `ImuExtrinsicCalibrationAlgorithm`
- `SENSOR_TO_BASE_EXTRINSIC + LIDAR_2D_EXTRINSIC` -> `Lidar2DExtrinsicCalibrationAlgorithm`
- `SENSOR_TO_BASE_EXTRINSIC + LIDAR_3D_EXTRINSIC` -> `Lidar3DExtrinsicCalibrationAlgorithm`
- `HAND_EYE + EYE_IN_HAND` -> `EyeInHandCalibrationAlgorithm`
- `HAND_EYE + EYE_TO_HAND` -> `EyeToHandCalibrationAlgorithm`

算法实现者优先读取：

- `input.request`
- `input.task_type`
- `input.task_subtype`
- `input.target_sensor_id`
- `input.target_sensor_type`
- `input.camera_intrinsic_task`
- `input.imu_intrinsic_task`
- `input.sensor_to_base_extrinsic_task`
- `input.hand_eye_task`
- `input.required_image_count`
- `input.required_static_segment_count`
- `input.required_motion_segment_count`
- `input.required_sample_count`
- `input.required_pose_count`
- `input.reference_board_id`
- `input.reference_base_frame_id`
- `input.latest_sensor_telemetry`
- `input.sensor_telemetry_history`
- `input.capture_quality_diagnostics`
- `input.storage_diagnostics`
- `input.latest_chassis_telemetry`
- `input.latest_control_telemetry`
- `input.latest_external_localization_telemetry`
- `input.data_window_start_timestamp_us`
- `input.data_window_end_timestamp_us`
- `input.image_sample_files`
- `input.imu_sample_files`
- `input.pointcloud_sample_files`
- `input.laser_scan_sample_files`
- `input.target_detection_files`
- `input.robot_pose_sample_files`
- `input.synchronized_dataset_files`

数据输入要求：

- `front_camera_intrinsic` / `downward_camera_intrinsic` 至少需要 `image_sample_files` 或包含图像列表的 `synchronized_dataset_files`。
- `imu_intrinsic` 至少需要 `imu_sample_files`，并能区分静止段和运动段。
- `front_camera_extrinsic` / `downward_camera_extrinsic` 至少需要图像样本和标定目标检测结果；检测结果可由算法内部生成，也可由 `target_detection_files` 提供。
- `lidar_2d_extrinsic` 至少需要 `laser_scan_sample_files` 和对应靶标/角点/平面检测结果。
- `lidar_3d_extrinsic` 至少需要 `pointcloud_sample_files` 和对应靶标/平面/点云配准输入。
- `imu_extrinsic` 至少需要 `imu_sample_files`，并通常需要底盘运动或外部真值轨迹辅助。
- `hand_eye` 至少需要图像/检测结果和 `robot_pose_sample_files`。
- 如果只存在 `sensor_telemetry_history`，但没有原始图像/IMU/点云/位姿文件，算法只能判断采集状态，不能完成真实传感器标定。

必须回填：

- `output.response.result.validation_summary.reprojection_error_px`
- `output.response.result.validation_summary.translation_residual_m`
- `output.response.result.validation_summary.rotation_residual_rad`
- `output.response.result.validation_summary.plane_residual_m`
- `output.response.result.validation_summary.repeatability_error_m`
- `output.response.result.validation_summary.auto_acceptance_passed`
- `output.response.result.estimated_params`
- `output.response.result.suitable_for_commit`
- `output.response.result.recommended_parameter_version`

典型失败原因：

- 图像帧、IMU 原始包、点云或机械臂位姿数量不足。
- 标定板 ID 不匹配或检测质量不足。
- 图像模糊、过曝、角点不足、点云稀疏、IMU 饱和。
- 采样窗口时间同步不满足。
- 车辆静止条件或运动激励条件不满足。
- 产物目录不可写或中间数据落盘失败。

当前完整 smoke 只跑 `front_camera_intrinsic` 作为 sensor intrinsic 的最小代表链路。它验证的是任务分发、结果回填和 report 汇总，不代表所有传感器子类型都已经完成真实算法验证。

## 报告和冒烟测试要求

模板维护者需要保证每个算法接入后，最终 report 能读到这些信息：

- 阶段是否成功。
- 自动验收是否通过。
- 推荐参数版本。
- 验证摘要关键指标。
- 产物引用。
- 失败原因或阻塞原因。

当前完整链路 smoke 命令：

```bash
./run_isaac_real_sim_test.sh --headless --tasks external,chassis,control,sensor_intrinsic
```

该 smoke 的定位是链路和合同验证：

- `external`：验证外部定位遥测、readiness、truth validation、stage result 和 report 汇总。
- `chassis`：gateway 模式下验证底盘任务下发、车端 agent 执行结果返回和 stage result。
- `control`：gateway 模式下验证控制评估任务下发、车端 agent 执行结果返回和 stage result。
- `sensor_intrinsic`：验证 sensor service 的 `front_camera_intrinsic` 模板分发和结果回填。
- `report`：验证 `overall_success`、各阶段结果、自动验收字段和 `/workshop_v2/get_report`。

smoke 通过不代表真实算法精度达标；真实算法合入后还要补对应模块的单元测试、数据回放测试和现场低速验收。

## 新增算法子类型流程

如果要新增算法类型或子类型，按这个顺序修改：

1. 在对应接口包里新增 task type / subtype 枚举。
2. 在对应 `Input` 结构里补充任务 payload 或展开字段。
3. 在 service node 中把 request payload 展开到 `Input`。
4. 在对应 `Input` 结构里补充数据文件引用字段，或复用已有 `FileReference` 字段。
5. 在 service node 中把采集产物、数据窗口和 telemetry history 填入 `Input`。
6. 在 `*_algorithm_template.cpp` 中新增分发规则。
7. 新增具体 `*_algorithm.cpp` 文件和类声明。
8. 在 `CMakeLists.txt` 中加入新源文件。
9. 在 smoke 中新增最小任务用例。
10. 在本文档中补充分发关系、数据输入要求和输出要求。

## 交付检查清单

算法实现者提交前至少自查：

- 算法只改了约定的算法文件，或已说明必须改接口/模板的原因。
- `run(...)` 的成功和失败路径都能稳定返回。
- 失败路径写了可定位的 `failure_reason`。
- 成功路径填了 `message`、`data_quality_passed`、`suitable_for_commit`、`recommended_parameter_version`。
- `validation_summary` 中和本算法相关的字段不是模板默认值。
- 参数结果写到了对应的 `estimated_*` 或 `result` 字段。
- 产物路径写入 `artifacts`，路径可追溯。
- 所需输入数据来自 `Input` 中的 telemetry history 或 `FileReference`，没有私自读取未声明路径。
- 必需数据为空时返回 `false`，并在 `failure_reason` 中指出缺少哪个输入。
- 不依赖 Isaac 专有 topic 或真实车辆 SDK 专有代码。
- 本地 build 通过。
- 相关 smoke 或回放测试通过。

模板维护者提交前至少自查：

- 新增字段在 node 层被统一填充。
- 所有新增枚举都有模板分发规则。
- 数据窗口、telemetry history 和 `FileReference` 产物引用有明确来源。
- 旧 smoke 不被破坏。
- report 能看到新增算法的关键结果。
- 文档中的文件路径、字段名和代码一致。
