# 底盘标定现场数据模板

这个目录只处理底盘标定的现场数据输入边界，不直接控制车辆。真实车辆运动仍由 Windows 车端 agent 执行动作原语，车间电脑侧负责采集底盘遥测、执行器命令和外部真值轨迹，并把这些文件写入 `dataset_index.yaml`。

## 数据流

```text
Windows 车端 agent 执行动作原语
  -> 底盘遥测 / 执行器命令 / 外部真值轨迹落盘
  -> replay_chassis_smoke.py 或真实采集程序写 dataset_index.yaml
  -> dataset_index_to_site_data_input.py 生成 site_data_input.yaml
  -> chassis_calibration_service 通过 data_input.* 读取算法输入
```

## 数据输入字段

`chassis_calibration_service` 当前读取：

- `data_input.chassis_motion_data_files`：底盘遥测，例如里程计、速度、角速度、故障码。
- `data_input.actuator_command_files`：执行器命令和反馈，例如目标速度、目标转角、制动命令。
- `data_input.truth_trajectory_files`：外部真值轨迹，用于和车端里程计对齐。

三类 CSV 的固定字段、单位、时间基和坐标系见 `chassis_csv_contract.md`。现场采集和离线自检脚本都会按这个协议校验列名和基础类型。

## 底盘类型

当前底盘标定保留四类入口：

- 阿克曼底盘标定：模板入口保留，方便继续复用当前 Isaac 小车和现场兼容配置。
- 差速轮底盘标定：已确认标定目标为左轮半径、右轮半径、驱动轮轮距。
- 单舵轮底盘标定：算法模板已预留，具体标定目标由算法实现方补充。
- 多舵轮底盘标定：算法模板已预留，具体标定目标由算法实现方补充。

## 动作原语

`chassis_calibration_service` 的通用入口已经支持这些动作原语的字段合法性校验：

- `straight_line`
- `arc`
- `in_place_rotation`
- `steering_sweep`
- `lateral_translation`
- `diagonal_motion`
- `module_alignment`
- `coordinated_steering`

通用入口只检查字段是否可用，具体“某类底盘需要哪些动作组合”和“数据是否足够求解”由四个底盘专属算法模板继续判断。

## 现场动作 profile

动作序列模板见：

```text
src/site_deployment/workshop_chassis_calibration_real/config/chassis_action_profile.yaml
```

这个 profile 固定了四类底盘在真实标定车间中建议执行的动作序列、速度、距离、超时和采集要求。现场部署时需要先按工位尺寸、安全速度、真实模块 ID 修改它，再用工具校验：

```bash
python3 src/site_deployment/workshop_chassis_calibration_real/chassis_action_profile.py \
  --profile /data/agv_calib/site_a/chassis_action_profile.yaml
```

导出某类底盘的总控任务片段：

```bash
python3 src/site_deployment/workshop_chassis_calibration_real/chassis_action_profile.py \
  --profile /data/agv_calib/site_a/chassis_action_profile.yaml \
  --chassis-type differential \
  --format requested_tasks \
  --output /data/agv_calib/site_a/chassis_requested_tasks.yaml
```

导出的 `requested_tasks` 字段和总控里的 `RequestedCalibrationTask` 对齐。后续接入总控时，可以直接把这些动作拆成多个底盘阶段，而不是只跑一个默认直线阶段。

总控 smoke 已支持直接读取这个 profile：

```bash
python3 src/simulation/tools/smoke_test_workshop_orchestrator.py \
  --tasks external,chassis,control,sensor_intrinsic \
  --chassis-profile-type ackermann \
  --chassis-action-profile src/site_deployment/workshop_chassis_calibration_real/config/chassis_action_profile.yaml
```

不传 `--chassis-action-profile` 时，smoke 仍保持旧行为，只生成一个最小直线底盘阶段。

现场 profile 也可以统一生成总控会话配置：

