feat:完善结构与仿真环境
This commit is contained in:
+38
-120
@@ -1,141 +1,59 @@
|
||||
# 🧠 AGV 标定中央大脑
|
||||
# 源码目录结构
|
||||
|
||||
**环境**: Ubuntu 22.04 + ROS 2 Humble | **语言**: C++ | **通信**: gRPC over Wi-Fi 6
|
||||
`src` 按“仿真、核心逻辑、通信、现场部署”的边界组织,而不是按临时实验文件组织。
|
||||
|
||||
> 标定车间的"发令大脑",通过局域网跨平台遥控 Windows 车端执行动作并拉取遥测数据。
|
||||
## 目录说明
|
||||
|
||||
---
|
||||
- `apps/`
|
||||
面向操作人员的工具和界面原型。
|
||||
|
||||
## 📋 目录
|
||||
- `communication/`
|
||||
ROS 2 接口包、TCP 帧协议、车间工控机到车端电脑的 gateway。这个层应该同时服务于仿真和现场部署。
|
||||
|
||||
1. [系统依赖安装](#1-系统依赖一键安装)
|
||||
2. [VS Code 插件配置](#2-vs-code-核心插件配置)
|
||||
3. [解决 IntelliSense 报错](#3-解决-vs-code-红色波浪线)
|
||||
4. [编译与运行](#4-编译与运行)
|
||||
- `core/`
|
||||
标定流程和标定算法,包括 `workshop_orchestrator`、底盘标定、运控标定、传感器标定、车辆参数管理等可复用核心逻辑。
|
||||
|
||||
---
|
||||
- `simulation/`
|
||||
部署前仿真验证代码。Isaac 车间、仿真车辆、仿真车端 agent、仿真传感器、仿真标定靶和旧版仿真包都放在这里。
|
||||
|
||||
## 1. 系统依赖一键安装
|
||||
- `deployment/`
|
||||
部署 profile 和从仿真迁移到现场前的检查清单。这里放配置基准,不放算法实现。
|
||||
|
||||
在 Ubuntu 22.04 终端执行以下命令:
|
||||
- `docs/`
|
||||
源码树内的设计说明、边界说明和迁移规则。
|
||||
|
||||
- `site_deployment/`
|
||||
真实现场部署代码,例如真实车端电脑适配器、真实车辆 SDK、PLC/CAN 或厂商控制器对接代码。
|
||||
|
||||
## 边界规则
|
||||
|
||||
- Isaac API 只放在 `simulation/`。
|
||||
- 真实车辆 SDK、PLC、CAN、厂商控制器相关代码只放在 `site_deployment/`。
|
||||
- ROS 2 接口、TCP 协议和 gateway 放在 `communication/`。
|
||||
- 编排流程和标定算法放在 `core/`。
|
||||
- 部署 profile 放在 `deployment/`。
|
||||
- 设计说明和迁移边界说明放在 `docs/`。
|
||||
|
||||
## 主要入口
|
||||
|
||||
Isaac 车间仿真:
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y build-essential cmake pkg-config gdb
|
||||
sudo apt install -y protobuf-compiler-grpc libgrpc++-dev libprotobuf-dev protobuf-compiler
|
||||
python3 src/simulation/isaac_workshop_sim/scripts/build_calibration_room.py
|
||||
```
|
||||
|
||||
> ⚠️ **警告**: 严禁自行去 GitHub 源码编译 gRPC,直接使用 Ubuntu 官方 APT 源即可,避免浪费时间与报错。
|
||||
|
||||
---
|
||||
|
||||
## 2. VS Code 核心插件配置
|
||||
|
||||
打开 VS Code → 扩展商店 (Extensions),**必须安装**以下 4 个插件:
|
||||
|
||||
| 插件名称 | 开发者 | 用途 |
|
||||
|---------|--------|------|
|
||||
| **C/C++** | Microsoft | 代码补全与 GDB 调试 |
|
||||
| **CMake Tools** | Microsoft | 底部快速构建状态栏 |
|
||||
| **ROS** | Microsoft | 自动识别 `colcon` 工作空间 |
|
||||
| **vscode-proto3** | zxh404 | `.proto` 文件语法高亮 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 解决 VS Code 红色波浪线 (IntelliSense 报错)
|
||||
|
||||
**问题原因**: gRPC 生成的 `.pb.h` 文件在 `colcon build` 阶段动态生成于 `build/` 目录,VS Code 初始无法识别。
|
||||
|
||||
**修复步骤**:
|
||||
|
||||
1. 按 `Ctrl+Shift+P` → 输入 `C/C++: Edit Configurations (JSON)`
|
||||
2. 确保 `c_cpp_properties.json` 包含以下配置:
|
||||
|
||||
```json
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ROS2",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"/opt/ros/humble/include/**",
|
||||
"${workspaceFolder}/build/agv_calib_brain/grpc_gen/**"
|
||||
],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "linux-gcc-x64"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> 💡 **提示**: `grpc_gen` 是 CMakeLists 中配置的自动生成源码路径,请根据实际情况微调。
|
||||
|
||||
---
|
||||
|
||||
## 4. 编译与运行 (CMake 自动化)
|
||||
|
||||
### 4.0 最小联调闭环
|
||||
|
||||
当前仓库已经补齐了一个最小可运行闭环:
|
||||
|
||||
- `vehicle_profile_manager`:提供默认车辆画像
|
||||
- `external_localization_service`:提供外部真值校核的最小执行端
|
||||
- `workshop_orchestrator_v2`:负责编排会话、计划和报告
|
||||
|
||||
启动顺序:
|
||||
仿真车端 agent:
|
||||
|
||||
```bash
|
||||
source /opt/ros/humble/setup.bash
|
||||
source install/setup.bash
|
||||
ros2 launch win_ubuntu_bridge minimal_workshop_demo.launch.py
|
||||
python3 src/simulation/vehicle_agent_sim/scripts/isaac_vehicle_agent_sim.py
|
||||
```
|
||||
|
||||
如果只想单独跑编排器:
|
||||
按 profile 启动完整仿真链路:
|
||||
|
||||
```bash
|
||||
ros2 launch workshop_orchestrator_v2 workshop_orchestrator_v2.launch.py
|
||||
python3 src/simulation/tools/launch_sim_stack.py
|
||||
```
|
||||
|
||||
可先调用这些接口做联调:
|
||||
这条链路中,车间工控机与车端电脑之间的底盘、运控、外部真值位姿和传感器数据都通过 TCP/WiFi6 仿真边界传输;Isaac topic 只留在仿真内部。
|
||||
|
||||
- `/vehicle_profile_manager/get_vehicle_profile`
|
||||
- `/vehicle_profile_manager/evaluate_vehicle_calibration_applicability`
|
||||
- `/external_localization/get_readiness`
|
||||
- `/external_localization/execute_task`
|
||||
- `/workshop_v2/create_session`
|
||||
- `/workshop_v2/execute_session`
|
||||
- `/workshop_v2/get_report`
|
||||
|
||||
最小会话建议至少包含:
|
||||
|
||||
- `session.config.localization_source_id = demo_vehicle_001`
|
||||
- `session.config.workcell_zone_id = demo_workcell`
|
||||
- 一个 `requested_tasks`,其中 `stage_type = EXTERNAL_REFERENCE_READY_CHECK_STAGE`
|
||||
- 该任务的 `task_params` 至少包含:
|
||||
- `external.static_sample_count`
|
||||
- `external.dynamic_sample_count`
|
||||
- `external.max_position_stddev_m`
|
||||
- `external.max_yaw_stddev_rad`
|
||||
- `external.max_tracking_loss_ratio`
|
||||
- `external.max_time_sync_offset_ms`
|
||||
- `external.timeout_sec`
|
||||
|
||||
|
||||
> ✨ **无需手动执行 `protoc`** —— CMakeLists.txt 已配置自动化脚本,编译时自动生成 C++ 网络源码。
|
||||
|
||||
### 4.1 编译
|
||||
|
||||
```bash
|
||||
# 回到工作空间根目录(如 ~/agv_ws)
|
||||
source /opt/ros/humble/setup.bash
|
||||
colcon build --packages-select agv_calib_brain --symlink-install
|
||||
```
|
||||
|
||||
### 4.2 运行
|
||||
|
||||
```bash
|
||||
source install/setup.bash
|
||||
ros2 run agv_calib_brain brain_node
|
||||
```
|
||||
车间 gateway 和 `workshop_orchestrator` 仍然按 ROS 2 包名启动;源码分别在 `communication/` 和 `core/` 下。
|
||||
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
#include "chassis_calibration_service/chassis_calibration_common.hpp"
|
||||
|
||||
namespace chassis_calibration_service
|
||||
{
|
||||
|
||||
bool DifferentialChassisAlgorithm::run(
|
||||
const ChassisCalibrationInput & input,
|
||||
ChassisCalibrationOutput & output,
|
||||
std::string & failure_reason) const
|
||||
{
|
||||
(void)failure_reason;
|
||||
|
||||
// TODO: 在这里填写差速底盘标定算法。
|
||||
// 算法工程师应从这里读取并计算:
|
||||
// - 任务请求:input.request(request_id、任务目的、selected_primitive、straight_line 参数、timeout 等)
|
||||
// - 底盘反馈:由上层扩展到 ChassisCalibrationInput 中的实时数据(轮速、左右电机反馈、IMU、里程计、定位等)
|
||||
// - 输出:output.response.result(success、error_code、validation_summary、estimated_params、artifacts 等)
|
||||
fill_common_result(input, output);
|
||||
output.response.result.message = "differential chassis template executed.";
|
||||
output.response.result.recommended_parameter_version = "differential_template_v1";
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace chassis_calibration_service
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
#include "control_calibration_service/control_calibration_common.hpp"
|
||||
|
||||
namespace control_calibration_service
|
||||
{
|
||||
|
||||
bool PurePursuitControlCalibrationAlgorithm::run(
|
||||
const ControlCalibrationInput & input,
|
||||
ControlCalibrationOutput & output,
|
||||
std::string & failure_reason) const
|
||||
{
|
||||
(void)failure_reason;
|
||||
|
||||
// Pure Pursuit 控制标定模板:
|
||||
// 适合基于参考轨迹和前视点策略的横向控制评估。
|
||||
// 算法工程师通常会在这里处理:
|
||||
// 1. 参考轨迹读取:
|
||||
// - input.reference_trajectory
|
||||
// - input.reference_stop_at_end / input.reference_timeout_sec
|
||||
// 2. 观测输入:
|
||||
// - input.control_telemetry_history 中的横向误差、航向误差、转向输出
|
||||
// - input.chassis_telemetry_history 中的速度、姿态和底盘运动状态
|
||||
// - input.truth_source_diagnostics / 真值历史,用于判断轨迹对齐是否可靠
|
||||
// 3. 参数输出:
|
||||
// - 可将前视距离、速度相关增益等写入 output.response.result.estimated_parameter_set
|
||||
// 4. 验收输出:
|
||||
// - 将最大误差、均方误差、振荡情况、自动验收结论写入 validation_summary
|
||||
fill_common_result(input, output);
|
||||
output.response.result.message = "pure pursuit control calibration template executed.";
|
||||
output.response.result.recommended_parameter_version = "pure_pursuit_template_v1";
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace control_calibration_service
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
#include "external_localization_service/external_localization_service_node.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "calibration_common_interfaces/msg/error_code.hpp"
|
||||
#include "calibration_common_interfaces/msg/job_state.hpp"
|
||||
|
||||
namespace external_localization_service
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
int64_t now_us()
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
using calibration_common_interfaces::msg::ErrorCode;
|
||||
using calibration_common_interfaces::msg::JobState;
|
||||
|
||||
ExternalLocalizationServiceNode::ExternalLocalizationServiceNode(const rclcpp::NodeOptions & options)
|
||||
: Node("external_localization_service", options)
|
||||
{
|
||||
readiness_service_ = create_service<ReadinessSrv>(
|
||||
"/external_localization/get_readiness",
|
||||
std::bind(&ExternalLocalizationServiceNode::handle_readiness, this, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
execute_task_action_server_ = rclcpp_action::create_server<ExecuteTask>(
|
||||
this,
|
||||
"/external_localization/execute_task",
|
||||
std::bind(&ExternalLocalizationServiceNode::handle_goal, this, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&ExternalLocalizationServiceNode::handle_cancel, this, std::placeholders::_1),
|
||||
std::bind(&ExternalLocalizationServiceNode::handle_accepted, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void ExternalLocalizationServiceNode::handle_readiness(
|
||||
const std::shared_ptr<ReadinessSrv::Request> request,
|
||||
std::shared_ptr<ReadinessSrv::Response> response)
|
||||
{
|
||||
(void)request;
|
||||
|
||||
// 这里先保留最小 readiness 逻辑。
|
||||
// 后续若接入真实外部定位设备/真值源桥接程序,可在这里增加:
|
||||
// - 真值源在线检查
|
||||
// - 同步状态检查
|
||||
// - 覆盖范围检查
|
||||
// - 观测质量检查
|
||||
// - 切源稳定性检查
|
||||
response->response.success = true;
|
||||
response->response.error_code.code = ErrorCode::OK;
|
||||
response->response.message = "external_localization_service is ready.";
|
||||
response->response.agent_ready = true;
|
||||
response->response.ready_for_reference_validation = true;
|
||||
response->response.checked_timestamp_us = now_us();
|
||||
response->response.validation_summary.time_sync_ok = true;
|
||||
response->response.validation_summary.coverage_ok = true;
|
||||
response->response.validation_summary.quality_ok = true;
|
||||
response->response.validation_summary.tracking_stable = true;
|
||||
response->response.validation_summary.recommended_as_truth_source = true;
|
||||
response->response.validation_summary.position_stddev_m = 0.0;
|
||||
response->response.validation_summary.yaw_stddev_rad = 0.0;
|
||||
response->response.validation_summary.tracking_loss_ratio = 0.0;
|
||||
response->response.validation_summary.time_sync_offset_ms = 0.0;
|
||||
}
|
||||
|
||||
rclcpp_action::GoalResponse ExternalLocalizationServiceNode::handle_goal(
|
||||
const rclcpp_action::GoalUUID & /*uuid*/,
|
||||
std::shared_ptr<const ExecuteTask::Goal> goal)
|
||||
{
|
||||
std::string reject_reason;
|
||||
if (!executor_.validate_goal(*goal, reject_reason)) {
|
||||
RCLCPP_WARN(get_logger(), "Reject external_localization goal: %s", reject_reason.c_str());
|
||||
return rclcpp_action::GoalResponse::REJECT;
|
||||
}
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
|
||||
}
|
||||
|
||||
rclcpp_action::CancelResponse ExternalLocalizationServiceNode::handle_cancel(
|
||||
const std::shared_ptr<GoalHandleExecuteTask> /*goal_handle*/)
|
||||
{
|
||||
return rclcpp_action::CancelResponse::ACCEPT;
|
||||
}
|
||||
|
||||
void ExternalLocalizationServiceNode::handle_accepted(
|
||||
const std::shared_ptr<GoalHandleExecuteTask> goal_handle)
|
||||
{
|
||||
std::thread(std::bind(&ExternalLocalizationServiceNode::execute_goal, this, goal_handle)).detach();
|
||||
}
|
||||
|
||||
void ExternalLocalizationServiceNode::execute_goal(
|
||||
const std::shared_ptr<GoalHandleExecuteTask> goal_handle)
|
||||
{
|
||||
auto feedback = std::make_shared<ExecuteTask::Feedback>();
|
||||
feedback->feedback.job_id = goal_handle->get_goal()->goal.header.request_id;
|
||||
feedback->feedback.state.state = JobState::RUNNING;
|
||||
feedback->feedback.progress = 0.5;
|
||||
feedback->feedback.error_code.code = ErrorCode::OK;
|
||||
feedback->feedback.message = "external localization task is running.";
|
||||
feedback->feedback.server_timestamp_us = now_us();
|
||||
feedback->feedback.safe_to_retry = false;
|
||||
goal_handle->publish_feedback(feedback);
|
||||
|
||||
auto result = std::make_shared<ExecuteTask::Result>();
|
||||
std::string failure_reason;
|
||||
if (!executor_.build_result(*goal_handle->get_goal(), *result, failure_reason)) {
|
||||
result->result.success = false;
|
||||
result->result.error_code.code = ErrorCode::INVALID_ARGUMENT;
|
||||
result->result.message = failure_reason;
|
||||
goal_handle->abort(result);
|
||||
return;
|
||||
}
|
||||
|
||||
goal_handle->succeed(result);
|
||||
}
|
||||
|
||||
} // namespace external_localization_service
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
#include "external_localization_service/external_localization_common.hpp"
|
||||
|
||||
namespace external_localization_service
|
||||
{
|
||||
|
||||
bool MarkerAlignmentAlgorithm::run(
|
||||
const ExternalLocalizationInput & input,
|
||||
ExternalLocalizationOutput & output,
|
||||
std::string & failure_reason) const
|
||||
{
|
||||
(void)failure_reason;
|
||||
|
||||
// 标靶对齐模板:
|
||||
// 【本文件负责什么】
|
||||
// - 负责标靶检测结果读取、坐标系对齐求解和残差统计相关算法实现。
|
||||
// - 后续算法工程师应主要修改本文件,不要改 node 层和模板分发层。
|
||||
//
|
||||
// 【建议优先读取的输入】
|
||||
// 1. input.marker_alignment_task
|
||||
// - target_board_id、min_valid_observation_count、timeout_sec 等关键约束。
|
||||
// 2. input.latest_external_localization_telemetry / input.external_localization_telemetry_history
|
||||
// - 读取观测位姿、标准差、丢失率、时间同步偏差和质量评分。
|
||||
// 3. input.marker_alignment_diagnostics
|
||||
// - 读取标靶检测失败、角点不足、姿态求解不稳定等问题描述。
|
||||
// 4. input.sensor_quality_diagnostics
|
||||
// - 如果标靶检测依赖相机 / LiDAR 质量,可在这里读取辅助质量信息。
|
||||
//
|
||||
// 【必写输出】
|
||||
// 1. output.response.result.result.workshop_to_localization
|
||||
// - 这是标靶对齐最核心的输出结果。
|
||||
// 2. output.response.result.result.residual_error_m / residual_error_rad
|
||||
// - 写回对齐残差,供 orchestrator 判断是否自动验收。
|
||||
// 3. output.response.result.validation_summary
|
||||
// - 写位置标准差、航向标准差、时间同步偏差等摘要。
|
||||
//
|
||||
// 【可选输出】
|
||||
// - output.response.result.artifacts
|
||||
// 可挂标靶检测日志、可视化结果、拟合报告、残差统计文件等。
|
||||
//
|
||||
// 【常见失败原因】
|
||||
// - 有效观测数不足、标靶检测失败、姿态求解不稳定、时间同步异常、质量评分过低。
|
||||
//
|
||||
// 【在这里添加真实算法】
|
||||
// - 请在 fill_external_localization_common_success(...) 之前或之后补充真实对齐求解逻辑。
|
||||
// - 当前文件仅提供交付模板,不包含真实外部定位算法。
|
||||
|
||||
fill_external_localization_common_success(
|
||||
input,
|
||||
output,
|
||||
"标靶对齐完成。",
|
||||
"demo_external_marker_alignment_v1");
|
||||
|
||||
output.response.result.result.workshop_frame_id = "workshop";
|
||||
output.response.result.result.localization_frame_id = "localization";
|
||||
output.response.result.result.workshop_to_localization.z_m = 0.0;
|
||||
output.response.result.result.position_repeatability_m = 0.0;
|
||||
output.response.result.result.yaw_repeatability_rad = 0.0;
|
||||
output.response.result.result.residual_error_m = 0.0;
|
||||
output.response.result.result.residual_error_rad = 0.0;
|
||||
output.response.result.result.tracking_loss_ratio = 0.0;
|
||||
output.response.result.result.time_sync_offset_ms = 0.0;
|
||||
output.response.result.result.validated_as_truth_source = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace external_localization_service
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
#include "external_localization_service/external_localization_common.hpp"
|
||||
|
||||
namespace external_localization_service
|
||||
{
|
||||
|
||||
bool ReferencePoseCollectionAlgorithm::run(
|
||||
const ExternalLocalizationInput & input,
|
||||
ExternalLocalizationOutput & output,
|
||||
std::string & failure_reason) const
|
||||
{
|
||||
(void)failure_reason;
|
||||
|
||||
// 参考位姿采集模板:
|
||||
// 【本文件负责什么】
|
||||
// - 负责参考位姿采集与静态重复性分析相关算法实现。
|
||||
// - 后续算法工程师应主要修改本文件,不要改 node 层和模板分发层。
|
||||
//
|
||||
// 【建议优先读取的输入】
|
||||
// 1. input.reference_pose_collection_task
|
||||
// - sample_count、require_vehicle_static、timeout_sec、min_sample_interval_sec 等采样约束。
|
||||
// 2. input.latest_external_localization_telemetry / input.external_localization_telemetry_history
|
||||
// - 每帧外部定位位姿、位置标准差、航向标准差、时间同步偏差、质量评分。
|
||||
// 3. input.latest_chassis_telemetry / input.chassis_telemetry_history
|
||||
// - 用于判断车辆是否真实静止,避免采入无效参考位姿。
|
||||
// 4. input.truth_source_diagnostics / input.acquisition_diagnostics
|
||||
// - 记录时间同步异常、观测缺失、采样不足、落盘失败等问题。
|
||||
//
|
||||
// 【必写输出】
|
||||
// 1. output.response.result.validation_summary
|
||||
// - 写位置标准差、航向标准差、时间同步偏差、是否推荐为真值源等摘要。
|
||||
// 2. output.response.result.result
|
||||
// - 写 workshop_frame_id / localization_frame_id / repeatability / residual 等结果。
|
||||
// 3. output.response.result.suitable_for_commit
|
||||
// - 明确当前采集结果是否建议进入下一阶段。
|
||||
//
|
||||
// 【可选输出】
|
||||
// - output.response.result.artifacts
|
||||
// 可挂采样日志、原始位姿文件、统计报告等文件引用。
|
||||
//
|
||||
// 【常见失败原因】
|
||||
// - 车辆未静止、有效样本不足、时间同步超标、外部定位观测丢失、采样频率不足。
|
||||
//
|
||||
// 【在这里添加真实算法】
|
||||
// - 请在 fill_external_localization_common_success(...) 之前或之后补充真实采样与统计逻辑。
|
||||
// - 当前文件仅提供交付模板,不包含真实外部定位算法。
|
||||
|
||||
fill_external_localization_common_success(
|
||||
input,
|
||||
output,
|
||||
"参考位姿采集完成。",
|
||||
"demo_external_reference_pose_collection_v1");
|
||||
|
||||
output.response.result.result.workshop_frame_id = "workshop";
|
||||
output.response.result.result.localization_frame_id = "localization";
|
||||
output.response.result.result.position_repeatability_m = 0.0;
|
||||
output.response.result.result.yaw_repeatability_rad = 0.0;
|
||||
output.response.result.result.residual_error_m = 0.0;
|
||||
output.response.result.result.residual_error_rad = 0.0;
|
||||
output.response.result.result.tracking_loss_ratio = 0.0;
|
||||
output.response.result.result.time_sync_offset_ms = 0.0;
|
||||
output.response.result.result.validated_as_truth_source = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace external_localization_service
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
#include "external_localization_service/external_localization_common.hpp"
|
||||
|
||||
namespace external_localization_service
|
||||
{
|
||||
|
||||
bool TruthSourceValidationAlgorithm::run(
|
||||
const ExternalLocalizationInput & input,
|
||||
ExternalLocalizationOutput & output,
|
||||
std::string & failure_reason) const
|
||||
{
|
||||
(void)failure_reason;
|
||||
|
||||
// 真值源验证模板:
|
||||
// 【本文件负责什么】
|
||||
// - 负责静态重复性、动态稳定性、时间同步与丢失率等真值源验证算法实现。
|
||||
// - 后续算法工程师应主要修改本文件,不要改 node 层和模板分发层。
|
||||
//
|
||||
// 【建议优先读取的输入】
|
||||
// 1. input.truth_source_validation_task
|
||||
// - static_sample_count、dynamic_sample_count、require_short_motion_segment 等任务要求。
|
||||
// 2. input.external_localization_telemetry_history
|
||||
// - 外部定位历史观测窗口,是稳定性、同步性、重复性分析的核心输入。
|
||||
// 3. input.chassis_telemetry_history / input.control_telemetry_history
|
||||
// - 如果要求短运动段验证,需要结合车辆实际运动状态和控制输出做时序对齐。
|
||||
// 4. input.sensor_telemetry_history
|
||||
// - 用于判断辅助传感器质量是否影响外部定位观测可信度。
|
||||
// 5. input.truth_source_diagnostics
|
||||
// - 记录时间同步超标、观测丢失、切源异常等问题。
|
||||
//
|
||||
// 【必写输出】
|
||||
// 1. output.response.result.validation_summary
|
||||
// - 这是 orchestrator 自动验收最关键的摘要区域。
|
||||
// 2. output.response.result.result
|
||||
// - 写 repeatability、tracking_loss_ratio、time_sync_offset_ms 等核心结果。
|
||||
// 3. output.response.result.data_quality_passed / suitable_for_commit
|
||||
// - 明确当前真值源是否可进入后续标定闭环。
|
||||
//
|
||||
// 【可选输出】
|
||||
// - output.response.result.artifacts
|
||||
// 可挂稳定性分析报告、同步统计图、丢失率分析文件等。
|
||||
//
|
||||
// 【常见失败原因】
|
||||
// - 动态窗口不足、时间同步偏差超阈值、观测丢失率过高、重复性不满足要求。
|
||||
//
|
||||
// 【在这里添加真实算法】
|
||||
// - 请在 fill_external_localization_common_success(...) 之前或之后补充真实验证逻辑。
|
||||
// - 当前文件仅提供交付模板,不包含真实外部定位算法。
|
||||
|
||||
fill_external_localization_common_success(
|
||||
input,
|
||||
output,
|
||||
"真值源验证完成。",
|
||||
"demo_external_truth_source_validation_v1");
|
||||
|
||||
output.response.result.validation_summary.time_sync_ok = true;
|
||||
output.response.result.validation_summary.coverage_ok = true;
|
||||
output.response.result.validation_summary.quality_ok = true;
|
||||
output.response.result.validation_summary.tracking_stable = true;
|
||||
output.response.result.validation_summary.recommended_as_truth_source = true;
|
||||
output.response.result.validation_summary.position_stddev_m = 0.0;
|
||||
output.response.result.validation_summary.yaw_stddev_rad = 0.0;
|
||||
output.response.result.validation_summary.tracking_loss_ratio = 0.0;
|
||||
output.response.result.validation_summary.time_sync_offset_ms = 0.0;
|
||||
|
||||
output.response.result.result.workshop_frame_id = "workshop";
|
||||
output.response.result.result.localization_frame_id = "localization";
|
||||
output.response.result.result.position_repeatability_m = 0.0;
|
||||
output.response.result.result.yaw_repeatability_rad = 0.0;
|
||||
output.response.result.result.residual_error_m = 0.0;
|
||||
output.response.result.result.residual_error_rad = 0.0;
|
||||
output.response.result.result.tracking_loss_ratio = 0.0;
|
||||
output.response.result.result.time_sync_offset_ms = 0.0;
|
||||
output.response.result.result.validated_as_truth_source = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace external_localization_service
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
#include "sensor_calibration_service/sensor_calibration_service_node.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "calibration_common_interfaces/msg/error_code.hpp"
|
||||
#include "calibration_common_interfaces/msg/job_state.hpp"
|
||||
#include "calibration_sensor_interfaces/msg/sensor_calibration_job_result.hpp"
|
||||
#include "calibration_sensor_interfaces/msg/sensor_readiness_response.hpp"
|
||||
|
||||
namespace sensor_calibration_service
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
int64_t now_us()
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
using calibration_common_interfaces::msg::ErrorCode;
|
||||
using calibration_common_interfaces::msg::JobState;
|
||||
|
||||
SensorCalibrationServiceNode::SensorCalibrationServiceNode(const rclcpp::NodeOptions & options)
|
||||
: Node("sensor_calibration_service", options)
|
||||
{
|
||||
readiness_service_ = create_service<ReadinessSrv>(
|
||||
"/sensor_calibration/get_readiness",
|
||||
std::bind(&SensorCalibrationServiceNode::handle_readiness, this, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
execute_task_action_server_ = rclcpp_action::create_server<ExecuteTask>(
|
||||
this,
|
||||
"/sensor_calibration/execute_task",
|
||||
std::bind(&SensorCalibrationServiceNode::handle_goal, this, std::placeholders::_1, std::placeholders::_2),
|
||||
std::bind(&SensorCalibrationServiceNode::handle_cancel, this, std::placeholders::_1),
|
||||
std::bind(&SensorCalibrationServiceNode::handle_accepted, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SensorCalibrationServiceNode::handle_readiness(
|
||||
const std::shared_ptr<ReadinessSrv::Request> request,
|
||||
std::shared_ptr<ReadinessSrv::Response> response)
|
||||
{
|
||||
(void)request;
|
||||
response->response.success = true;
|
||||
response->response.error_code.code = ErrorCode::OK;
|
||||
response->response.message = "sensor_calibration_service is ready.";
|
||||
response->response.agent_ready = true;
|
||||
response->response.capture_pipeline_ready = true;
|
||||
response->response.storage_ready = true;
|
||||
response->response.telemetry_ready = true;
|
||||
response->response.vehicle_safe_to_move = true;
|
||||
response->response.arm_ready = true;
|
||||
response->response.ready_sensor_ids.push_back("demo_sensor_001");
|
||||
response->response.checked_timestamp_us = now_us();
|
||||
}
|
||||
|
||||
rclcpp_action::GoalResponse SensorCalibrationServiceNode::handle_goal(
|
||||
const rclcpp_action::GoalUUID & /*uuid*/,
|
||||
std::shared_ptr<const ExecuteTask::Goal> goal)
|
||||
{
|
||||
if (goal->goal.header.request_id.empty()) {
|
||||
return rclcpp_action::GoalResponse::REJECT;
|
||||
}
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
|
||||
}
|
||||
|
||||
rclcpp_action::CancelResponse SensorCalibrationServiceNode::handle_cancel(
|
||||
const std::shared_ptr<GoalHandleExecuteTask> /*goal_handle*/)
|
||||
{
|
||||
return rclcpp_action::CancelResponse::ACCEPT;
|
||||
}
|
||||
|
||||
void SensorCalibrationServiceNode::handle_accepted(
|
||||
const std::shared_ptr<GoalHandleExecuteTask> goal_handle)
|
||||
{
|
||||
std::thread(std::bind(&SensorCalibrationServiceNode::execute_goal, this, goal_handle)).detach();
|
||||
}
|
||||
|
||||
void SensorCalibrationServiceNode::execute_goal(
|
||||
const std::shared_ptr<GoalHandleExecuteTask> goal_handle)
|
||||
{
|
||||
// 先回一帧 RUNNING feedback,告诉 orchestrator 当前任务已经进入执行阶段。
|
||||
auto feedback = std::make_shared<ExecuteTask::Feedback>();
|
||||
feedback->feedback.state.state = JobState::RUNNING;
|
||||
goal_handle->publish_feedback(feedback);
|
||||
|
||||
// ===== 组装算法输入上下文 =====
|
||||
// 当前模板阶段先把“算法最常用的任务侧输入”显式展开。
|
||||
// 后续如果要接真实车辆画像、已生效参数查询、历史遥测缓存、外部定位缓存,
|
||||
// 也应继续在这里补齐并写入 SensorCalibrationInput。
|
||||
SensorCalibrationAlgorithmTemplate::Input input;
|
||||
input.request = *goal_handle->get_goal();
|
||||
input.task_type = goal_handle->get_goal()->goal.selected_task;
|
||||
input.task_subtype = goal_handle->get_goal()->goal.task_subtype;
|
||||
input.target_sensor_id = resolve_target_sensor_id(*goal_handle->get_goal());
|
||||
input.camera_intrinsic_task = goal_handle->get_goal()->goal.camera_intrinsic;
|
||||
input.imu_intrinsic_task = goal_handle->get_goal()->goal.imu_intrinsic;
|
||||
input.sensor_to_base_extrinsic_task = goal_handle->get_goal()->goal.sensor_to_base_extrinsic;
|
||||
input.hand_eye_task = goal_handle->get_goal()->goal.hand_eye;
|
||||
input.required_image_count = goal_handle->get_goal()->goal.camera_intrinsic.required_image_count;
|
||||
input.required_static_segment_count = goal_handle->get_goal()->goal.imu_intrinsic.required_static_segment_count;
|
||||
input.required_motion_segment_count = goal_handle->get_goal()->goal.imu_intrinsic.required_motion_segment_count;
|
||||
input.required_sample_count = goal_handle->get_goal()->goal.sensor_to_base_extrinsic.required_sample_count;
|
||||
input.required_pose_count = goal_handle->get_goal()->goal.hand_eye.required_pose_count;
|
||||
switch (goal_handle->get_goal()->goal.selected_task.value) {
|
||||
case TaskType::CAMERA_INTRINSIC:
|
||||
input.reference_timeout_sec = goal_handle->get_goal()->goal.camera_intrinsic.timeout_sec;
|
||||
break;
|
||||
case TaskType::IMU_INTRINSIC:
|
||||
input.reference_timeout_sec = goal_handle->get_goal()->goal.imu_intrinsic.timeout_sec;
|
||||
break;
|
||||
case TaskType::SENSOR_TO_BASE_EXTRINSIC:
|
||||
input.reference_timeout_sec = goal_handle->get_goal()->goal.sensor_to_base_extrinsic.timeout_sec;
|
||||
break;
|
||||
case TaskType::HAND_EYE:
|
||||
input.reference_timeout_sec = goal_handle->get_goal()->goal.hand_eye.timeout_sec;
|
||||
break;
|
||||
default:
|
||||
input.reference_timeout_sec = 0.0;
|
||||
break;
|
||||
}
|
||||
input.reference_board_id = goal_handle->get_goal()->goal.camera_intrinsic.target_board_id;
|
||||
input.reference_base_frame_id = goal_handle->get_goal()->goal.sensor_to_base_extrinsic.base_frame_id;
|
||||
input.reference_arm_id = goal_handle->get_goal()->goal.hand_eye.arm_id;
|
||||
input.sensor_history_available = false;
|
||||
input.chassis_history_available = false;
|
||||
input.control_history_available = false;
|
||||
input.truth_history_available = false;
|
||||
|
||||
SensorCalibrationAlgorithmTemplate::Output output;
|
||||
std::string failure_reason;
|
||||
if (!algorithm_.run(input, output, failure_reason)) {
|
||||
auto result = std::make_shared<ExecuteTask::Result>();
|
||||
result->result.success = false;
|
||||
result->result.error_code.code = ErrorCode::INVALID_STATE;
|
||||
result->result.message = failure_reason;
|
||||
result->result.job_id = goal_handle->get_goal()->goal.header.request_id;
|
||||
result->result.data_quality_passed = false;
|
||||
result->result.suitable_for_commit = false;
|
||||
goal_handle->abort(result);
|
||||
return;
|
||||
}
|
||||
|
||||
auto result = std::make_shared<ExecuteTask::Result>(output.response);
|
||||
goal_handle->succeed(result);
|
||||
}
|
||||
|
||||
std::string SensorCalibrationServiceNode::resolve_target_sensor_id(const ExecuteTask::Goal & goal) const
|
||||
{
|
||||
switch (goal.goal.selected_task.value) {
|
||||
case TaskType::CAMERA_INTRINSIC:
|
||||
return goal.goal.camera_intrinsic.sensor_id;
|
||||
case TaskType::IMU_INTRINSIC:
|
||||
return goal.goal.imu_intrinsic.sensor_id;
|
||||
case TaskType::SENSOR_TO_BASE_EXTRINSIC:
|
||||
return goal.goal.sensor_to_base_extrinsic.sensor_id;
|
||||
case TaskType::HAND_EYE:
|
||||
return goal.goal.hand_eye.sensor_id;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sensor_calibration_service
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
#include "sensor_calibration_service/sensor_calibration_common.hpp"
|
||||
|
||||
namespace sensor_calibration_service
|
||||
{
|
||||
|
||||
bool FrontCameraExtrinsicCalibrationAlgorithm::run(
|
||||
const SensorCalibrationInput & input,
|
||||
SensorCalibrationOutput & output,
|
||||
std::string & failure_reason) const
|
||||
{
|
||||
(void)failure_reason;
|
||||
|
||||
// 前视相机到 base_link 外参标定模板。
|
||||
fill_common_result(input, output);
|
||||
output.response.result.message = "front camera extrinsic calibration template executed.";
|
||||
output.response.result.recommended_parameter_version = "front_camera_extrinsic_template_v1";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DownwardCameraExtrinsicCalibrationAlgorithm::run(
|
||||
const SensorCalibrationInput & input,
|
||||
SensorCalibrationOutput & output,
|
||||
std::string & failure_reason) const
|
||||
{
|
||||
(void)failure_reason;
|
||||
|
||||
// 下视相机到 base_link 外参标定模板。
|
||||
fill_common_result(input, output);
|
||||
output.response.result.message = "downward camera extrinsic calibration template executed.";
|
||||
output.response.result.recommended_parameter_version = "downward_camera_extrinsic_template_v1";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImuExtrinsicCalibrationAlgorithm::run(
|
||||
const SensorCalibrationInput & input,
|
||||
SensorCalibrationOutput & output,
|
||||
std::string & failure_reason) const
|
||||
{
|
||||
(void)failure_reason;
|
||||
|
||||
// IMU 到 base_link 外参标定模板。
|
||||
fill_common_result(input, output);
|
||||
output.response.result.message = "imu extrinsic calibration template executed.";
|
||||
output.response.result.recommended_parameter_version = "imu_extrinsic_template_v1";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lidar2DExtrinsicCalibrationAlgorithm::run(
|
||||
const SensorCalibrationInput & input,
|
||||
SensorCalibrationOutput & output,
|
||||
std::string & failure_reason) const
|
||||
{
|
||||
(void)failure_reason;
|
||||
|
||||
// 2D 激光雷达到 base_link 外参标定模板。
|
||||
fill_common_result(input, output);
|
||||
output.response.result.message = "2d lidar extrinsic calibration template executed.";
|
||||
output.response.result.recommended_parameter_version = "lidar_2d_extrinsic_template_v1";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lidar3DExtrinsicCalibrationAlgorithm::run(
|
||||
const SensorCalibrationInput & input,
|
||||
SensorCalibrationOutput & output,
|
||||
std::string & failure_reason) const
|
||||
{
|
||||
(void)failure_reason;
|
||||
|
||||
// 3D 激光雷达到 base_link 外参标定模板。
|
||||
fill_common_result(input, output);
|
||||
output.response.result.message = "3d lidar extrinsic calibration template executed.";
|
||||
output.response.result.recommended_parameter_version = "lidar_3d_extrinsic_template_v1";
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace sensor_calibration_service
|
||||
-180
@@ -1,180 +0,0 @@
|
||||
#include "vehicle_profile_manager/vehicle_profile_manager_node.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "calibration_common_interfaces/msg/error_code.hpp"
|
||||
#include "calibration_vehicle_profile_interfaces/msg/chassis_type.hpp"
|
||||
#include "calibration_vehicle_profile_interfaces/msg/workflow_stage_type.hpp"
|
||||
#include "rclcpp_components/register_node_macro.hpp"
|
||||
|
||||
namespace vehicle_profile_manager
|
||||
{
|
||||
|
||||
using calibration_common_interfaces::msg::ErrorCode;
|
||||
using calibration_vehicle_profile_interfaces::msg::ChassisType;
|
||||
using calibration_vehicle_profile_interfaces::msg::WorkflowStageType;
|
||||
|
||||
VehicleProfileManagerNode::VehicleProfileManagerNode(const rclcpp::NodeOptions & options)
|
||||
: Node("vehicle_profile_manager", options)
|
||||
{
|
||||
get_profile_service_ = create_service<GetProfileSrv>(
|
||||
"/vehicle_profile_manager/get_vehicle_profile",
|
||||
std::bind(
|
||||
&VehicleProfileManagerNode::handle_get_profile, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
register_service_ = create_service<RegisterSrv>(
|
||||
"/vehicle_profile_manager/register_or_update_vehicle_profile",
|
||||
std::bind(
|
||||
&VehicleProfileManagerNode::handle_register, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
applicability_service_ = create_service<ApplicabilitySrv>(
|
||||
"/vehicle_profile_manager/evaluate_vehicle_calibration_applicability",
|
||||
std::bind(
|
||||
&VehicleProfileManagerNode::handle_applicability, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
heartbeat_service_ = create_service<HeartbeatSrv>(
|
||||
"/vehicle_profile_manager/heartbeat",
|
||||
std::bind(
|
||||
&VehicleProfileManagerNode::handle_heartbeat, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
load_demo_profile();
|
||||
|
||||
RCLCPP_INFO(get_logger(), "VehicleProfileManagerNode 启动,已预载 demo 车辆画像。");
|
||||
}
|
||||
|
||||
void VehicleProfileManagerNode::handle_get_profile(
|
||||
const std::shared_ptr<GetProfileSrv::Request> request,
|
||||
std::shared_ptr<GetProfileSrv::Response> response)
|
||||
{
|
||||
const auto & vehicle_id = request->request.vehicle_id;
|
||||
auto it = profiles_.find(vehicle_id);
|
||||
if (it == profiles_.end()) {
|
||||
response->response.success = false;
|
||||
response->response.error_code.code = ErrorCode::INVALID_ARGUMENT;
|
||||
response->response.message = "找不到 vehicle_id=[" + vehicle_id + "] 的车辆画像。";
|
||||
return;
|
||||
}
|
||||
response->response.success = true;
|
||||
response->response.error_code.code = ErrorCode::OK;
|
||||
response->response.message = "查询成功。";
|
||||
response->response.profile = it->second;
|
||||
}
|
||||
|
||||
void VehicleProfileManagerNode::handle_register(
|
||||
const std::shared_ptr<RegisterSrv::Request> request,
|
||||
std::shared_ptr<RegisterSrv::Response> response)
|
||||
{
|
||||
const auto & vehicle_id = request->request.profile.base_info.vehicle_id;
|
||||
if (vehicle_id.empty()) {
|
||||
response->response.success = false;
|
||||
response->response.error_code.code = ErrorCode::INVALID_ARGUMENT;
|
||||
response->response.message = "vehicle_id 不能为空。";
|
||||
return;
|
||||
}
|
||||
profiles_[vehicle_id] = request->request.profile;
|
||||
RCLCPP_INFO(get_logger(), "已注册/更新车辆画像 vehicle_id=[%s]", vehicle_id.c_str());
|
||||
response->response.success = true;
|
||||
response->response.error_code.code = ErrorCode::OK;
|
||||
response->response.message = "注册/更新成功。";
|
||||
}
|
||||
|
||||
void VehicleProfileManagerNode::handle_applicability(
|
||||
const std::shared_ptr<ApplicabilitySrv::Request> request,
|
||||
std::shared_ptr<ApplicabilitySrv::Response> response)
|
||||
{
|
||||
const auto & profile = request->request.profile_snapshot;
|
||||
auto stages = evaluate_supported_stages(profile);
|
||||
|
||||
response->response.success = true;
|
||||
response->response.error_code.code = ErrorCode::OK;
|
||||
response->response.message = "适用性评估完成。";
|
||||
response->response.overall_supported = !stages.empty();
|
||||
response->response.recommended_workflow_stages = stages;
|
||||
}
|
||||
|
||||
void VehicleProfileManagerNode::handle_heartbeat(
|
||||
const std::shared_ptr<HeartbeatSrv::Request> /*request*/,
|
||||
std::shared_ptr<HeartbeatSrv::Response> response)
|
||||
{
|
||||
const auto now_us = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
response->response.success = true;
|
||||
response->response.error_code.code = ErrorCode::OK;
|
||||
response->response.message = "vehicle_profile_manager 在线。";
|
||||
response->response.server_timestamp_us = now_us;
|
||||
response->response.vehicle_ready = true;
|
||||
}
|
||||
|
||||
std::vector<WorkflowStageType>
|
||||
VehicleProfileManagerNode::evaluate_supported_stages(const VehicleProfile & profile) const
|
||||
{
|
||||
std::vector<WorkflowStageType> stages;
|
||||
|
||||
auto make_stage = [](uint8_t v) {
|
||||
WorkflowStageType s;
|
||||
s.value = v;
|
||||
return s;
|
||||
};
|
||||
|
||||
// 预检和画像校验始终支持。
|
||||
stages.push_back(make_stage(WorkflowStageType::PROFILE_VALIDATION_STAGE));
|
||||
stages.push_back(make_stage(WorkflowStageType::WORKSHOP_PRECHECK_STAGE));
|
||||
|
||||
// 底盘标定:底盘类型已指定时支持。
|
||||
if (profile.chassis_type.value != ChassisType::CHASSIS_TYPE_UNSPECIFIED) {
|
||||
stages.push_back(make_stage(WorkflowStageType::CHASSIS_CALIBRATION_STAGE));
|
||||
stages.push_back(make_stage(WorkflowStageType::CONTROL_CALIBRATION_STAGE));
|
||||
}
|
||||
|
||||
// 传感器标定:有传感器配置时支持。
|
||||
if (!profile.sensors.empty()) {
|
||||
stages.push_back(make_stage(WorkflowStageType::SENSOR_INTRINSIC_CALIBRATION_STAGE));
|
||||
stages.push_back(make_stage(WorkflowStageType::SENSOR_EXTRINSIC_CALIBRATION_STAGE));
|
||||
}
|
||||
|
||||
// 手眼标定:有机械臂且有传感器时支持。
|
||||
if (profile.arm_profile.has_mechanical_arm && !profile.sensors.empty()) {
|
||||
stages.push_back(make_stage(WorkflowStageType::HAND_EYE_CALIBRATION_STAGE));
|
||||
}
|
||||
|
||||
// 最终阶段始终加入。
|
||||
stages.push_back(make_stage(WorkflowStageType::PARAMETER_COMMIT_STAGE));
|
||||
stages.push_back(make_stage(WorkflowStageType::REPORT_ARCHIVE_STAGE));
|
||||
|
||||
return stages;
|
||||
}
|
||||
|
||||
void VehicleProfileManagerNode::load_demo_profile()
|
||||
{
|
||||
VehicleProfile demo;
|
||||
|
||||
// 基础信息
|
||||
demo.base_info.vehicle_id = "demo_agv_001";
|
||||
demo.base_info.vehicle_name = "Demo AGV";
|
||||
demo.base_info.model_name = "DemoModel-X1";
|
||||
demo.base_info.manufacturer = "Demo Manufacturer";
|
||||
|
||||
// 底盘类型:差速
|
||||
demo.chassis_type.value = ChassisType::DIFFERENTIAL;
|
||||
|
||||
// base_link
|
||||
demo.base_link_frame = "base_link";
|
||||
|
||||
// 画像版本
|
||||
demo.profile_version = "demo_v1";
|
||||
|
||||
// 启用的工作流阶段
|
||||
demo.enabled_workflow_stages = evaluate_supported_stages(demo);
|
||||
|
||||
profiles_[demo.base_info.vehicle_id] = demo;
|
||||
RCLCPP_INFO(get_logger(), "已预载 demo 车辆画像 vehicle_id=[%s]",
|
||||
demo.base_info.vehicle_id.c_str());
|
||||
}
|
||||
|
||||
} // namespace vehicle_profile_manager
|
||||
|
||||
RCLCPP_COMPONENTS_REGISTER_NODE(vehicle_profile_manager::VehicleProfileManagerNode)
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
from launch import LaunchDescription
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
return LaunchDescription([
|
||||
Node(
|
||||
package="workshop_orchestrator_v2",
|
||||
executable="workshop_orchestrator_v2_node",
|
||||
name="workshop_orchestrator_v2",
|
||||
output="screen",
|
||||
)
|
||||
])
|
||||
@@ -1,153 +0,0 @@
|
||||
#include "workshop_orchestrator_v2/precheck_runner.hpp"
|
||||
|
||||
#include "calibration_workshop_orchestration_interfaces/msg/precheck_item.hpp"
|
||||
|
||||
namespace workshop_orchestrator_v2
|
||||
{
|
||||
|
||||
bool PrecheckRunner::has_metadata_key(const StagePlan & stage, const std::string & key) const
|
||||
{
|
||||
for (const auto & kv : stage.metadata) {
|
||||
if (kv.key == key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PrecheckRunner::has_metadata_prefix(const StagePlan & stage, const std::string & prefix) const
|
||||
{
|
||||
for (const auto & kv : stage.metadata) {
|
||||
if (kv.key.rfind(prefix, 0) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
WorkshopPrecheckResponse PrecheckRunner::run(const WorkshopSession & session) const
|
||||
{
|
||||
WorkshopPrecheckResponse response;
|
||||
response.success = true;
|
||||
response.error_code = make_error_code(ErrorCode::OK);
|
||||
response.checked_timestamp_us = now_us();
|
||||
|
||||
auto append_item = [&](const std::string & code,
|
||||
const std::string & name,
|
||||
bool passed,
|
||||
const std::string & message,
|
||||
uint8_t stage_type,
|
||||
uint8_t module_type) {
|
||||
calibration_workshop_orchestration_interfaces::msg::PrecheckItem item;
|
||||
item.item_code = code;
|
||||
item.display_name = name;
|
||||
item.passed = passed;
|
||||
item.blocking = true;
|
||||
item.error_code = make_error_code(passed ? ErrorCode::OK : ErrorCode::INVALID_STATE);
|
||||
item.message = message;
|
||||
item.related_stage_type = make_stage_type(stage_type);
|
||||
item.related_module_type = make_module_type(module_type);
|
||||
response.items.push_back(item);
|
||||
if (!passed) {
|
||||
response.blocking_issue_count += 1;
|
||||
response.all_passed = false;
|
||||
}
|
||||
};
|
||||
|
||||
response.all_passed = true;
|
||||
response.blocking_issue_count = 0;
|
||||
|
||||
append_item(
|
||||
"plan_not_empty",
|
||||
"Execution plan exists",
|
||||
!session.stage_plan.empty(),
|
||||
session.stage_plan.empty() ? "Session has no executable stages." : "Session has executable stages.",
|
||||
WorkflowStageType::WORKFLOW_STAGE_UNSPECIFIED,
|
||||
CalibrationModuleType::CALIBRATION_MODULE_UNSPECIFIED);
|
||||
|
||||
for (const auto & stage : session.stage_plan) {
|
||||
if (stage.module_type.value == CalibrationModuleType::CHASSIS_MODULE) {
|
||||
const bool passed =
|
||||
has_metadata_key(stage, metadata_keys::CHASSIS_PRIMITIVE_TYPE) &&
|
||||
has_metadata_key(stage, metadata_keys::CHASSIS_STRAIGHT_LINE_DISTANCE_M) &&
|
||||
has_metadata_key(stage, metadata_keys::CHASSIS_STRAIGHT_LINE_SPEED_MS);
|
||||
append_item(
|
||||
stage.stage_id + ".metadata",
|
||||
stage.display_name + " metadata",
|
||||
passed,
|
||||
passed ? "底盘阶段输入完整。" : "底盘阶段缺少 primitive_type 或直线动作参数。",
|
||||
stage.stage_type.value,
|
||||
stage.module_type.value);
|
||||
} else if (stage.module_type.value == CalibrationModuleType::CONTROL_MODULE) {
|
||||
const bool passed =
|
||||
has_metadata_key(stage, metadata_keys::CONTROL_TASK_TYPE) &&
|
||||
has_metadata_prefix(stage, metadata_keys::CONTROL_TRAJECTORY_PREFIX);
|
||||
append_item(
|
||||
stage.stage_id + ".metadata",
|
||||
stage.display_name + " metadata",
|
||||
passed,
|
||||
passed ? "运控阶段输入完整。" : "运控阶段缺少 control.task_type 或轨迹点输入。",
|
||||
stage.stage_type.value,
|
||||
stage.module_type.value);
|
||||
} else if (
|
||||
stage.module_type.value == CalibrationModuleType::SENSOR_INTRINSIC_MODULE ||
|
||||
stage.module_type.value == CalibrationModuleType::SENSOR_EXTRINSIC_MODULE ||
|
||||
stage.module_type.value == CalibrationModuleType::HAND_EYE_MODULE) {
|
||||
bool passed =
|
||||
has_metadata_key(stage, metadata_keys::SENSOR_ID) &&
|
||||
has_metadata_key(stage, metadata_keys::SENSOR_TASK_SUBTYPE);
|
||||
std::string message = passed ? "传感器阶段基础输入完整。" : "传感器阶段缺少 sensor.sensor_id 或 sensor.task_subtype。";
|
||||
|
||||
if (passed && stage.module_type.value == CalibrationModuleType::SENSOR_INTRINSIC_MODULE) {
|
||||
const bool has_image_count = has_metadata_key(stage, metadata_keys::CAMERA_INTRINSIC_REQUIRED_IMAGE_COUNT);
|
||||
const bool has_board = has_metadata_key(stage, metadata_keys::CAMERA_INTRINSIC_TARGET_BOARD_ID);
|
||||
passed = has_image_count || has_board;
|
||||
message = passed ? "传感器内参阶段输入完整。" : "传感器内参阶段缺少图像数或标定板信息。";
|
||||
}
|
||||
|
||||
if (passed && stage.module_type.value == CalibrationModuleType::SENSOR_EXTRINSIC_MODULE) {
|
||||
passed =
|
||||
has_metadata_key(stage, metadata_keys::SENSOR_EXTRINSIC_BASE_FRAME_ID) &&
|
||||
has_metadata_key(stage, metadata_keys::SENSOR_EXTRINSIC_REQUIRED_SAMPLE_COUNT);
|
||||
message = passed ? "传感器外参阶段输入完整。" : "传感器外参阶段缺少 base_frame_id 或 required_sample_count。";
|
||||
}
|
||||
|
||||
if (passed && stage.module_type.value == CalibrationModuleType::HAND_EYE_MODULE) {
|
||||
passed =
|
||||
has_metadata_key(stage, metadata_keys::HAND_EYE_ARM_ID) &&
|
||||
has_metadata_key(stage, metadata_keys::HAND_EYE_REQUIRED_POSE_COUNT);
|
||||
message = passed ? "手眼阶段输入完整。" : "手眼阶段缺少 arm_id 或 required_pose_count。";
|
||||
}
|
||||
|
||||
append_item(
|
||||
stage.stage_id + ".metadata",
|
||||
stage.display_name + " metadata",
|
||||
passed,
|
||||
message,
|
||||
stage.stage_type.value,
|
||||
stage.module_type.value);
|
||||
} else if (stage.module_type.value == CalibrationModuleType::EXTERNAL_LOCALIZATION_MODULE) {
|
||||
const bool passed =
|
||||
has_metadata_key(stage, metadata_keys::EXTERNAL_STATIC_SAMPLE_COUNT) &&
|
||||
has_metadata_key(stage, metadata_keys::EXTERNAL_DYNAMIC_SAMPLE_COUNT) &&
|
||||
has_metadata_key(stage, metadata_keys::EXTERNAL_MAX_POSITION_STDDEV_M) &&
|
||||
has_metadata_key(stage, metadata_keys::EXTERNAL_MAX_YAW_STDDEV_RAD) &&
|
||||
has_metadata_key(stage, metadata_keys::EXTERNAL_MAX_TRACKING_LOSS_RATIO) &&
|
||||
has_metadata_key(stage, metadata_keys::EXTERNAL_MAX_TIME_SYNC_OFFSET_MS) &&
|
||||
has_metadata_key(stage, metadata_keys::EXTERNAL_TIMEOUT_SEC);
|
||||
append_item(
|
||||
stage.stage_id + ".metadata",
|
||||
stage.display_name + " metadata",
|
||||
passed,
|
||||
passed ? "external 阶段输入完整。" : "external 阶段缺少真值校核阈值配置。",
|
||||
stage.stage_type.value,
|
||||
stage.module_type.value);
|
||||
}
|
||||
}
|
||||
|
||||
response.ready_for_start = response.all_passed;
|
||||
response.message = response.ready_for_start ? "Precheck passed." : "Precheck failed.";
|
||||
return response;
|
||||
}
|
||||
|
||||
} // namespace workshop_orchestrator_v2
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
# workshop_ui_pyside6_config_aligned
|
||||
# 操作员界面原型
|
||||
|
||||
这版 PySide6 原型的目标不是单纯展示界面,而是:
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(vehicle_internal_interfaces)
|
||||
|
||||
find_package(ament_cmake REQUIRED)
|
||||
find_package(rosidl_default_generators REQUIRED)
|
||||
find_package(calibration_vehicle_profile_interfaces REQUIRED)
|
||||
|
||||
set(msg_files
|
||||
"msg/VehicleControlMode.msg"
|
||||
"msg/AckermannDriveCommand.msg"
|
||||
"msg/VehicleSafetyCommand.msg"
|
||||
"msg/AckermannActuatorState.msg"
|
||||
"msg/VehicleInternalState.msg"
|
||||
"msg/VehicleHealthStatus.msg"
|
||||
"msg/VehicleTimeSyncStatus.msg"
|
||||
"msg/SensorLinkStatus.msg"
|
||||
)
|
||||
|
||||
rosidl_generate_interfaces(${PROJECT_NAME}
|
||||
${msg_files}
|
||||
DEPENDENCIES calibration_vehicle_profile_interfaces
|
||||
)
|
||||
|
||||
ament_export_dependencies(rosidl_default_runtime)
|
||||
ament_package()
|
||||
@@ -0,0 +1,17 @@
|
||||
# 车辆内部接口
|
||||
|
||||
这个包定义车端电脑和车辆本体之间的内部 ROS2 消息。
|
||||
|
||||
它不直接作为车间电脑和车端电脑之间的 WiFi6 协议。WiFi6 对外协议仍由 `calibration_*_interfaces/proto` 定义。这个包用于把真实 Windows 小车的 CAN、串口、厂商 SDK 或仿真 Isaac topic 统一映射成车端内部语义。
|
||||
|
||||
当前消息包括:
|
||||
|
||||
- `AckermannDriveCommand`:阿克曼底盘速度、转角、制动、超时命令。
|
||||
- `VehicleSafetyCommand`:上使能、下使能、急停、清故障、标定低速模式。
|
||||
- `AckermannActuatorState`:速度、转角、轮速、电机电流、制动/油门反馈。
|
||||
- `VehicleInternalState`:车辆模式、安全状态、底盘执行器状态、电源与运动状态。
|
||||
- `VehicleHealthStatus`:控制器在线、总线状态、通信质量和车端资源状态。
|
||||
- `VehicleTimeSyncStatus`:车端、传感器、外部真值之间的时间同步状态。
|
||||
- `SensorLinkStatus`:车载传感器在线状态、帧率、丢帧和延迟。
|
||||
|
||||
仿真中,`vehicle_agent_sim` 会把车间电脑下发的控制请求转换为 `AckermannDriveCommand`,同时继续发布 Isaac 当前需要的 `cmd_vel`。真实部署时,Windows 车端应把这些内部消息映射到实际车辆接口。
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
# =========================================================
|
||||
# 阿克曼底盘内部执行器状态
|
||||
# 发送方:车辆控制器 / Isaac 底盘执行器适配层
|
||||
# 接收方:车端电脑
|
||||
# =========================================================
|
||||
|
||||
# 状态时间戳
|
||||
int64 hardware_timestamp_us
|
||||
|
||||
# 实际纵向速度
|
||||
float64 actual_speed_ms
|
||||
# 实际纵向加速度
|
||||
float64 actual_accel_ms2
|
||||
# 实际前轮等效转角
|
||||
float64 actual_steering_angle_rad
|
||||
# 实际转向角速度
|
||||
float64 actual_steering_rate_rads
|
||||
|
||||
# 左后驱动轮速度
|
||||
float64 rear_left_wheel_speed_ms
|
||||
# 右后驱动轮速度
|
||||
float64 rear_right_wheel_speed_ms
|
||||
# 左前轮等效转角
|
||||
float64 front_left_steering_angle_rad
|
||||
# 右前轮等效转角
|
||||
float64 front_right_steering_angle_rad
|
||||
|
||||
# 驱动电机电流
|
||||
float64 drive_motor_current_amp
|
||||
# 转向电机电流
|
||||
float64 steering_motor_current_amp
|
||||
# 制动压力或制动比例,范围 [0, 1]
|
||||
float64 brake_pressure
|
||||
# 驱动控制输出,范围 [0, 1]
|
||||
float64 throttle_output
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
# =========================================================
|
||||
# 阿克曼底盘内部控制命令
|
||||
# 发送方:车端电脑
|
||||
# 接收方:车辆运动控制器 / Isaac 底盘执行器适配层
|
||||
# =========================================================
|
||||
|
||||
# 命令时间戳
|
||||
int64 command_timestamp_us
|
||||
# 命令 ID,用于追踪和去重
|
||||
string command_id
|
||||
# 命令来源,例如 vehicle_agent_sim / real_vehicle_agent
|
||||
string source
|
||||
# 控制模式
|
||||
vehicle_internal_interfaces/VehicleControlMode control_mode
|
||||
|
||||
# 目标纵向速度
|
||||
float64 target_speed_ms
|
||||
# 目标纵向加速度;0 表示由车辆控制器默认限幅
|
||||
float64 target_accel_ms2
|
||||
# 目标前轮等效转角,左正右负
|
||||
float64 target_steering_angle_rad
|
||||
# 目标转向角速度;0 表示由车辆控制器默认限幅
|
||||
float64 target_steering_rate_rads
|
||||
|
||||
# 制动命令,范围 [0, 1]
|
||||
float64 brake_command
|
||||
# 油门 / 驱动命令,范围 [0, 1];仿真可选
|
||||
float64 throttle_command
|
||||
# 命令超时时间
|
||||
float64 command_timeout_sec
|
||||
|
||||
# 是否要求控制器在超时或任务结束后停车
|
||||
bool stop_when_timeout
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
# =========================================================
|
||||
# 车端传感器链路状态
|
||||
# 发送方:车端传感器代理
|
||||
# 接收方:车端电脑 / 车间电脑桥接层
|
||||
# =========================================================
|
||||
|
||||
# 状态时间戳
|
||||
int64 hardware_timestamp_us
|
||||
|
||||
# 传感器 ID
|
||||
string sensor_id
|
||||
# 传感器类型
|
||||
calibration_vehicle_profile_interfaces/SensorType sensor_type
|
||||
# 传感器 frame
|
||||
string frame_id
|
||||
# 车端订阅或驱动 topic / 通道名
|
||||
string source_channel
|
||||
|
||||
# 是否在线
|
||||
bool online
|
||||
# 当前帧率
|
||||
float64 frame_rate_hz
|
||||
# 最近一帧年龄
|
||||
float64 latest_frame_age_ms
|
||||
# 累计帧数
|
||||
uint64 frame_count
|
||||
# 丢帧比例
|
||||
float64 dropped_frame_ratio
|
||||
# 最近一帧传输延迟
|
||||
float64 latest_transport_latency_ms
|
||||
|
||||
# 当前链路状态说明
|
||||
string status_message
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# =========================================================
|
||||
# 车辆内部控制模式
|
||||
# 作用:车端电脑与车辆控制器之间约定当前车辆控制状态
|
||||
# =========================================================
|
||||
|
||||
uint8 MODE_UNSPECIFIED=0
|
||||
uint8 DISABLED=1
|
||||
uint8 MANUAL=2
|
||||
uint8 AUTO=3
|
||||
uint8 CALIBRATION=4
|
||||
uint8 ESTOP=5
|
||||
uint8 FAULT=6
|
||||
|
||||
# 当前模式
|
||||
uint8 value
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
# =========================================================
|
||||
# 车辆内部健康诊断状态
|
||||
# 发送方:车端电脑 / 车辆控制器
|
||||
# 接收方:车端电脑内部监控或车间电脑桥接层
|
||||
# =========================================================
|
||||
|
||||
# 状态时间戳
|
||||
int64 hardware_timestamp_us
|
||||
|
||||
# 车端电脑进程是否在线
|
||||
bool vehicle_agent_online
|
||||
# 车辆主控制器是否在线
|
||||
bool vehicle_controller_online
|
||||
# 驱动控制器是否在线
|
||||
bool drive_controller_online
|
||||
# 转向控制器是否在线
|
||||
bool steering_controller_online
|
||||
# 传感器总线是否在线
|
||||
bool sensor_bus_online
|
||||
# CAN 或厂商控制链路是否在线
|
||||
bool vehicle_bus_online
|
||||
# 外部真值链路是否在线
|
||||
bool external_truth_link_online
|
||||
|
||||
# 通信质量
|
||||
float64 vehicle_bus_rx_hz
|
||||
float64 vehicle_bus_drop_ratio
|
||||
float64 command_latency_ms
|
||||
float64 telemetry_latency_ms
|
||||
|
||||
# 车端电脑资源
|
||||
float64 cpu_load_ratio
|
||||
float64 memory_used_ratio
|
||||
float64 disk_used_ratio
|
||||
float64 temperature_c
|
||||
|
||||
# 诊断摘要
|
||||
bool healthy
|
||||
string diagnostic_code
|
||||
string diagnostic_message
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
# =========================================================
|
||||
# 车辆内部综合状态
|
||||
# 发送方:车辆控制器 / Isaac 底盘执行器适配层
|
||||
# 接收方:车端电脑
|
||||
# =========================================================
|
||||
|
||||
# 状态时间戳
|
||||
int64 hardware_timestamp_us
|
||||
# 当前控制模式
|
||||
vehicle_internal_interfaces/VehicleControlMode current_mode
|
||||
|
||||
# 车辆是否已上使能
|
||||
bool vehicle_enabled
|
||||
# 急停是否触发
|
||||
bool estop_engaged
|
||||
# 是否处于低速标定模式
|
||||
bool calibration_low_speed_mode
|
||||
# 是否存在故障
|
||||
bool fault_active
|
||||
# 主故障码
|
||||
string primary_fault_code
|
||||
# 主故障说明
|
||||
string primary_fault_message
|
||||
|
||||
# 当前底盘状态
|
||||
vehicle_internal_interfaces/AckermannActuatorState ackermann_state
|
||||
|
||||
# 电源状态
|
||||
float64 battery_voltage_v
|
||||
float64 battery_current_amp
|
||||
float64 battery_soc
|
||||
|
||||
# 车辆运动状态
|
||||
float64 yaw_rate_rads
|
||||
float64 lateral_accel_ms2
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# =========================================================
|
||||
# 车辆内部安全命令
|
||||
# 发送方:车端电脑
|
||||
# 接收方:车辆安全控制器 / 底盘控制器
|
||||
# =========================================================
|
||||
|
||||
# 命令时间戳
|
||||
int64 command_timestamp_us
|
||||
# 命令 ID
|
||||
string command_id
|
||||
# 命令来源
|
||||
string source
|
||||
|
||||
# 上使能车辆
|
||||
bool enable_vehicle
|
||||
# 下使能车辆
|
||||
bool disable_vehicle
|
||||
# 触发急停
|
||||
bool engage_estop
|
||||
# 解除急停
|
||||
bool release_estop
|
||||
# 清除可恢复故障
|
||||
bool clear_faults
|
||||
# 切换到标定低速安全模式
|
||||
bool enter_calibration_low_speed_mode
|
||||
# 退出标定低速安全模式
|
||||
bool exit_calibration_low_speed_mode
|
||||
|
||||
# 操作原因
|
||||
string reason
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# =========================================================
|
||||
# 车辆内部时间同步状态
|
||||
# 发送方:车端电脑
|
||||
# 接收方:车间电脑桥接层 / 车端诊断
|
||||
# =========================================================
|
||||
|
||||
# 状态时间戳
|
||||
int64 hardware_timestamp_us
|
||||
|
||||
# 同步源,例如 ptp / ntp / external_truth / sim_clock
|
||||
string sync_source
|
||||
# 是否认为已同步
|
||||
bool synchronized
|
||||
|
||||
# 车端系统时钟相对传感器硬件时钟偏差
|
||||
float64 system_to_sensor_offset_ms
|
||||
# 车端系统时钟相对外部真值时钟偏差
|
||||
float64 system_to_external_truth_offset_ms
|
||||
# 同步抖动
|
||||
float64 jitter_ms
|
||||
# 近期最大时间同步误差
|
||||
float64 max_offset_ms
|
||||
# 时间同步链路延迟
|
||||
float64 sync_transport_latency_ms
|
||||
|
||||
# 说明
|
||||
string status_message
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0"?>
|
||||
<package format="3">
|
||||
<name>vehicle_internal_interfaces</name>
|
||||
<version>0.0.1</version>
|
||||
<description>ROS 2 interfaces for vehicle-computer to vehicle-controller internal communication.</description>
|
||||
|
||||
<maintainer email="user@example.com">user</maintainer>
|
||||
<license>Proprietary</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
<buildtool_depend>rosidl_default_generators</buildtool_depend>
|
||||
|
||||
<depend>calibration_vehicle_profile_interfaces</depend>
|
||||
|
||||
<exec_depend>rosidl_default_runtime</exec_depend>
|
||||
|
||||
<member_of_group>rosidl_interface_packages</member_of_group>
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
+3
-3
@@ -3,8 +3,8 @@ syntax = "proto3";
|
||||
package agv.calibration.common;
|
||||
|
||||
// ├── msg/
|
||||
// │ ├── Vector3d.msg
|
||||
// │ ├── Pose3dEuler.msg
|
||||
// │ ├── Vector3D.msg
|
||||
// │ ├── Pose3D.msg
|
||||
// │ ├── RequestHeader.msg
|
||||
// │ ├── ErrorCode.msg
|
||||
// │ ├── StandardResponse.msg
|
||||
@@ -263,4 +263,4 @@ message FileReference {
|
||||
message KeyValuePair {
|
||||
string key = 1; // 键
|
||||
string value = 2; // 值
|
||||
}
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package agv.calibration.transport;
|
||||
|
||||
// =========================================================
|
||||
// 文件作用:WiFi6/TCP 传输契约
|
||||
// 使用范围:
|
||||
// 1) Ubuntu 车间工控机与 Windows 车端代理之间的网络边界
|
||||
// 2) 仿真车端 agent 与真实车端 agent 需要共同遵守的帧号和通道约定
|
||||
// 3) 业务消息仍由 chassis/control/sensor/external_localization proto 定义
|
||||
// 说明:
|
||||
// 1) WiFi6 是承载网络,当前工程传输层使用 TCP 长/短连接
|
||||
// 2) 当前仿真实现 payload 使用 proto 字段名风格的 JSON
|
||||
// 3) 现场可切换为 protobuf binary,但必须保持本文件中的通道和帧号不变
|
||||
// =========================================================
|
||||
|
||||
// =========================================================
|
||||
// WiFi6 逻辑通道
|
||||
// =========================================================
|
||||
enum Wifi6Channel {
|
||||
WIFI6_CHANNEL_UNSPECIFIED = 0;
|
||||
WIFI6_CHANNEL_CHASSIS = 1; // 底盘标定动作和急停
|
||||
WIFI6_CHANNEL_CONTROL = 2; // 运控参数评估、轨迹跟踪
|
||||
WIFI6_CHANNEL_SENSOR = 3; // 车端传感器原始数据
|
||||
WIFI6_CHANNEL_EXTERNAL_POSE = 4; // 外部真值位姿输入到车端
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// 默认端口
|
||||
// 说明:
|
||||
// 1) WiFi6 边界默认只暴露一个车端 gateway 端口
|
||||
// 2) channel 只是协议里的逻辑通道,不应该被理解成“一类功能一个物理端口”
|
||||
// 3) 9001-9004 仅保留给仿真 router 背后的内部域服务或旧版兼容使用
|
||||
// =========================================================
|
||||
enum Wifi6DefaultPort {
|
||||
WIFI6_DEFAULT_PORT_UNSPECIFIED = 0;
|
||||
WIFI6_DEFAULT_PORT_VEHICLE_GATEWAY = 9000;
|
||||
WIFI6_LEGACY_INTERNAL_PORT_CHASSIS = 9001;
|
||||
WIFI6_LEGACY_INTERNAL_PORT_CONTROL = 9002;
|
||||
WIFI6_LEGACY_INTERNAL_PORT_SENSOR = 9003;
|
||||
WIFI6_LEGACY_INTERNAL_PORT_EXTERNAL_POSE = 9004;
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// TCP 载荷编码方式
|
||||
// =========================================================
|
||||
enum Wifi6PayloadEncoding {
|
||||
WIFI6_PAYLOAD_ENCODING_UNSPECIFIED = 0;
|
||||
WIFI6_PAYLOAD_ENCODING_JSON_PROTO_FIELD_NAMES = 1; // 当前仿真实现:JSON key 使用 proto 字段名
|
||||
WIFI6_PAYLOAD_ENCODING_PROTOBUF_BINARY = 2; // 现场高性能实现可选
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// TCP 连接方向
|
||||
// =========================================================
|
||||
enum Wifi6FrameDirection {
|
||||
WIFI6_FRAME_DIRECTION_UNSPECIFIED = 0;
|
||||
WIFI6_FRAME_DIRECTION_WORKSHOP_TO_VEHICLE = 1;
|
||||
WIFI6_FRAME_DIRECTION_VEHICLE_TO_WORKSHOP = 2;
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// TCP 帧号
|
||||
// 帧头格式固定为:
|
||||
// uint32 little-endian msg_type
|
||||
// uint32 little-endian payload_len
|
||||
// payload_len bytes payload
|
||||
// 注意:
|
||||
// 1) 这个 8 字节帧头不是 protobuf 序列化结果,而是传输层二进制头
|
||||
// 2) payload 的业务结构由本字段注释中对应的 proto 消息定义
|
||||
// 3) 逻辑 channel 由 msg_type 映射得到;同一个 gateway 端口根据 msg_type 做路由
|
||||
// =========================================================
|
||||
enum Wifi6FrameType {
|
||||
WIFI6_FRAME_TYPE_UNSPECIFIED = 0;
|
||||
|
||||
// 底盘域:chassis_calibration.proto / AgvCalibChassisService
|
||||
WIFI6_FRAME_CHASSIS_GET_READINESS_REQ = 1; // AgentReadinessRequest
|
||||
WIFI6_FRAME_CHASSIS_GET_READINESS_RSP = 2; // ChassisReadinessResponse
|
||||
WIFI6_FRAME_CHASSIS_MOTION_PRIMITIVE_REQ = 3; // MotionPrimitiveRequest
|
||||
WIFI6_FRAME_CHASSIS_MOTION_PRIMITIVE_RSP = 4; // ChassisJobResult
|
||||
WIFI6_FRAME_CHASSIS_EMERGENCY_BRAKE_REQ = 5; // Empty 或 EmergencyBrake 请求
|
||||
WIFI6_FRAME_CHASSIS_EMERGENCY_BRAKE_RSP = 6; // StandardResponse
|
||||
|
||||
// 运控域:control_calibration.proto / AgvCalibControlService
|
||||
WIFI6_FRAME_CONTROL_GET_READINESS_REQ = 11; // AgentReadinessRequest
|
||||
WIFI6_FRAME_CONTROL_GET_READINESS_RSP = 12; // ControlReadinessResponse
|
||||
WIFI6_FRAME_CONTROL_EVALUATION_REQ = 13; // ControllerEvaluationRequest
|
||||
WIFI6_FRAME_CONTROL_EVALUATION_RSP = 14; // ControlJobResult
|
||||
|
||||
// 传感器域:sensor_calibration.proto / AgvCalibSensorService
|
||||
WIFI6_FRAME_SENSOR_GET_READINESS_REQ = 21; // AgentReadinessRequest
|
||||
WIFI6_FRAME_SENSOR_GET_READINESS_RSP = 22; // SensorReadinessResponse
|
||||
WIFI6_FRAME_SENSOR_GET_LATEST_FRAME_REQ = 23; // StreamVehicleSensorDataRequest 的轻量轮询形态
|
||||
WIFI6_FRAME_SENSOR_GET_LATEST_FRAME_RSP = 24; // VehicleSensorFrame
|
||||
WIFI6_FRAME_SENSOR_LIST_SENSORS_REQ = 25; // Empty 或能力查询请求
|
||||
WIFI6_FRAME_SENSOR_LIST_SENSORS_RSP = 26; // Sensor 列表响应
|
||||
|
||||
// 外部真值位姿域:external_localization.proto / AgvCalibExternalPoseFeedService
|
||||
WIFI6_FRAME_EXTERNAL_POSE_PUSH_REQ = 31; // ExternalLocalizationTelemetry
|
||||
WIFI6_FRAME_EXTERNAL_POSE_PUSH_RSP = 32; // StandardResponse
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// TCP 帧头的 protobuf 表达
|
||||
// 说明:
|
||||
// 1) 这个 message 仅用于文档、测试、代码生成时表达契约
|
||||
// 2) 实际线上帧头仍是上面注释中定义的 8 字节小端二进制结构
|
||||
// =========================================================
|
||||
message Wifi6TcpFrameHeader {
|
||||
Wifi6FrameType msg_type = 1; // 对应 8 字节帧头中的 uint32 msg_type
|
||||
uint32 payload_len = 2; // 对应 8 字节帧头中的 uint32 payload_len
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// 逻辑通道端点
|
||||
// =========================================================
|
||||
message Wifi6ChannelEndpoint {
|
||||
Wifi6Channel channel = 1;
|
||||
Wifi6DefaultPort default_port = 2;
|
||||
Wifi6FrameDirection request_direction = 3;
|
||||
string canonical_proto_file = 4; // 例如 chassis_calibration.proto
|
||||
string canonical_service = 5; // 例如 AgvCalibChassisService
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// 请求 / 响应帧映射
|
||||
// =========================================================
|
||||
message Wifi6FrameMapping {
|
||||
Wifi6Channel channel = 1;
|
||||
Wifi6FrameType request_type = 2;
|
||||
Wifi6FrameType response_type = 3;
|
||||
Wifi6PayloadEncoding payload_encoding = 4;
|
||||
string request_message = 5; // proto 消息名
|
||||
string response_message = 6; // proto 消息名
|
||||
string rpc_name = 7; // 对齐的 RPC 名称;轻量轮询可为空
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// 当前工程默认契约版本
|
||||
// =========================================================
|
||||
message Wifi6TransportContract {
|
||||
string contract_version = 1; // 当前为 "wifi6_tcp_v1"
|
||||
Wifi6PayloadEncoding default_payload_encoding = 2;
|
||||
repeated Wifi6ChannelEndpoint endpoints = 3;
|
||||
repeated Wifi6FrameMapping frame_mappings = 4;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user