```bash
python3 src/deployment/tools/build_workshop_session_config.py \
  --site-profile src/deployment/profiles/site_template.yaml \
  --tasks external,chassis,control,sensor_intrinsic \
  --chassis-type differential \
  --chassis-action-profile src/site_deployment/workshop_chassis_calibration_real/config/chassis_action_profile.yaml \
  --control-evaluation-profile src/site_deployment/workshop_control_calibration_real/config/control_evaluation_profile.yaml \
  --output /data/agv_calib/site_a/session_001/workshop_session_config.yaml
```

## 按动作 profile 执行并采集

现场需要边执行底盘动作边采集数据时，使用：

```bash
source /opt/ros/humble/setup.bash
source install/setup.bash

python3 src/site_deployment/workshop_chassis_calibration_real/run_chassis_profile_capture.py \
  --config /data/agv_calib/site_a/chassis_data.yaml \
  --action-profile /data/agv_calib/site_a/chassis_action_profile.yaml \
  --chassis-type differential \
  --session-id session_001 \
  --site-id site_a \
  --vehicle-id AGV-001 \
  --session-dir /data/agv_calib/site_a/session_001 \
  --dataset-index-path /data/agv_calib/site_a/session_001/dataset_index.yaml \
  --chassis-telemetry-topic /chassis/telemetry \
  --external-pose-topic /workshop/external_localization/vehicle/pose
```

这个入口会：

- 打开三类 CSV 采集文件。
- 按动作 profile 顺序向 `/chassis/execute_motion_primitive` 发送动作。
- 动作前后按 profile 配置保留采集缓冲时间。
- 命令话题不可用时，`command_source=auto` 会按动作 profile 写计划命令行。
- 全部动作结束后统一写 `dataset_index.yaml`。

本地最小 smoke：

```bash
source /opt/ros/humble/setup.bash
source install/setup.bash

python3 src/site_deployment/workshop_chassis_calibration_real/smoke_test_chassis_profile_capture.py \
  --chassis-type ackermann
```

这个 smoke 会启动假的底盘 action server、底盘遥测和外部真值发布器，然后调用 `run_chassis_profile_capture.py` 验证 CSV 和 `dataset_index.yaml` 写入链路。

## 待提交参数包

真实算法输出参数后，先生成待审批/待提交参数包，不在这里假装已经写入车端：

```bash
python3 src/site_deployment/workshop_chassis_calibration_real/stage_chassis_parameter_commit.py \
  --dataset-index /data/agv_calib/site_a/session_001/dataset_index.yaml \
  --estimated-params /data/agv_calib/site_a/session_001/chassis/estimated_params.yaml \
  --operator-id operator_001 \
  --previous-parameter-version current_vehicle_chassis_v1
```

输出默认写到：

```text
/data/agv_calib/site_a/session_001/chassis/pending_chassis_commit.yaml
```

这个文件记录参数版本、参数摘要、数据集索引、人工审批状态、持久化写入意图和回滚引用。真正写入车端仍由后续真实车端提交实现负责。

会话收尾时也可以直接走统一入口。该入口会先生成 `site_data_input.yaml`，如果发现 `chassis/estimated_params.yaml` 或显式传入 `--chassis-estimated-params`，会自动生成 `pending_chassis_commit.yaml` 并把底盘待提交信息写回 `dataset_index.yaml`：

```bash
python3 src/deployment/tools/finalize_site_session.py \
  --site-profile /data/agv_calib/site_a/site.yaml \
  --session-dir /data/agv_calib/site_a/session_001 \
  --dataset-index /data/agv_calib/site_a/session_001/dataset_index.yaml \
  --chassis-estimated-params /data/agv_calib/site_a/session_001/chassis/estimated_params.yaml \
  --chassis-operator-id operator_001 \
  --previous-chassis-parameter-version current_vehicle_chassis_v1
```

总控 report 会从 `dataset_index.yaml` 的底盘待提交元数据中自动加入：

- `chassis_estimated_params_file`
- `chassis_pending_commit_file`
- `chassis_pending_commit_state`
- `chassis_pending_parameter_version`
- `chassis_pending_commit_approval_required`
- `chassis_pending_commit_approved`

人工确认后，车间电脑侧先生成“已审批参数交接包”。这一步仍然不调用车端写参服务，只把后续车端适配器需要消费的内容固定下来：

```bash
python3 src/site_deployment/workshop_chassis_calibration_real/approve_chassis_pending_parameters.py \
  /data/agv_calib/site_a/session_001/chassis/pending_chassis_commit.yaml \
  --operator-id operator_001
```

这条命令会校验 `estimated_params.yaml` 的 sha256 摘要，标记人工审批通过，生成：

```text
/data/agv_calib/site_a/session_001/chassis/approved_chassis_parameter_handoff.yaml
```

该交接包是车间电脑侧产物，记录参数版本、车辆 ID、审批人、sha256 摘要、待写入参数和车端后续需要实现的消费合同。真实车端写入、持久化和失败回滚先不在这里实现。

## 离线自检

复制模板后填写真实 `session_id/site_id/vehicle_id/session_dir/dataset_index_path`，或用命令行覆盖：

```bash
python3 src/site_deployment/workshop_chassis_calibration_real/replay_chassis_smoke.py \
  --config src/site_deployment/workshop_chassis_calibration_real/config/chassis_data_template.yaml \
  --session-id session_001 \
  --site-id site_a \
  --vehicle-id AGV-001 \
  --session-dir /tmp/agv_calib_chassis/session_001 \
  --dataset-index-path /tmp/agv_calib_chassis/session_001/dataset_index.yaml
```

不传真实 CSV 时，脚本会生成一段最小合成数据，只用于验证索引和参数注入链路。现场使用真实数据时：

```bash
python3 src/site_deployment/workshop_chassis_calibration_real/replay_chassis_smoke.py \
  --config /data/agv_calib/site_a/chassis_data.yaml \
  --chassis-motion-csv /data/raw/chassis_motion.csv \
  --actuator-command-csv /data/raw/actuator_commands.csv \
  --truth-trajectory-csv /data/raw/truth_trajectory.csv \
  --no-synthetic
```

随后可运行：

```bash
python3 src/deployment/tools/validate_dataset_index.py \
  /tmp/agv_calib_chassis/session_001/dataset_index.yaml \
  --tasks chassis \
  --require-existing-files

python3 src/deployment/tools/dataset_index_to_site_data_input.py \
  /tmp/agv_calib_chassis/session_001/dataset_index.yaml \
  --strict
```

## 现场采集落盘

现场运行时使用 `capture_chassis_session.py` 采集三类数据：

```bash
source /opt/ros/humble/setup.bash
source install/setup.bash

python3 src/site_deployment/workshop_chassis_calibration_real/capture_chassis_session.py \
  --config /data/agv_calib/site_a/chassis_data.yaml \
  --session-id session_001 \
  --site-id site_a \
  --vehicle-id AGV-001 \
  --session-dir /data/agv_calib/site_a/session_001 \
  --dataset-index-path /data/agv_calib/site_a/session_001/dataset_index.yaml \
  --chassis-telemetry-topic /chassis/telemetry \
  --external-pose-topic /workshop/external_localization/vehicle/pose \
  --ackermann-command-topic /vehicle/AGV-001/internal/ackermann_cmd \
  --duration-sec 60
```

输出文件：

- `chassis/chassis_motion.csv`：底盘遥测。
- `chassis/actuator_commands.csv`：执行器命令。命令话题没有数据时，`command_source=auto` 会写入一条计划命令，避免数据索引缺失。
- `external/truth_trajectory.csv`：外部真值轨迹。
- `chassis/chassis_diagnostics.json`：采集摘要和检查结果。
- `dataset_index.yaml`：给后续 `site_data_input.yaml` 转换使用的数据索引。

## 算法填充边界

详细算法合同见 `src/docs/chassis_calibration_algorithm_contract.md`。

底盘算法同事主要填这些 C++ 文件：

- `ackermann_chassis_algorithm.cpp`
- `differential_chassis_algorithm.cpp`
- `single_steer_wheel_chassis_algorithm.cpp`
- `multi_steer_wheel_chassis_algorithm.cpp`

这些文件的输入已经包含 `chassis_motion_data_files`、`actuator_command_files`、`truth_trajectory_files`，后续可以在算法里按车型读取对应文件并写回 `estimated_params` 和 `validation_summary`。
