feat: developing.........

This commit is contained in:
li-shihao-code
2026-03-10 13:26:14 +08:00
parent 01960271f7
commit efc644a77d
46 changed files with 16331 additions and 748 deletions
@@ -0,0 +1,414 @@
import sys
import math
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QPushButton, QLabel, QGroupBox,
QTextEdit, QLineEdit, QCheckBox, QStackedWidget,
QListWidget, QListWidgetItem, QProgressBar, QComboBox, QGridLayout, QFormLayout)
from PyQt5.QtCore import Qt, QTimer, QSize, QPointF
from PyQt5.QtGui import QFont, QTextCursor, QPainter, QColor, QPen, QBrush, QPolygonF
# =================================================================
# 🗺️ 共享组件:车间 2D 数字孪生沙盘 (高对比白底版)
# =================================================================
class WorkshopMapWidget(QWidget):
def __init__(self):
super().__init__()
self.setMinimumSize(400, 300)
self.setStyleSheet("background-color: #FFFFFF; border: 1px solid #D1D5DB; border-radius: 6px;")
self.agv_x, self.agv_y, self.agv_yaw = 0.0, 0.0, 0.0
self.scale = 35.0
def update_pose(self, x, y, yaw):
self.agv_x, self.agv_y, self.agv_yaw = x, y, yaw
self.update()
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
w, h = self.width(), self.height()
cx, cy = w / 2, h / 2
# 浅灰网格
painter.setPen(QPen(QColor("#E5E7EB"), 1, Qt.DashLine))
for i in range(-20, 21):
px = cx + i * self.scale
painter.drawLine(int(px), 0, int(px), h)
py = cy - i * self.scale
painter.drawLine(0, int(py), w, int(py))
# 绝对坐标系原点
painter.setPen(QPen(QColor(220, 38, 38, 200), 2))
painter.drawLine(int(cx), int(cy), int(cx + 50), int(cy))
painter.setPen(QPen(QColor(22, 163, 74, 200), 2))
painter.drawLine(int(cx), int(cy), int(cx), int(cy - 50))
# 绘制 AGV (宝蓝色高亮)
pixel_x = cx + (self.agv_x * self.scale)
pixel_y = cy - (self.agv_y * self.scale)
painter.translate(pixel_x, pixel_y)
painter.rotate(-math.degrees(self.agv_yaw))
car_l, car_w = 1.2 * self.scale, 0.7 * self.scale
painter.setBrush(QBrush(QColor(37, 99, 235, 80)))
painter.setPen(QPen(QColor(37, 99, 235), 2))
poly = QPolygonF([QPointF(car_l/2, 0), QPointF(-car_l/2, -car_w/2), QPointF(-car_l/4, 0), QPointF(-car_l/2, car_w/2)])
painter.drawPolygon(poly)
# =================================================================
# 🖥️ 主控台:侧边栏 + 多页面栈架构
# =================================================================
class CalibrationDashboard(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("🚀 L4 AGV 自动化车间调度总控台 (v3.0 工业侧边栏版)")
self.resize(1350, 850)
# 🚨 全局样式:深色侧边栏 + 明亮工作区
self.setStyleSheet("""
QMainWindow { background-color: #F3F4F6; color: #1F2937; font-family: 'Microsoft YaHei', sans-serif; }
/* 侧边栏样式 */
QListWidget { background-color: #1E293B; color: #9CA3AF; border: none; font-size: 15px; font-weight: bold; outline: 0; padding-top: 10px;}
QListWidget::item { padding: 15px 20px; border-bottom: 1px solid #334155; }
QListWidget::item:hover { background-color: #334155; color: #F8FAFC; }
QListWidget::item:selected { background-color: #2563EB; color: #FFFFFF; border-left: 4px solid #60A5FA; }
/* 业务面板样式 */
QGroupBox { font-weight: bold; color: #1D4ED8; font-size: 14px; border: 1px solid #D1D5DB; border-radius: 6px; margin-top: 15px; background-color: #FFFFFF; }
QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 5px; color: #1D4ED8;}
QLabel { color: #1F2937; font-weight: bold; background: transparent; }
QLineEdit, QComboBox { background-color: #F9FAFB; border: 1px solid #D1D5DB; padding: 6px; border-radius: 4px; font-weight: bold;}
QPushButton { font-weight: bold; font-size: 14px; border-radius: 5px; padding: 10px; border: none; background-color: #E5E7EB; color: #1F2937;}
QPushButton:hover { background-color: #D1D5DB; }
QPushButton:disabled { background-color: #E5E7EB; color: #9CA3AF; }
QCheckBox { font-weight: bold; font-size: 13px; }
""")
self.sim_time = 0.0
self.is_connected = False
self.init_ui()
# 模拟外部真值发送高频坐标
self.sim_timer = QTimer(self)
self.sim_timer.timeout.connect(self.simulate_ground_truth)
def init_ui(self):
main_widget = QWidget()
self.setCentralWidget(main_widget)
main_layout = QHBoxLayout(main_widget)
main_layout.setContentsMargins(0, 0, 0, 0)
main_layout.setSpacing(0)
# ==========================================
# 🗂️ 1. 左侧导航栏 (Sidebar)
# ==========================================
sidebar_container = QWidget()
sidebar_container.setFixedWidth(240)
sidebar_container.setStyleSheet("background-color: #1E293B;")
sidebar_layout = QVBoxLayout(sidebar_container)
sidebar_layout.setContentsMargins(0, 0, 0, 0)
# 侧边栏顶部 Logo
lbl_logo = QLabel("🚀 AGV Calib Studio")
lbl_logo.setStyleSheet("color: #60A5FA; font-size: 18px; font-weight: bold; padding: 20px 10px; background-color: #0F172A;")
lbl_logo.setAlignment(Qt.AlignCenter)
sidebar_layout.addWidget(lbl_logo)
self.nav_list = QListWidget()
nav_items = [
"🏠 首页: 车辆会话与画像",
"⚙️ 阶段一: 底盘能力标定",
"🧠 阶段二: 运控参数寻优",
"📷 阶段三: 传感器走停拍"
]
for text in nav_items:
item = QListWidgetItem(text)
self.nav_list.addItem(item)
self.nav_list.currentRowChanged.connect(self.switch_page)
sidebar_layout.addWidget(self.nav_list)
# ==========================================
# 📑 2. 右侧工作区 (上: 堆叠页面, 下: 全局日志)
# ==========================================
right_container = QWidget()
right_layout = QVBoxLayout(right_container)
right_layout.setContentsMargins(15, 15, 15, 15)
self.stacked_widget = QStackedWidget()
# 实例化四个功能页面
self.page_home = self.create_page_home()
self.page_chassis = self.create_page_chassis()
self.page_control = self.create_page_control()
self.page_sensor = self.create_page_sensor()
self.stacked_widget.addWidget(self.page_home)
self.stacked_widget.addWidget(self.page_chassis)
self.stacked_widget.addWidget(self.page_control)
self.stacked_widget.addWidget(self.page_sensor)
# 💻 底部全局系统日志 (无论切到哪页,日志永远可见!)
grp_log = QGroupBox("💻 全局调度中心日志 (Session Console)")
lyt_log = QVBoxLayout(grp_log)
self.txt_log = QTextEdit()
self.txt_log.setReadOnly(True)
self.txt_log.setFont(QFont("Consolas", 11, QFont.Bold))
self.txt_log.setStyleSheet("background-color: #F8FAFC; color: #15803D; border: 1px solid #D1D5DB; padding: 5px;")
self.txt_log.setFixedHeight(150)
lyt_log.addWidget(self.txt_log)
right_layout.addWidget(self.stacked_widget, 1)
right_layout.addWidget(grp_log)
main_layout.addWidget(sidebar_container)
main_layout.addWidget(right_container)
self.nav_list.setCurrentRow(0)
self.append_log("✅ 自动化车间多页签 UI 挂载成功!")
self.append_log("📌 请在 [首页] 完成 Session 握手,并下发 Vehicle Profile。")
# ---------------------------------------------------------
# 📄 页面 1: 首页 (对应 vehicle_profile.proto)
# ---------------------------------------------------------
def create_page_home(self):
page = QWidget()
layout = QHBoxLayout(page)
left_lyt = QVBoxLayout()
grp_net = QGroupBox("🌐 1. 车端节点寻址与心跳")
lyt_net = QFormLayout(grp_net)
self.in_ip = QLineEdit("192.168.31.105")
lyt_net.addRow("车端 Agent IP:", self.in_ip)
btn_conn = QPushButton("📡 发送 HeartbeatRequest")
btn_conn.setStyleSheet("background-color: #16A34A; color: white;")
btn_conn.clicked.connect(self.mock_connect)
lyt_net.addRow("", btn_conn)
grp_prof = QGroupBox("📋 2. 车辆画像配置 (Vehicle Profile)")
lyt_prof = QFormLayout(grp_prof)
lyt_prof.addRow("Session ID:", QLineEdit("SES-20260308-01"))
lyt_prof.addRow("车辆唯一 ID:", QLineEdit("AGV-T01-Pro"))
cb_chassis = QComboBox()
cb_chassis.addItems(["DIFFERENTIAL (差速)", "ACKERMANN (阿克曼)", "MULTI_STEER (多舵轮)"])
lyt_prof.addRow("底盘构型:", cb_chassis)
cb_algo = QComboBox()
cb_algo.addItems(["PID + 纯追踪 (PP)", "MPC", "LQR"])
lyt_prof.addRow("运控算法:", cb_algo)
btn_recipe = QPushButton("📝 提交画像生成 Recipe 任务树")
btn_recipe.setStyleSheet("background-color: #2563EB; color: white; padding: 15px;")
btn_recipe.clicked.connect(lambda: self.append_log("✅ 车辆画像下发成功!标定配方 (Recipe) 已激活,请按序流转。"))
left_lyt.addWidget(grp_net)
left_lyt.addWidget(grp_prof)
left_lyt.addWidget(btn_recipe)
left_lyt.addStretch()
right_lyt = QVBoxLayout()
grp_map = QGroupBox("📡 外部真值绝对地图 (Ground Truth)")
lyt_map = QVBoxLayout(grp_map)
self.lbl_pose_home = QLabel("📍 绝对位姿 -> X: 0.00 | Y: 0.00 | Yaw: 0°")
self.lbl_pose_home.setStyleSheet("color: #0369A1; font-size: 14px;")
self.map_widget_home = WorkshopMapWidget()
lyt_map.addWidget(self.lbl_pose_home)
lyt_map.addWidget(self.map_widget_home)
right_lyt.addWidget(grp_map)
layout.addLayout(left_lyt, 1)
layout.addLayout(right_lyt, 1)
return page
# ---------------------------------------------------------
# 📄 页面 2: 底盘标定 (对应 agv_calib_chassis.proto)
# ---------------------------------------------------------
def create_page_chassis(self):
page = QWidget()
layout = QHBoxLayout(page)
left_lyt = QVBoxLayout()
grp_act = QGroupBox("⚙️ 动作原语长任务 (StartMotionPrimitive)")
lyt_act = QVBoxLayout(grp_act)
actions = ["⬆️ 测距: 直线行驶 5 米", "🔄 测角: 原地自转 360°", "〰️ 测死区: 舵角扫频测试"]
for act in actions:
b = QPushButton(act)
b.setStyleSheet("background-color: #4F46E5; color: white;")
b.clicked.connect(lambda checked, t=act: self.append_log(f"🚀 提交底盘长任务: {t}。获得 JobID: JOB-CH-991"))
lyt_act.addWidget(b)
lyt_act.addStretch()
btn_estop = QPushButton("🛑 触发系统级急停 (Emergency Brake)")
btn_estop.setStyleSheet("background-color: #DC2626; color: white; padding: 15px;")
lyt_act.addWidget(btn_estop)
left_lyt.addWidget(grp_act)
right_lyt = QVBoxLayout()
grp_tel = QGroupBox("🌊 底盘 50Hz 遥测流 (StreamChassisTelemetry)")
lyt_tel = QVBoxLayout(grp_tel)
self.lbl_fl = QLabel("FL 模块: RPM: 0.0 | 脉冲: 0")
self.lbl_fr = QLabel("FR 模块: RPM: 0.0 | 脉冲: 0")
for lbl in [self.lbl_fl, self.lbl_fr]:
lbl.setFont(QFont("Consolas", 14, QFont.Bold))
lyt_tel.addWidget(lbl)
self.map_widget_chas = WorkshopMapWidget()
lyt_tel.addWidget(self.map_widget_chas)
right_lyt.addWidget(grp_tel)
layout.addLayout(left_lyt, 1)
layout.addLayout(right_lyt, 1)
return page
# ---------------------------------------------------------
# 📄 页面 3: 运控寻优 (对应 agv_calib_control.proto)
# ---------------------------------------------------------
def create_page_control(self):
page = QWidget()
layout = QHBoxLayout(page)
left_lyt = QVBoxLayout()
grp_inj = QGroupBox("🧠 控制参数热注入 (InjectControllerParameters)")
lyt_inj = QFormLayout(grp_inj)
lyt_inj.addRow("横向 Kp:", QLineEdit("1.5"))
lyt_inj.addRow("横向 Ki:", QLineEdit("0.0"))
lyt_inj.addRow("前瞻距离 (Ld):", QLineEdit("1.2"))
btn_inj = QPushButton("💉 热注入车端内存")
btn_inj.setStyleSheet("background-color: #D97706; color: white;")
btn_inj.clicked.connect(lambda: self.append_log("💉 参数已注入车端,当前版本: Ver-1.0.2"))
lyt_inj.addRow(btn_inj)
grp_eval = QGroupBox("🏎️ 闭环考题评估 (StartControllerEvaluation)")
lyt_eval = QVBoxLayout(grp_eval)
btn_eval = QPushButton("🚗 跑 S 型测试轨迹测 RMSE")
btn_eval.setStyleSheet("background-color: #059669; color: white;")
btn_eval.clicked.connect(lambda: self.append_log("🚗 轨迹跟踪 Job 已创建,正在收集 ControlTelemetry..."))
lyt_eval.addWidget(btn_eval)
left_lyt.addWidget(grp_inj)
left_lyt.addWidget(grp_eval)
left_lyt.addStretch()
right_lyt = QVBoxLayout()
grp_map = QGroupBox("📉 实时闭环循迹监控")
lyt_map = QVBoxLayout(grp_map)
self.map_widget_ctrl = WorkshopMapWidget()
lyt_map.addWidget(self.map_widget_ctrl)
right_lyt.addWidget(grp_map)
layout.addLayout(left_lyt, 1)
layout.addLayout(right_lyt, 1)
return page
# ---------------------------------------------------------
# 📄 页面 4: 传感器标定 (对应 agv_calib_sensor.proto)
# ---------------------------------------------------------
def create_page_sensor(self):
page = QWidget()
layout = QVBoxLayout(page)
grp_cap = QGroupBox("📷 走停拍与同步锁存 (SynchronizedCapture)")
lyt_cap = QVBoxLayout(grp_cap)
h_checks = QHBoxLayout()
h_checks.addWidget(QCheckBox("前视相机 (cam_front)"))
h_checks.addWidget(QCheckBox("机械臂相机 (arm_cam)"))
h_checks.addWidget(QCheckBox("3D激光雷达 (lidar_top)"))
lyt_cap.addLayout(h_checks)
h_btns = QHBoxLayout()
btn_move = QPushButton("🚙 1. 移动至底盘/机械臂标定位")
btn_move.setStyleSheet("background-color: #2563EB; color: white;")
btn_move.clicked.connect(lambda: self.append_log("🚙 调度 Job 已发: 底盘与机械臂正在前往联合观测点..."))
btn_trig = QPushButton("📸 2. 发起硬同步锁存")
btn_trig.setStyleSheet("background-color: #DB2777; color: white;")
btn_trig.clicked.connect(lambda: self.append_log("📸 锁存成功!CaptureID: CAP-778899"))
h_btns.addWidget(btn_move)
h_btns.addWidget(btn_trig)
lyt_cap.addLayout(h_btns)
grp_down = QGroupBox("📥 流式文件切块下载 (DownloadCapturedArtifact)")
lyt_down = QVBoxLayout(grp_down)
lyt_down.addWidget(QLabel("Job 状态:轮询拉取 LiDAR 压缩包..."))
self.prog_bar = QProgressBar()
self.prog_bar.setValue(0)
btn_dl = QPushButton("⬇️ 根据 CaptureID 启动文件流传输")
btn_dl.setStyleSheet("background-color: #7C3AED; color: white;")
btn_dl.clicked.connect(self.mock_download)
lyt_down.addWidget(self.prog_bar)
lyt_down.addWidget(btn_dl)
layout.addWidget(grp_cap)
layout.addWidget(grp_down)
layout.addStretch()
return page
# =========================================================
# 🔄 UI 交互与动画逻辑
# =========================================================
def switch_page(self, index):
self.stacked_widget.setCurrentIndex(index)
page_names = ["[首页: 画像配置]", "[P1: 底盘运动学]", "[P2: 运控闭环]", "[P3: 传感器外参]"]
self.append_log(f"👁️ UI 视图已切换至: {page_names[index]}")
def mock_connect(self):
if not self.is_connected:
self.append_log(f"🔄 发送 HeartbeatRequest -> {self.in_ip.text()} ...")
self.append_log("✅ 收到 HeartbeatResponse: 车辆正常,vehicle_ready = True")
self.is_connected = True
self.sim_timer.start(33)
else:
self.append_log("⚠️ 会话连接已物理断开。")
self.is_connected = False
self.sim_timer.stop()
def mock_download(self):
self.append_log("📥 创建 LRO 异步下载任务,后台轮询 JobStatus...")
self.prog_bar.setValue(0)
self.dl_timer = QTimer()
self.dl_timer.timeout.connect(self._step_dl)
self.dl_timer.start(50)
def _step_dl(self):
val = self.prog_bar.value() + 4
self.prog_bar.setValue(val)
if val >= 100:
self.dl_timer.stop()
self.append_log("✅ JobStatus = SUCCEEDED, 数据文件安全落盘,Digest校验通过!")
def append_log(self, text):
self.txt_log.append(text)
self.txt_log.moveCursor(QTextCursor.End)
def simulate_ground_truth(self):
# 假真值发生器:让小车跑一个平滑的 8 字形
self.sim_time += 0.05
x = 4.0 * math.sin(self.sim_time * 0.5)
y = 2.0 * math.sin(self.sim_time)
dx, dy = 2.0 * math.cos(self.sim_time * 0.5), 2.0 * math.cos(self.sim_time)
yaw = math.atan2(dy, dx)
pose_str = f"📍 绝对位姿 -> X: {x:+.2f}m | Y: {y:+.2f}m | Yaw: {math.degrees(yaw):+05.1f}°"
self.lbl_pose_home.setText(pose_str)
# 🚨 魔法:同步更新所有页面的地图实例!
self.map_widget_home.update_pose(x, y, yaw)
self.map_widget_chas.update_pose(x, y, yaw)
self.map_widget_ctrl.update_pose(x, y, yaw)
# 模拟底盘页面的高频遥测数字
if self.stacked_widget.currentIndex() == 1:
rpm = abs(80 * math.cos(self.sim_time))
self.lbl_fl.setText(f"FL 模块: RPM: {rpm:.1f} | 脉冲: {int(self.sim_time*1000)}")
self.lbl_fr.setText(f"FR 模块: RPM: {rpm:.1f} | 脉冲: {int(self.sim_time*1000)}")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = CalibrationDashboard()
window.show()
sys.exit(app.exec_())
@@ -1,15 +0,0 @@
# === [Goal] 行为树给网关下发的下载任务 ===
int64 capture_timestamp_us # 刚才拿到的取件码
string sensor_id # 例如 "cam_front"
uint8 DATA_TYPE_IMAGE = 0
uint8 DATA_TYPE_POINTCLOUD = 1
uint8 data_type # 告诉网关下图片还是下点云
string save_directory # 保存的 Ubuntu 目录,如 "/tmp/calib_data"
---
# === [Result] 网关下完后返回给行为树的结果 ===
bool success
string saved_file_path # 🚨 终极目的:返回存好的绝对路径 (如 /tmp/calib_data/cam_front_167888.png)
string error_message
---
# === [Feedback] 网关实时汇报的下载进度 ===
uint64 downloaded_bytes # 已下载的字节数 (供行为树监控是否卡死)
Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

@@ -1,6 +0,0 @@
string camera_id
float64 fx
float64 fy
float64 cx
float64 cy
float64[] dist_coeffs
@@ -1,10 +0,0 @@
int64 hardware_timestamp_us
float64 odom_x_m
float64 odom_y_m
float64 odom_yaw_rad
float64 feedback_linear_vel_ms
float64 feedback_angular_vel_rads
float64 left_motor_current_amp
float64 right_motor_current_amp
float64 steering_motor_current_amp
float64 cmd_steering_output
@@ -0,0 +1,31 @@
# =========================================================
# 文件作用:统一错误码定义
# 对应 protoErrorCode
# 说明:
# 1) ROS2 msg 不支持 proto 的 enum 语法
# 2) 因此这里使用 常量 + code 字段 的方式表达
# 3) 其他 msg / srv 中的 error_code 字段统一使用 uint16
# =========================================================
uint16 ERROR_CODE_UNSPECIFIED=0
uint16 OK=1
uint16 INVALID_ARGUMENT=2
uint16 INVALID_STATE=3
uint16 VEHICLE_BUSY=4
uint16 NOT_READY=5
uint16 TIMEOUT=6
uint16 NETWORK_LOSS=7
uint16 SAFETY_TRIGGERED=8
uint16 HARDWARE_FAULT=9
uint16 FILE_NOT_FOUND=10
uint16 CHECKSUM_MISMATCH=11
uint16 INTERNAL_ERROR=12
uint16 UNSUPPORTED_CAPABILITY=13
uint16 RESOURCE_LOCKED=14
uint16 MANUAL_CONFIRM_REQUIRED=15
uint16 APPROVAL_REQUIRED=16
uint16 VALIDATION_FAILED=17
uint16 ROLLBACK_REQUIRED=18
uint16 DATA_QUALITY_INSUFFICIENT=19
uint16 code # 当前错误码值
@@ -0,0 +1,8 @@
# =========================================================
# 文件作用:文件摘要
# 对应 protoFileDigest
# 作用:用于参数包、URDF、数据文件校验
# =========================================================
string checksum_type # 摘要算法,例如 sha256
string checksum_value # 摘要值
@@ -0,0 +1,11 @@
# =========================================================
# 文件作用:文件引用
# 对应 protoFileReference
# 作用:用于报告、参数包、数据集、日志包等产物追溯
# =========================================================
string file_name # 文件名
string file_uri # 文件路径 / URI
int64 size_bytes # 文件大小(字节)
string description # 文件说明
FileDigest digest # 文件摘要
@@ -1,13 +0,0 @@
int64 hardware_timestamp_us
int64 encoder_ticks_fl
int64 encoder_ticks_fr
int64 encoder_ticks_rl
int64 encoder_ticks_rr
float64 actual_steer_angle_front_deg
float64 actual_steer_angle_rear_deg
float64 current_fl_amp
float64 current_fr_amp
float64 current_rl_amp
float64 current_rr_amp
float64 current_steer_front_amp
uint32 driver_error_code
@@ -0,0 +1,12 @@
# =========================================================
# 文件作用:长任务受理响应
# 对应 protoJobAccepted
# 作用:耗时任务先返回 job_id,后续再查询状态
# 发送方:服务端
# 接收方:Ubuntu 车间电脑
# =========================================================
bool accepted # 是否受理成功
uint16 error_code # 未受理时的错误码,取值参考 ErrorCode.msg
string message # 说明
string job_id # 长任务 ID
@@ -0,0 +1,10 @@
# =========================================================
# 文件作用:长任务查询请求
# 对应 protoJobQuery
# 作用:通过 job_id 查询执行状态
# 发送方:Ubuntu 车间电脑
# 接收方:Linux 服务 或 Windows 车端代理
# =========================================================
RequestHeader header # 请求头
string job_id # 任务 ID
@@ -0,0 +1,19 @@
# =========================================================
# 文件作用:长任务状态定义
# 对应 protoJobState
# 说明:
# 1) ROS2 msg 不支持 proto 的 enum 语法
# 2) 因此这里使用 常量 + state 字段 的方式表达
# =========================================================
uint8 JOB_STATE_UNSPECIFIED=0
uint8 PENDING=1
uint8 RUNNING=2
uint8 WAITING_MANUAL=3
uint8 WAITING_APPROVAL=4
uint8 SUCCEEDED=5
uint8 FAILED=6
uint8 CANCELED=7
uint8 ROLLED_BACK=8
uint8 state # 当前任务状态值
@@ -0,0 +1,15 @@
# =========================================================
# 文件作用:长任务状态响应
# 对应 protoJobStatus
# 作用:返回任务执行状态、进度、错误信息
# 发送方:服务端
# 接收方:Ubuntu 车间电脑
# =========================================================
string job_id # 任务 ID
uint8 state # 当前状态,取值参考 JobState.msg
float64 progress # 进度,建议范围 0.0 ~ 1.0
uint16 error_code # 当前错误码,取值参考 ErrorCode.msg
string message # 状态说明
int64 server_timestamp_us # 服务端时间戳
bool safe_to_retry # 是否适合自动重试
@@ -0,0 +1,8 @@
# =========================================================
# 文件作用:键值对
# 对应 protoKeyValuePair
# 作用:用于扩展元数据,避免每次新增少量字段都改协议
# =========================================================
string key # 键
string value # 值
@@ -0,0 +1,16 @@
# =========================================================
# 文件作用:六自由度位姿
# 对应 protoPose3D
# 作用:
# 1) 用于表达 base_link、传感器、外部定位坐标系之间的位姿关系
# 2) 平移单位为米
# 3) 旋转单位为弧度
# 4) 采用 xyz + rpy 表达
# =========================================================
float64 x_m # X 方向平移(m)
float64 y_m # Y 方向平移(m)
float64 z_m # Z 方向平移(m)
float64 roll_rad # 绕 X 轴旋转(rad
float64 pitch_rad # 绕 Y 轴旋转(rad
float64 yaw_rad # 绕 Z 轴旋转(rad
@@ -0,0 +1,15 @@
# =========================================================
# 文件作用:通用请求头
# 对应 protoRequestHeader
# 作用:带齐一次会话、任务、车辆、请求追踪信息
# 发送方:通常为 Ubuntu 车间电脑
# 接收方:Linux 内部服务 或 Windows 车端代理
# =========================================================
string session_id # 本次整车标定会话 ID
string task_id # 当前任务 ID / 阶段 ID
string vehicle_id # 车辆 ID
string request_id # 本次请求唯一 ID
int64 client_send_timestamp_us # 请求发送时间戳(微秒)
string operator_id # 操作员工号 / 工位号
string workshop_host # Ubuntu 车间电脑主机名
@@ -1,8 +0,0 @@
string source_frame
string target_frame
float64 trans_x_mm
float64 trans_y_mm
float64 trans_z_mm
float64 roll_deg
float64 pitch_deg
float64 yaw_deg
@@ -0,0 +1,14 @@
# =========================================================
# 文件作用:通用短响应
# 对应 protoStandardResponse
# 作用:用于同步服务调用的标准响应
# 发送方:服务端
# 接收方:调用方
# 说明:
# 1) error_code 使用 uint16
# 2) 取值参考 ErrorCode.msg 中定义的常量
# =========================================================
bool success # 是否成功
uint16 error_code # 错误码,取值参考 ErrorCode.msg
string message # 说明文字
@@ -1,5 +0,0 @@
float64 x_m
float64 y_m
float64 yaw_rad
float64 target_speed_ms
float64 curvature
@@ -0,0 +1,9 @@
# =========================================================
# 文件作用:三维向量
# 对应 protoVector3D
# 作用:用于表达三轴偏置、平移量、加速度等三维数据
# =========================================================
float64 x # X 轴分量
float64 y # Y 轴分量
float64 z # Z 轴分量
@@ -0,0 +1,949 @@
# 自动化标定车间 Proto 协同工作说明
[![Protocol](https://img.shields.io/badge/Protocol-Protocol%20Buffers-blue)](https://developers.google.com/protocol-buffers)
[![Platform](https://img.shields.io/badge/Platform-Ubuntu%20%7C%20Linux%20%7C%20Windows-green)](https://ubuntu.com)
[![Status](https://img.shields.io/badge/Status-Design%20Phase-orange)](./)
> **核心定位**:面向自动化标定车间的分层协议设计,实现车间总控、专业域服务与车端代理的协同工作。
---
## 📋 目录
- [1. 项目目标](#1-项目目标)
- [2. 七个 Proto 文件的职责划分](#2-七个-proto-文件的职责划分)
- [2.1 calibration_common.proto](#21-calibration_commonproto)
- [2.2 vehicle_profile.proto](#22-vehicle_profileproto)
- [2.3 external_localization.proto](#23-external_localizationproto)
- [2.4 chassis_calibration.proto](#24-chassis_calibrationproto)
- [2.5 sensor_calibration.proto](#25-sensor_calibrationproto)
- [2.6 control_calibration.proto](#26-control_calibrationproto)
- [2.7 workshop_orchestration.proto](#27-workshop_orchestrationproto)
- [3. 七个 Proto 的依赖关系](#3-七个-proto-的依赖关系)
- [4. 协同架构图](#4-协同架构图)
- [5. 自动化标定车间的完整逻辑流程](#5-自动化标定车间的完整逻辑流程)
- [6. 为什么一定要拆成这 7 个 Proto](#6-为什么一定要拆成这-7-个-proto)
- [7. 从通信形态角度看,这 7 个 Proto 如何使用](#7-从通信形态角度看这-7-个-proto-如何使用)
- [8. 七个 Proto 在一次完整会话中的协同时序](#8-七个-proto-在一次完整会话中的协同时序)
- [9. 七个 Proto 的工程分层建议](#9-七个-proto-的工程分层建议)
- [10. 当前这套 Proto 的核心协同原则](#10-当前这套-proto-的核心协同原则)
- [11. 落地实现时还必须注意的点](#11-落地实现时还必须注意的点)
- [12. 推荐的理解方式](#12-推荐的理解方式)
- [13. 总结](#13-总结)
- [14. 后续建议](#14-后续建议)
---
## 1. 项目目标
本项目面向**自动化标定车间**场景,目标是通过一组分层设计的 `.proto` 协议文件,定义:
- 车间上位机(Ubuntu / Linux)内部各服务之间的通信接口
- 车间上位机与车端执行代理(Windows)之间的通信接口
- 自动化调度、车辆建模、外部定位、底盘标定、传感器标定、运控参数标定等模块之间的职责边界
- 整车自动化标定流程的统一任务组织方式、状态表达方式、结果归档方式
这一套协议的核心设计思想不是"把所有逻辑塞到一个服务里",而是:
1. **车间总控负责流程编排**
2. **各标定域服务负责专业能力**
3. **车端代理只负责动作执行、数据采集、参数落盘**
4. **所有流程都围绕统一的会话、任务、车辆画像、作业状态进行协同**
---
## 2. 七个 Proto 文件的职责划分
本工程共包含 **7** 个核心 `.proto` 文件,它们共同构成自动化标定车间的通信协议层。
---
### 2.1 calibration_common.proto
**作用:公共基础协议定义**
这是整个系统的底层公共协议文件,所有其他 proto 都会依赖它。
**主要定义内容:**
- 通用请求头 `RequestHeader`
- 空消息、标准响应、错误码
- 长任务状态表达 `JobAccepted` / `JobQuery` / `JobStatus`
- 心跳请求与响应 `HeartbeatRequest` / `HeartbeatResponse`
- 文件摘要 `FileDigest`
- 统一错误码 `ErrorCode`
- 统一任务状态 `JobState`
**解决的共性问题:**
- 一次请求是谁发起的
- 属于哪个标定会话
- 当前针对哪台车
- 这个任务现在处于什么状态
- 返回的是不是成功
- 错误是否可重试
- 文件、参数包如何做摘要校验
> **一句话理解**`calibration_common.proto` 是整个自动化标定车间协议体系的"地基"。
---
### 2.2 vehicle_profile.proto
**作用:车辆画像与能力建模**
这个文件负责表达"待标定对象到底是什么"。
**主要定义内容:**
- 车辆底盘类型
- 是否带机械臂
- 传感器配置类型
- 控制器算法配置
- 车辆基础几何与命名信息
- URDF 导入关联信息
- 车辆能力声明
**解决的对象建模问题:**
- 当前进站的是哪种车
- 是阿克曼、差速、单舵轮还是多舵轮
- 车上有哪些传感器
- 机械臂有没有
- 需要做哪些标定任务
- 底盘标定该走哪套动作原语
- 运控参数标定应该面向哪些控制器
> **一句话理解**`vehicle_profile.proto` 负责回答"这台车是谁、长什么样、具备什么能力"。
---
### 2.3 external_localization.proto
**作用:外部定位能力与测量结果接口**
这个文件负责定义自动化车间中"外部定位系统"相关的协议。
这里的外部定位通常不是车上自带定位,而是车间里的外部测量系统,例如:
- 动捕系统
- 激光跟踪仪
- 高精度全站仪
- 外部相机阵列
- 标定工装测量系统
**主要职责:**
- 请求外部定位系统开始测量
- 获取目标位姿 / 轨迹
- 获取参考基准坐标
- 记录测量结果质量
- 提供给底盘、传感器、运控标定使用的真值参考
它在整个车间流程中是一个非常关键的"**真值来源模块**"。
> **一句话理解**`external_localization.proto` 负责给整个自动化标定车间提供高精度外部参考。
---
### 2.4 chassis_calibration.proto
**作用:底盘标定执行代理协议**
这个文件主要描述**车间上位机 → Windows 车端代理**之间,底盘标定相关的执行接口。
**主要内容:**
- 底盘工作模式切换
- 车端底盘能力查询
- 动作原语执行请求
- 底盘遥测流
- 底盘参数写入
- 当前生效参数查询
- 紧急刹停
**典型动作原语:**
- 直线行驶
- 圆弧行驶
- 原地旋转
- 舵角扫动
**典型遥测:**
- 里程计
- 轮速
- 编码器
- 舵角
- 驱动器状态
- 急停状态
这个文件本质上不是"求解底盘参数",而是定义:
> Linux 上位机如何命令车端去做底盘动作,并拿回原始数据,再由 Linux 进行参数求解。
> **一句话理解**`chassis_calibration.proto` 是底盘标定的"执行与数据回传协议"。
---
### 2.5 sensor_calibration.proto
**作用:传感器标定任务与结果接口**
这个文件负责定义传感器标定域相关协议。
**覆盖范围:**
- 下视相机
- 前视相机
- 机械臂相机(眼在手上 / 眼在手外)
- 2D 激光雷达
- 3D 激光雷达
- IMU
**典型标定内容:**
- 相机内参
- IMU 内参(如 `accel_bias` / `gyro_bias`
- 传感器外参(相对于 `base_link`
- 标定数据采集任务
- 标定结果质量评估
- 标定结果写入与查询
这个文件通常会与 `vehicle_profile.proto``external_localization.proto``workshop_orchestration.proto` 有很强协作关系。
因为是否要做某种传感器标定、该使用哪种工装、是否需要机械臂配合、外部真值从哪里来,都取决于车辆画像和整体流程编排。
> **一句话理解**`sensor_calibration.proto` 负责定义传感器标定的任务、数据和结果。
---
### 2.6 control_calibration.proto
**作用:运控参数调优标定执行代理协议**
这个文件负责定义车辆控制参数调优相关接口。
这里的"运控参数标定"一般不是底盘几何标定,而是:
- 横向控制器参数标定
- 纵向控制器参数标定
**典型控制算法:**
- PID
- MPC
- LQR
- PPPure Pursuit
并且要明确拆分为:
- **横向控制器**
- **纵向控制器**
因为这两类控制目标不同,参数含义不同,评估指标也不同。
**主要功能:**
- 控制工作模式切换
- 参数热加载
- 控制评估任务启动
- 遥测流回传
- 参数固化
- 当前参数查询
- 紧急停车
**常见评估任务:**
- 轨迹跟踪任务
- 速度阶跃任务
**常见回传指标:**
- 横向误差
- 航向误差
- 速度误差
- 转向输出
- 油门 / 驱动输出
- 饱和状态
> **一句话理解**`control_calibration.proto` 负责让 Linux 调参服务可以驱动车端执行控制测试并闭环评估。
---
### 2.7 workshop_orchestration.proto
**作用:车间总控编排协议**
这是整个自动化标定车间里最核心的"总控协议"。
它不是具体做底盘求解,也不是具体做传感器求解,而是负责编排整条流程:
- 创建整车标定会话
- 加载车辆画像
- 校验前置条件
- 决定本次需要执行哪些任务
- 按阶段推进流程
- 跟踪每个子任务状态
- 统一处理失败、重试、跳过、中止
- 归档全部标定结果
- 输出最终标定报告
它会与其余 6 个 proto 对应的服务产生协作关系。
你可以把它理解成:
> 自动化标定车间的"总导演"和"状态机主控器"
> **一句话理解**`workshop_orchestration.proto` 决定"先做什么、后做什么、谁调用谁、失败了怎么办"。
---
## 3. 七个 Proto 的依赖关系
整体依赖关系可以概括为:
| Proto 文件 | 被依赖方 | 作用 |
|:-----------|:---------|:-----|
| `calibration_common.proto` | 所有其他 proto | 公共基础能力 |
| `vehicle_profile.proto` | orchestration、底盘、传感器、运控标定 | 车辆建模依据 |
| `external_localization.proto` | 底盘、传感器、运控标定 | 外部真值/参考测量 |
| `chassis_calibration.proto` | `workshop_orchestration.proto` | 被总控调度 |
| `sensor_calibration.proto` | `workshop_orchestration.proto` | 被总控调度 |
| `control_calibration.proto` | `workshop_orchestration.proto` | 被总控调度 |
| `workshop_orchestration.proto` | - | 作为总控层,组织全部流程 |
---
## 4. 协同架构图
下面是一个推荐的逻辑协同图:
```mermaid
flowchart TD
A[工作人员在 Ubuntu 车间电脑配置车辆信息] --> B[vehicle_profile.proto
生成车辆画像]
B --> C[workshop_orchestration.proto
创建整车标定会话]
C --> D[前置检查 / 能力校验 / 安全检查]
D --> E[external_localization.proto
初始化外部定位系统]
E --> F[chassis_calibration.proto
执行底盘动作与回传遥测]
E --> G[sensor_calibration.proto
执行传感器采集与求解]
E --> H[control_calibration.proto
执行轨迹/速度测试并评估]
F --> C
G --> C
H --> C
C --> I[统一汇总结果]
I --> J[参数写入 / 版本归档 / 生成报告]
```
---
## 5. 自动化标定车间的完整逻辑流程
下面从工程落地角度梳理一遍完整的自动化流程。
---
### 阶段 1:车辆入站与基础配置
工作人员在 Ubuntu 车间电脑上完成基础配置:
- 输入车辆 ID
- 选择底盘类型
- 选择是否带机械臂
- 选择搭载的传感器类型
- 选择控制器类型(横向 / 纵向)
- 导入 URDF(可选但强烈建议)
- 选择本次需要执行的标定项目
此时由 `vehicle_profile.proto` 完成车辆画像建模。
**输出结果:**
- 一个完整的车辆画像对象
- 当前标定会话的基础上下文
---
### 阶段 2:创建整车标定会话
`workshop_orchestration.proto` 创建一轮完整的标定会话。
这里通常会生成:
- `session_id`
- 标定计划
- 子任务列表
- 初始状态机节点
同时将统一的 `RequestHeader` 注入后续所有子任务请求。
**输出结果:**
- 一次完整的整车标定上下文
- 后续所有模块共享同一会话 ID
---
### 阶段 3:前置检查
总控模块调用各子服务检查前置条件:
- 车辆是否在线
- 车端代理是否在线
- 外部定位系统是否在线
- 传感器是否可通信
- 驱动器是否健康
- 急停状态是否释放
- 当前车辆能力是否满足目标任务
这里会大量使用:
- `Heartbeat`
- `StandardResponse`
- `JobStatus`
- 车辆能力查询
- 外部定位能力查询
若前置检查失败,则流程不能继续。
---
### 阶段 4:外部定位系统建立参考
`external_localization.proto` 驱动外部定位系统建立参考坐标框架。
例如:
- 建立车间世界坐标系
- 识别待标定车辆
- 获取基准工装位姿
- 连续输出外部真值位姿
- 检查测量质量是否达标
这一阶段输出的是后续标定需要依赖的"真值"或"参考值"。
---
### 阶段 5:底盘标定
`workshop_orchestration.proto` 调度 `chassis_calibration.proto` 所对应的服务进行底盘标定。
**基本流程:**
1. 切换到底盘标定模式
2. 查询底盘能力
3. 按底盘类型下发动作原语
4. 车端执行动作
5. 车端持续回传遥测
6. Linux 求解底盘参数
7. 参数写入车端
8. 查询写入后的生效结果
**不同底盘类型的动作组合:**
| 底盘类型 | 动作组合 |
|:---------|:---------|
| 阿克曼 | 直线、圆弧、舵角扫动 |
| 差速 | 直线、原地旋转、圆弧 |
| 单舵轮 | 直线、圆弧、舵角扫动 |
| 多舵轮 | 直线、旋转、单模块扫动或联动测试 |
**底盘标定解决的问题:**
- 直线跑偏
- 曲率误差
- 轮径补偿
- 轴距/轮距有效值
- 编码器比例
- 舵角零偏
- 模块安装误差
---
### 阶段 6:传感器标定
`workshop_orchestration.proto` 调度 `sensor_calibration.proto` 进行传感器标定。
**常见流程:**
1. 根据车辆画像,识别本车有哪些传感器需要标定
2. 检查工装和采集条件
3. 触发采集任务
4. 回传采集状态和原始数据引用
5. Linux 侧完成求解
6. 输出标定参数
7. 参数写入配置系统
8. 校验结果质量是否达标
**两大类参数:**
#### 6.1 内参
例如:
- 相机内参矩阵
- 畸变参数
- IMU 的 `accel_bias`
- IMU 的 `gyro_bias`
#### 6.2 外参
例如:
- 各传感器相对 `base_link` 的位姿
- 手眼关系
- 机械臂末端与相机的位姿关系
- 外部定位参考坐标系到车体系的关系
---
### 阶段 7:运控参数标定
`workshop_orchestration.proto` 调度 `control_calibration.proto` 进行运控参数调优。
> **注意**:控制器必须拆成横向与纵向两大类来表达。
#### 横向控制器
例如:
- PID(横向误差 / 航向误差)
- MPC
- LQR
- Pure Pursuit
#### 纵向控制器
例如:
- PID(速度环)
- MPC 速度规划控制
- 其他速度控制算法
**典型流程:**
1. 切换到调参模式
2. 下发一版候选参数
3. 启动车端评估任务
4. 执行轨迹跟踪或速度阶跃测试
5. 回传遥测数据
6. Linux 计算性能指标
7. 决定继续迭代还是固化参数
8. 固化最终参数版本
**评估指标:**
- 横向误差
- 航向误差
- 速度误差
- 超调量
- 稳态误差
- 控制量抖动
- 饱和比例
- 收敛时间
---
### 阶段 8:统一收敛判定与结果归档
当底盘标定、传感器标定、运控标定都完成后,由总控模块统一判定:
- 是否全部成功
- 是否存在部分成功
- 是否允许跳过某项后完成整体会话
- 是否需要人工复核
- 是否需要重试某一子项
然后进行:
- 参数版本归档
- 文件摘要记录
- 标定结果持久化
- 会话状态闭环
- 生成最终报告
---
## 6. 为什么一定要拆成这 7 个 Proto
从自动化标定车间落地的角度,这样拆分有几个核心优点。
---
### 6.1 降低耦合
如果把所有内容塞到一个 proto 或一个服务里,会导致:
- 车辆建模与执行耦合
- 专业算法与流程编排耦合
- 车端执行与求解逻辑耦合
- 协议难维护
- 后续扩展困难
拆分后,每个 proto 只负责一个明确领域。
---
### 6.2 便于多人协作开发
你这个项目本身就已经是多人分工场景:
- 你负责自动化车间运行逻辑
- 别人负责外部定位
- 别人负责底盘标定算法
- 别人负责运控调参算法
- 别人负责传感器标定算法
在这种情况下,proto 分层能让每个人在清晰边界内开发。
---
### 6.3 便于后续接 ROS2
你后面明确希望把 proto 内容进一步映射为:
- ROS2 msg
- ROS2 srv
- ROS2 action
如果 proto 设计本身就已经分层清晰,那么后续转换到 ROS2 的结构也会更自然:
- 公共消息 → 基础 msg
- 短请求/响应 → srv
- 长任务 → action
- 流式遥测 → topic
---
### 6.4 更符合真实车间运行逻辑
真实车间不是"一个函数跑到底",而是:
- 一个总控状态机
- 多个专业子服务
- 一个或多个车端执行代理
- 多个外部设备
- 多阶段流程推进
- 随时可能失败、重试、中断、恢复
所以协议设计也必须体现真实的系统边界。
---
## 7. 从通信形态角度看,这 7 个 Proto 如何使用
自动化标定车间里并不是所有消息都属于同一种通信形式。
---
### 7.1 适合短同步调用的内容
典型如:
- 工作模式切换
- 能力查询
- 当前参数查询
- 心跳
- 参数写入确认
这类内容通常是**请求-响应式**的。
---
### 7.2 适合长任务异步处理的内容
典型如:
- 启动底盘测试任务
- 启动传感器采集任务
- 启动控制评估任务
- 启动外部定位测量任务
这些任务通常不会立刻完成,因此需要:
- 先返回 `JobAccepted`
- 后续通过 `JobQuery` / `JobStatus` 查询
- 或进一步映射为 action
---
### 7.3 适合流式数据输出的内容
典型如:
- 底盘遥测
- 控制遥测
- 外部定位连续位姿流
- 传感器采集状态流
这类数据天然是持续输出的,更适合 topic / stream。
---
## 8. 七个 Proto 在一次完整会话中的协同时序
![自动化标定车间协同时序图](../docs/images/sequence_diagram.png)
---
## 9. 七个 Proto 的工程分层建议
为了后续工程实现更清晰,建议代码目录也按这 7 个 proto 的职责进行分层。
**参考结构:**
```text
proto/
├── calibration_common.proto
├── vehicle_profile.proto
├── external_localization.proto
├── chassis_calibration.proto
├── sensor_calibration.proto
├── control_calibration.proto
└── workshop_orchestration.proto
services/
├── workshop_orchestrator/
├── vehicle_profile_manager/
├── external_localization_service/
├── chassis_calibration_service/
├── sensor_calibration_service/
├── control_calibration_service/
└── vehicle_agent_windows/
algorithms/
├── chassis_solver/
├── sensor_solver/
├── control_tuner/
└── external_localization_backend/
configs/
├── vehicles/
├── sensors/
├── controllers/
└── workshop/
```
---
## 10. 当前这套 Proto 的核心协同原则
这 7 个 proto 在设计上应始终遵循以下原则。
---
### 原则 1:总控只编排,不求解
`workshop_orchestration.proto` 负责:
- 编排
- 状态推进
- 失败处理
- 结果汇总
但不要在总控里塞入底盘求解、传感器求解、控制调参等专业算法细节。
---
### 原则 2:车端只执行,不决策
Windows 车端代理负责:
- 接收命令
- 执行动作
- 采集遥测
- 写入参数
- 回传状态
它不应该承载复杂的标定求解逻辑。
---
### 原则 3:参数求解在 Linux 侧完成
不论是:
- 底盘参数求解
- 传感器参数求解
- 运控参数优化
都应优先放在 Ubuntu / Linux 车间电脑侧完成。
这与你之前确定的原则完全一致。
---
### 原则 4:车辆画像先行
任何标定任务开始之前,都必须先有完整的车辆画像。
否则:
- 不知道该做哪些任务
- 不知道该下发哪些动作
- 不知道哪些传感器存在
- 不知道是否涉及机械臂
- 不知道使用哪套控制参数模板
---
### 原则 5:统一任务状态表达
所有耗时任务都应尽量统一为:
- 受理
- 执行中
- 成功
- 失败
- 取消
并配合统一错误码和统一重试语义。
这样总控状态机才容易实现。
---
## 11. 落地实现时还必须注意的点
虽然这 7 个 proto 已经能覆盖主干流程,但真正落地时,还必须继续注意以下内容。
---
### 11.1 安全状态一定要贯穿所有流程
例如:
- 急停是否触发
- 防撞区域是否占用
- 机械臂是否处于安全姿态
- 外部定位工装是否到位
- 当前车速是否允许切模式
- 参数写入时是否禁止车辆运动
这些要么在公共协议中补状态字段,要么在各业务协议中明确体现。
---
### 11.2 所有结果都要有版本和摘要
尤其是:
- 底盘参数
- 传感器外参
- 相机内参
- IMU 偏置
- 运控参数
- URDF 引用版本
都建议具备:
- `parameter_version`
- `digest`
- `applied_timestamp`
- `source_session_id`
---
### 11.3 所有长任务最好支持中止与恢复
真实车间里很常见:
- 人员临时介入
- 车辆断电
- 网络闪断
- 外部定位丢失
- 工装被碰撞
- 某个子任务失败
所以流程设计最好支持:
- 取消任务
- 安全中止
- 从阶段恢复
- 局部重跑
---
### 11.4 结果质量不能只给成功/失败
例如:
- 传感器标定应给重投影误差、残差、覆盖率
- 底盘标定应给直线误差、角度误差、曲率误差
- 运控调参应给超调、稳态误差、收敛时间
- 外部定位应给测量质量等级、可见性状态
也就是说,最终不应该只有 `success=true/false`,而应该尽可能有"质量指标"。
---
## 12. 推荐的理解方式
如果把整个自动化标定车间看成一个工厂流水线,那么:
| Proto 文件 | 类比角色 |
|:-----------|:---------|
| `calibration_common.proto` | 通用工单格式、错误码、状态码、追踪信息 |
| `vehicle_profile.proto` | 产品型号定义表 |
| `external_localization.proto` | 高精度测量工位 |
| `chassis_calibration.proto` | 底盘调校工位 |
| `sensor_calibration.proto` | 传感器标定工位 |
| `control_calibration.proto` | 运控调参工位 |
| `workshop_orchestration.proto` | 整条产线的总调度系统 |
---
## 13. 总结
这 7 个 proto 不是彼此独立的零散文件,而是共同组成了自动化标定车间的完整协议体系:
- `calibration_common.proto` 提供统一基础能力
- `vehicle_profile.proto` 描述待标定车辆对象
- `external_localization.proto` 提供外部真值参考
- `chassis_calibration.proto` 负责底盘标定执行与数据回传
- `sensor_calibration.proto` 负责传感器标定任务与结果表达
- `control_calibration.proto` 负责横纵向控制参数调优执行链路
- `workshop_orchestration.proto` 负责全流程编排、状态推进与结果汇总
它们共同支撑起一条完整的自动化标定车间流程:
**车辆入站 → 车辆画像 → 会话创建 → 前置检查 → 外部定位建立参考 → 底盘标定 → 传感器标定 → 运控调参 → 参数写入 → 结果归档 → 会话闭环**
这也是后续继续落地到:
- ROS2 接口映射
- C++ 服务实现
- 状态机编排
- 车端代理实现
- 参数管理系统
- 标定报告系统
的协议基础。
---
## 📄 附录
### 相关文档
- [Protocol Buffers 官方文档](https://developers.google.com/protocol-buffers)
- [ROS2 接口设计指南](https://docs.ros.org/)
### 修订记录
| 版本 | 日期 | 说明 |
|:-----|:-----|:-----|
| v1.0 | 2026-03-10 | 初始版本,定义 7 个核心 proto 职责 |
---
*本文档遵循 [Markdown 最佳实践](https://www.markdownguide.org/basic-syntax/) 编写。*
@@ -1,165 +0,0 @@
syntax = "proto3";
// 规范包名:agv.calibration.chassis
// 设计原则:专门负责底盘最底层机械物理特征的开环标定与体检
package agv.calibration.chassis;
// =========================================================
// 核心服务:AGV 底盘底层硬件自诊与物理运动学标定代理服务
// [部署端 Server]Windows 车端 (只负责听口令、转电机、报裸数据)
// [调用端 Client]Linux 车间服务器 (负责发口令、看雷达真值、算误差)
// =========================================================
service AgvCalibChassisService {
// ---------------------------------------------------------
// 第一步:权限接管与安全熔断 (剥夺车端算法大脑)
// ---------------------------------------------------------
// 💻 [Linux 发送 -> Windows]:要求切断底盘运动学逆解,进入纯物理开环直驱模式
// 🚙 [Windows 返回 -> Linux]:返回接管是否成功的回执
rpc SetDiagnosticMode(DiagnosticModeRequest) returns (StandardResponse);
// 💻 [Linux 发送 -> Windows]:无视一切状态立刻抱死电机的紧急急停指令
// 🚙 [Windows 返回 -> Linux]:返回急停执行状态
rpc HardwareEmergencyBrake(Empty) returns (StandardResponse);
// ---------------------------------------------------------
// 第二步:打开体征监控水龙头 (数字孪生健康诊断)
// ---------------------------------------------------------
// 💻 [Linux 发送 -> Windows]:发送空请求,触发高频推流开关
// 🚙 [Windows 持续流式返回 -> Linux]:以 50Hz 频率持续不断地回传原始脉冲与电流
rpc StreamHardwareTelemetry(Empty) returns (stream HardwareState);
// ---------------------------------------------------------
// 第三步:原始物理开环考题下发 (逼迫底盘暴露机械缺陷)
// ---------------------------------------------------------
// 💻 [Linux 发送 -> Windows]:绕过算法,直接命令指定驱动轮以固定 RPM 盲跑
// 🚙 [Windows 返回 -> Linux]:返回电机是否已成功按给定 RPM 运转
rpc ExecuteRawDriveCommand(RawDriveRequest) returns (StandardResponse);
// 💻 [Linux 发送 -> Windows]:直接对转向机构下发绝对物理角度 (测机械装歪的角度)
// 🚙 [Windows 返回 -> Linux]:返回舵机是否已开始执行角度指令
rpc ExecuteRawSteerCommand(RawSteerRequest) returns (StandardResponse);
// ---------------------------------------------------------
// 第四步:物理本底参数定稿写值 (标定闭环结束)
// ---------------------------------------------------------
// 💻 [Linux 发送 -> Windows]:下发 Linux 结合外部真值算出的绝对物理修正系数
// 🚙 [Windows 返回 -> Linux]:将系数覆写到本地硬盘/驱动板后,返回成功回执
rpc CommitKinematicParameters(KinematicParams) returns (StandardResponse);
}
// =========================================================
// 基础通用消息结构
// =========================================================
// 空消息,通常作为触发类请求发送
// 💻 [流向]Linux 发送 -> Windows
message Empty {}
// 通用应答载荷
// 🚙 [流向]Windows 返回 -> Linux
message StandardResponse {
bool success = 1;
string message = 2; // 若失败,返回驱动器底层报错详情 (如 "ERR_MOTOR_OVERCURRENT")
}
// =========================================================
// 1. 权限模式请求载荷
// =========================================================
// 💻 [流向]Linux 发送 -> Windows
message DiagnosticModeRequest {
enum Mode {
NORMAL_KINEMATICS = 0; // 正常模式 (底盘接收 V_x, Omega,由车端执行逆解分配)
DIRECT_RAW_DRIVE = 1; // 直驱模式 (切断逆解,允许 Linux 直接独立下发左/右轮转速)
}
Mode target_mode = 1;
}
// =========================================================
// 2. 硬件底层遥测推流载荷 (裸数据)
// =========================================================
// 🚙 [流向]Windows 疯狂上报 -> Linux (50Hz)
message HardwareState {
// 底层获取到脉冲那一瞬间的高精度单调系统时钟 (绝对微秒数)
int64 hardware_timestamp_us = 1;
// --- A. 原始编码器反馈 (Linux 拿它与雷达真值做除法,算真实物理位移与滑移率) ---
// 🚨 严禁返回平滑后的速度(m/s),必须返回最原始的累计脉冲 Ticks!
int64 encoder_ticks_fl = 2; // 左前轮累计脉冲
int64 encoder_ticks_fr = 3; // 右前轮累计脉冲
int64 encoder_ticks_rl = 4;
int64 encoder_ticks_rr = 5;
// --- B. 物理舵角反馈 (Linux 拿它比对指令响应时间,测定机械死区) ---
double actual_steer_angle_front_deg = 6;
double actual_steer_angle_rear_deg = 7;
// --- C. 动力与负载健康状态 (Linux 防烧毁熔断的判断依据) ---
// 若维持匀速所需的电流异常激增,说明减速机干涉或刹车未放,Linux 会立刻触发急停
double current_fl_amp = 8; // 左前电机实际相电流 (安培)
double current_fr_amp = 9;
double current_rl_amp = 10;
double current_rr_amp = 11;
double current_steer_front_amp = 12;// 前转向舵机实际电流 (安培)
// --- D. 驱动器硬件报警位 ---
uint32 driver_error_code = 13; // 0x00=健康, 0x01=过压, 0x02=堵转过流等
}
// =========================================================
// 3. 原始动作指令请求载荷
// =========================================================
// 💻 [流向]Linux 发送 -> Windows
message RawDriveRequest {
string test_case_id = 1; // 测试流水号 (如 "Slip_Test_0.5m")
// 直接下发给电机的原始指令 (若是两驱车,后轮填 0 即可)
double fl_motor_rpm = 2; // 左前轮目标物理转速 (RPM)
double fr_motor_rpm = 3; // 右前轮目标物理转速 (RPM)
double rl_motor_rpm = 4;
double rr_motor_rpm = 5;
// 🚨 断网防飞车底线:若超过该时间未收到新指令,车端底层必须自动刹车
double duration_sec = 6;
}
// 💻 [流向]Linux 发送 -> Windows
message RawSteerRequest {
string test_case_id = 1; // 测试流水号 (如 "Deadzone_Sweep_5deg")
// 针对舵机/转向推杆的绝对物理角度指令 (度)
double front_steer_angle_deg = 2;
double rear_steer_angle_deg = 3;
// 扫频测试参数 (用于测定机械往复间隙 Backlash)
optional double sweep_amplitude_deg = 4; // 往复抖动幅度 (度)
optional double sweep_frequency_hz = 5; // 抖动频率 (Hz)
double duration_sec = 6;
}
// =========================================================
// 4. 物理运动学本底参数定稿载荷
// =========================================================
// 💻 [流向]Linux 发送 -> Windows
message KinematicParams {
// (注:全字段使用 optional,Linux 测了哪一项就只下发哪一项要求车端覆盖,未发的不作修改)
// --- 1. 真实有效物理轮径 (纠正“开环跑偏”与里程计位移误差) ---
optional double wheel_radius_fl_m = 1;
optional double wheel_radius_fr_m = 2;
optional double wheel_radius_rl_m = 3;
optional double wheel_radius_rr_m = 4;
// --- 2. 机械零位绝对偏差补偿 (纠正“指令0度但车子斜着走”) ---
optional double steer_zero_offset_front_deg = 5;
optional double steer_zero_offset_rear_deg = 6;
// --- 3. 旋转几何协同参数 (纠正“原地打转时车体甩尾晃动”) ---
optional double effective_track_width_m = 7; // 左右轮真实物理有效轮距 (m)
optional double effective_wheel_base_m = 8; // 前后轮真实物理有效轴距 (m)
// 针对多舵轮底盘:瞬时旋转中心(ICR)的物理几何偏移
optional double icr_offset_x_m = 9;
optional double icr_offset_y_m = 10;
}
@@ -1,185 +0,0 @@
syntax = "proto3";
// 规范包名,确保与传感器外参标定业务(agv.calibration.sensor)严格物理与逻辑隔离
package agv.calibration.control;
// =========================================================
// 核心服务:AGV 运控大脑(PID/MPC)参数自动化寻优调教代理
// [部署端 Server]Windows车端 (满血保留自身算法,负责执行闭环追踪与高频汇报)
// [调用端 Client]:Linux标定服务器 (上帝视角,负责发轨迹、看误差、AI打分与发新参数)
// =========================================================
service AgvCalibControlService {
// ---------------------------------------------------------
// 第一步:权限接管与生命周期安全管控
// ---------------------------------------------------------
// 💻 [Linux 发送 -> Windows]:要求切断避障,但保留底层 PID/MPC 算法就绪
// 🚙 [Windows 返回 -> Linux]:回复模式切换成功,准备好接考题
rpc SetControlMode(ModeRequest) returns (StandardResponse);
// 💻 [Linux 发送 -> Windows]:断网或飞车时的最高级别急停,无视一切直接刹车
// 🚙 [Windows 返回 -> Linux]:返回底层抱死结果
rpc EmergencyStop(Empty) returns (StandardResponse);
// ---------------------------------------------------------
// 第二步:运动考题下发 (开环排雷 / 闭环寻优 / 波峰对齐)
// ---------------------------------------------------------
// 【场景A: 纯物理开环备用】
// 💻 [Linux 发送 -> Windows]:要求切断算法盲跑,多用于摸底或辅助验证
// 🚙 [Windows 返回 -> Linux]:确认已按指定 RPM/PWM 运转
rpc ExecuteOpenLoopCmd(OpenLoopRequest) returns (StandardResponse);
// 【场景B: 算法闭环调优】
// 💻 [Linux 发送 -> Windows]:下发一条由几百个点组成的测试轨迹(如 S型贝塞尔曲线)
// 🚙 [Windows 返回 -> Linux]:收到轨迹后,车端立刻使用它自带的 PID/MPC 算法努力贴合轨迹跑圈
rpc FollowTestTrajectory(TrajectoryRequest) returns (StandardResponse);
// 【场景C: 波峰时序对齐】
// 💻 [Linux 发送 -> Windows]:下发极短促的阶跃加速指令,人为制造绝对速度波峰
// 🚙 [Windows 返回 -> Linux]:确认加速。(Linux 借此波峰算出网络的绝对 Time Offset)
rpc ExecuteStepResponse(StepResponseRequest) returns (StandardResponse);
// ---------------------------------------------------------
// 第三步:运控参数 AI 寻优:动态热注入与最终固化
// ---------------------------------------------------------
// 💻 [Linux 发送 -> Windows]Linux 发现上一圈跑得差,AI算出了新的 PID/前瞻距离,要求立即热注入
// 🚙 [Windows 返回 -> Linux]:车端将新参数瞬间覆写进运行内存(不重启系统),随时准备用新参数重跑
rpc InjectTuningParameters(ControlParams) returns (StandardResponse);
// 💻 [Linux 发送 -> Windows]Linux 判定误差极小,调优结束,命令固化目前内存里的最高分参数
// 🚙 [Windows 返回 -> Linux]:车端将这组完美参数永久覆写进硬盘的 config.yaml 或系统注册表
rpc CommitControlParameters(Empty) returns (StandardResponse);
// ---------------------------------------------------------
// 第四步:高频数字孪生体感上报 (50Hz)
// ---------------------------------------------------------
// 💻 [Linux 发送 -> Windows]:空包触发,命令车端开始疯狂推流
// 🚙 [Windows 持续流式返回 -> Linux]:以 50Hz 频率,持续上报自己的里程计坐标、速度和单调时间戳
rpc StreamTelemetry(Empty) returns (stream TelemetryData);
}
// =========================================================
// 基础通用消息结构
// =========================================================
// 💻 [流向]Linux 发送 -> Windows (通常用作触发信号)
message Empty {}
// 🚙 [流向]Windows 返回 -> Linux (通用应答)
message StandardResponse {
bool success = 1;
string message = 2; // 包含执行成功的回执,或底盘卡死/驱动器报错等异常原因
}
// =========================================================
// 1. 模式控制结构体
// =========================================================
// 💻 [流向]Linux 发送 -> Windows
message ModeRequest {
enum Mode {
NORMAL_MODE = 0; // 正常业务模式(打开避障和导航,出厂默认状态)
OPEN_LOOP_MODE = 1; // 物理开环标定模式(切断所有算法纠偏,提线木偶状态)
TUNING_MODE = 2; // 闭环调优模式(切断环境避障,但必须保留原生 PID/MPC 追踪算法)
}
Mode target_mode = 1;
}
// =========================================================
// 2. 动作指令请求载荷
// =========================================================
// 💻 [流向]Linux 发送 -> Windows
message OpenLoopRequest {
double left_motor_cmd = 1; // 左驱动轮目标转速 (RPM) 或占空比
double right_motor_cmd = 2; // 右驱动轮目标转速 (RPM) 或占空比
double steering_angle = 3; // 针对单/多舵轮底盘的绝对舵角指令 (度,差速轮忽略)
// 🚨 极度关键的安全设计:指令超时时间
// 业务潜台词:车端若失去网络连接,超时后必须由底层代码强制将速度归零,严防撞墙!
double duration_sec = 4;
}
// 💻 [流向]Linux 发送 -> Windows (组成考卷的一小步)
message TrajectoryPoint {
double x_m = 1; // 目标点 X 坐标 (米)
double y_m = 2; // 目标点 Y 坐标 (米)
double yaw_rad = 3; // 目标点 偏航角 (弧度)
double target_speed_ms = 4; // 到达该点时的期望线速度 (米/秒)
double curvature = 5; // 该点处的轨迹曲率 (可选项,用于辅助前瞻距离映射)
}
// 💻 [流向]Linux 发送 -> Windows (下发整张考卷)
message TrajectoryRequest {
string test_case_id = 1; // 考题名称,如 "Bezier_Curve_S_Speed_1.2"
repeated TrajectoryPoint path = 2; // 组成考题曲线的稠密坐标点阵列
}
// 💻 [流向]Linux 发送 -> Windows (制造波峰)
message StepResponseRequest {
double target_velocity_ms = 1; // 极速阶跃的目标线速度 (如猛烈加速到 1.5 m/s)
double duration_sec = 2; // 阶跃维持时间 (极短,如 1~2 秒即可,用于产生绝对波峰)
}
// =========================================================
// 3. 待调优运控参数载荷 (支持增量式热更新)
// =========================================================
// 💻 [流向]Linux 发送 -> Windows
message ControlParams {
// 🚨 业务潜台词:采用 optional 关键字,允许 Linux 每次只下发需要修改的个别参数。
// 没下发的参数,Windows 必须保持内存中的原样,千万不能清零!
// --- 底盘物理运动学修正系数 (由第一阶段开环算得) ---
optional double wheel_radius_left_ratio = 1; // 左侧真实有效轮径补偿乘数 (如 1.002)
optional double wheel_radius_right_ratio = 2; // 右侧真实有效轮径补偿乘数 (如 0.998)
optional double effective_track_width_m = 3; // 有效轮距 (m)
optional double steering_zero_offset_deg = 4; // 舵角机械零位静态偏差 (度)
// --- 经典 PID 控制增益 ---
optional double pid_kp_lateral = 5;
optional double pid_ki_lateral = 6;
optional double pid_kd_lateral = 7; // 用于提供阻尼,抑制高频画龙震荡
optional double pid_kp_heading = 8;
optional double pid_ki_heading = 9;
optional double pid_kd_heading = 10;
// --- 先进算法核心参数 ---
optional double pure_pursuit_lookahead_m = 11; // 纯追踪前瞻距离 Ld (m)
optional double mpc_weight_q_lateral = 12; // MPC Q矩阵:对横向误差的惩罚权重
optional double mpc_weight_r_steering = 13; // MPC R矩阵:对转向电机发力剧烈度的惩罚权重 (控制平顺性)
}
// =========================================================
// 4. 高频遥测推流载荷 (数字孪生状态汇报)
// =========================================================
// 🚙 [流向]Windows 疯狂上报 -> Linux (50Hz)
message TelemetryData {
// 🚨 互相关对齐的核心依据:
// 必须使用 Windows 底层高精度单调时钟 (如 QueryPerformanceCounter) 的绝对微秒数。
// 绝对禁止在车端人为做时序平滑或使用受 NTP 影响的系统时间!
int64 hardware_timestamp_us = 1;
// --- A. 车端推算的内部里程计位姿 (Odom) ---
// 业务潜台词:Linux 拿这个跟外面上帝视角的雷达真值相减,就算出了算法的实际追踪误差(RMSE)
double odom_x_m = 2;
double odom_y_m = 3;
double odom_yaw_rad = 4;
// --- B. 底层执行器真实物理反馈 (用于提取波峰) ---
// 业务潜台词:用于跟指令速度比对,提取波峰,并计算 Jerk (加加速度/平顺性)
double feedback_linear_vel_ms = 5; // 编码器解算的真实线速度 (m/s)
double feedback_angular_vel_rads = 6;// 陀螺仪或编码器解算的真实角速度 (rad/s)
// --- C. 硬件健康与功耗监控 (用于 Linux 诊断干涉卡死) ---
// 业务潜台词:如果遇到急弯时电流长期满载,Linux 判定该考题超出了这台车的物理极限。
double left_motor_current_amp = 7; // 左驱动电机实时电流 (A)
double right_motor_current_amp = 8; // 右驱动电机实时电流 (A)
double steering_motor_current_amp = 9; // 转向舵机实时电流 (A)
// --- D. 算法控制输出量 (用于 Linux 识别死区或物理饱和) ---
// 业务潜台词:观察 PID 算出的期望舵角,看是否长期顶在软件限幅上 (如打满死舵)
double cmd_steering_output = 10; // 控制算法计算出的期望底层舵角指令 (度/弧度)
}
@@ -1,147 +0,0 @@
syntax = "proto3";
// chassis control
// --
package agv.calibration.sensor;
// =========================================================
//
// [ Server]Windows车端 ()
// [ Client]Linux标定服务器 ( Ceres )
// =========================================================
service SensorCalibrationService {
// ---------------------------------------------------------
// ()
// ---------------------------------------------------------
// 💻 [Linux -> Windows]
// 🚙 [Windows -> Linux]
// 🚨 Linux sleep(0.5s)
rpc MoveToObservationPose (PoseRequest) returns (StandardResponse);
// ---------------------------------------------------------
// ()
// ---------------------------------------------------------
// 💻 [Linux -> Windows]
// 🚙 [Windows -> Linux]
rpc TriggerSyncCapture (CaptureRequest) returns (CaptureResponse);
// ---------------------------------------------------------
// ( Windows )
// ---------------------------------------------------------
// 💻 [Linux -> Windows]/
// 🚙 [Windows -> Linux] 5MB+ Linux
// 🚨 使 stream gRPC 4MB
rpc DownloadImage (DataFetchRequest) returns (stream FileChunk);
rpc DownloadPointCloud (DataFetchRequest) returns (stream FileChunk);
// ---------------------------------------------------------
// 稿 ()
// ---------------------------------------------------------
// 💻 [Linux -> Windows]Linux 4x4
// 🚙 [Windows -> Linux] sensor_config.yaml
rpc CommitCalibrationResults (CalibrationPayload) returns (StandardResponse);
}
// =========================================================
//
// =========================================================
// 🚙 []Windows -> Linux ()
message StandardResponse {
bool success = 1;
string message = 2; // 线
}
// =========================================================
// 1. ()
// =========================================================
// 💻 []Linux -> Windows
message PoseRequest {
double target_x_m = 1; // X ()
double target_y_m = 2; // Y ()
double target_yaw_deg = 3; // ()
bool is_relative = 4; // true: ; false:
}
// =========================================================
// 2. ()
// =========================================================
// 💻 []Linux -> Windows
message CaptureRequest {
// ["cam_front", "lidar_top"]
// copy
repeated string sensor_ids = 1;
}
// 🚙 []Windows -> Linux
message CaptureResponse {
bool success = 1;
// 🚨 ()
// Wi-Fi
int64 capture_timestamp_us = 2;
string error_message = 3;
}
// =========================================================
// 3. ()
// =========================================================
// 💻 []Linux -> Windows ()
message DataFetchRequest {
int64 capture_timestamp_us = 1; // 2
string sensor_id = 2; // "cam_front"
}
// 🚙 []Windows -> Linux (线)
message FileChunk {
// gRPC 4MB
bytes chunk_data = 1; // C++ 512KB - 1MB
bool is_last_chunk = 2; // Linux
// 🚨 "jpg" "jpeg"
// 3D
// "png", "bmp", "raw" "pcd"
string format_ext = 3;
}
// =========================================================
// 4. ()
// =========================================================
// 💻 []Linux -> Windows (线)
message CameraIntrinsics {
string camera_id = 1;
double fx = 2; double fy = 3;
double cx = 4; double cy = 5;
repeated double dist_coeffs = 6; // [k1, k2, p1, p2, k3]
}
// 💻 []Linux -> Windows ( 6-DOF )
message SensorExtrinsics {
// source target
string source_frame = 1; // "lidar_top" "cam_left"
string target_frame = 2; // "cam_front" "base_link" ()
// ( mm)
double trans_x_mm = 3;
double trans_y_mm = 4;
double trans_z_mm = 5;
// 姿 ( degrees便)
double roll_deg = 6;
double pitch_deg = 7;
double yaw_deg = 8;
}
// 💻 []Linux -> Windows ()
message CalibrationPayload {
string task_id = 1; // MES
// repeated Linux
// for
repeated CameraIntrinsics updated_intrinsics = 2;
repeated SensorExtrinsics updated_extrinsics = 3;
}
@@ -0,0 +1,211 @@
syntax = "proto3";
package agv.calibration.common;
// =========================================================
//
// 使
// 1) Ubuntu
// 2) Ubuntu Windows
// 3)
// =========================================================
// =========================================================
//
// RPC 使
// =========================================================
message Empty {}
// =========================================================
//
//
// =========================================================
message Vector3D {
double x = 1; // X
double y = 2; // Y
double z = 3; // Z
}
// =========================================================
// 姿
// base_link姿
//
// 1)
// 2)
// 3) xyz + rpy 便
// =========================================================
message Pose3D {
double x_m = 1; // X m
double y_m = 2; // Y m
double z_m = 3; // Z m
double roll_rad = 4; // X rad
double pitch_rad = 5; // Y rad
double yaw_rad = 6; // Z rad
}
// =========================================================
//
//
// Ubuntu
// Linux Windows
// =========================================================
message RequestHeader {
string session_id = 1; // ID
string task_id = 2; // ID / ID
string vehicle_id = 3; // ID
string request_id = 4; // ID
int64 client_send_timestamp_us = 5; //
string operator_id = 6; // /
string workshop_host = 7; // Ubuntu
}
// =========================================================
//
// 便
// =========================================================
enum ErrorCode {
ERROR_CODE_UNSPECIFIED = 0; //
OK = 1; //
INVALID_ARGUMENT = 2; //
INVALID_STATE = 3; //
VEHICLE_BUSY = 4; //
NOT_READY = 5; //
TIMEOUT = 6; //
NETWORK_LOSS = 7; //
SAFETY_TRIGGERED = 8; //
HARDWARE_FAULT = 9; //
FILE_NOT_FOUND = 10; //
CHECKSUM_MISMATCH = 11; //
INTERNAL_ERROR = 12; //
UNSUPPORTED_CAPABILITY = 13; //
RESOURCE_LOCKED = 14; //
MANUAL_CONFIRM_REQUIRED = 15;//
APPROVAL_REQUIRED = 16; //
VALIDATION_FAILED = 17; //
ROLLBACK_REQUIRED = 18; //
DATA_QUALITY_INSUFFICIENT = 19; //
}
// =========================================================
//
// RPC
//
//
// =========================================================
message StandardResponse {
bool success = 1; //
ErrorCode error_code = 2; //
string message = 3; //
}
// =========================================================
//
//
// =========================================================
enum JobState {
JOB_STATE_UNSPECIFIED = 0; //
PENDING = 1; //
RUNNING = 2; //
WAITING_MANUAL = 3; // /
WAITING_APPROVAL = 4; //
SUCCEEDED = 5; //
FAILED = 6; //
CANCELED = 7; //
ROLLED_BACK = 8; //
}
// =========================================================
//
// job_id
//
// Ubuntu
// =========================================================
message JobAccepted {
bool accepted = 1; //
ErrorCode error_code = 2; //
string message = 3; //
string job_id = 4; // ID
}
// =========================================================
//
// job_id
// Ubuntu
// Linux Windows
// =========================================================
message JobQuery {
RequestHeader header = 1; //
string job_id = 2; // ID
}
// =========================================================
//
//
//
// Ubuntu
// =========================================================
message JobStatus {
string job_id = 1; // ID
JobState state = 2; //
double progress = 3; // 0.0 ~ 1.0
ErrorCode error_code = 4; //
string message = 5; //
int64 server_timestamp_us = 6; //
bool safe_to_retry = 7; //
}
// =========================================================
//
// Linux Windows 线
// Ubuntu
// Windows / Linux
// =========================================================
message HeartbeatRequest {
RequestHeader header = 1; //
string agent_name = 2; // /
int32 expect_next_heartbeat_ms = 3; // ms
}
// =========================================================
//
// 线
//
//
// =========================================================
message HeartbeatResponse {
bool success = 1; //
ErrorCode error_code = 2; //
string message = 3; //
int64 server_timestamp_us = 4; //
bool vehicle_ready = 5; // /
}
// =========================================================
//
// URDF
// =========================================================
message FileDigest {
string checksum_type = 1; // sha256
string checksum_value = 2; //
}
// =========================================================
//
//
// =========================================================
message FileReference {
string file_name = 1; //
string file_uri = 2; // / URI
int64 size_bytes = 3; //
string description = 4; //
FileDigest digest = 5; //
}
// =========================================================
//
//
// =========================================================
message KeyValuePair {
string key = 1; //
string value = 2; //
}
@@ -0,0 +1,359 @@
syntax = "proto3";
package agv.calibration.chassis;
import "calibration_common.proto";
import "vehicle_profile.proto";
// =========================================================
//
//
// Ubuntu (Linux) -> Windows
//
// 1) Linux
// 2) Windows
// =========================================================
// =========================================================
//
// Windows
// Ubuntu
// =========================================================
service AgvCalibChassisService {
//
rpc Heartbeat(.agv.calibration.common.HeartbeatRequest)
returns (.agv.calibration.common.HeartbeatResponse);
//
rpc SetChassisWorkMode(ChassisWorkModeRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc GetChassisCapability(ChassisCapabilityRequest)
returns (ChassisCapabilityResponse);
//
rpc StartMotionPrimitive(MotionPrimitiveRequest)
returns (.agv.calibration.common.JobAccepted);
//
rpc GetChassisJobStatus(.agv.calibration.common.JobQuery)
returns (.agv.calibration.common.JobStatus);
//
rpc GetChassisJobResult(.agv.calibration.common.JobQuery)
returns (ChassisJobResult);
//
rpc CancelChassisJob(.agv.calibration.common.JobQuery)
returns (.agv.calibration.common.StandardResponse);
//
rpc StreamChassisTelemetry(StreamChassisTelemetryRequest)
returns (stream ChassisTelemetry);
//
rpc CommitChassisCalibrationParameters(CommitChassisCalibrationParametersRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc GetAppliedChassisCalibrationParameters(GetAppliedChassisCalibrationParametersRequest)
returns (AppliedChassisCalibrationParametersResponse);
//
rpc EmergencyBrake(.agv.calibration.common.Empty)
returns (.agv.calibration.common.StandardResponse);
}
// =========================================================
//
//
// =========================================================
message ChassisWorkModeRequest {
.agv.calibration.common.RequestHeader header = 1; //
enum Mode {
CHASSIS_MODE_UNSPECIFIED = 0; //
NORMAL_MODE = 1; //
CALIBRATION_READY_MODE = 2; //
DIRECT_EXECUTION_MODE = 3; //
VALIDATION_MODE = 4; //
}
Mode target_mode = 2; //
string reason = 3; //
}
// =========================================================
//
//
// =========================================================
message ChassisCapabilityRequest {
.agv.calibration.common.RequestHeader header = 1; //
}
// =========================================================
//
//
// =========================================================
message ChassisCapabilityResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
.agv.calibration.vehicle.profile.ChassisType chassis_type = 4; //
bool supports_straight_line = 5; // 线
bool supports_arc = 6; //
bool supports_in_place_rotation = 7; //
bool supports_steer_sweep = 8; //
bool supports_reverse_motion = 9; //
}
// =========================================================
// 线
// 线线
// =========================================================
message StraightLineCommand {
double target_speed_ms = 1; // 线m/s
double target_distance_m = 2; // m
bool reverse = 3; //
}
// =========================================================
//
// 线
// =========================================================
message ArcCommand {
double target_speed_ms = 1; // 线m/s
double radius_m = 2; // m
double sweep_angle_deg = 3; //
bool clockwise = 4; //
}
// =========================================================
//
//
// =========================================================
message InPlaceRotationCommand {
double target_yaw_deg = 1; //
double target_angular_vel_deg_s = 2; // /
}
// =========================================================
//
//
// =========================================================
message SteeringSweepCommand {
double target_angle_deg = 1; //
double sweep_amplitude_deg = 2; //
double sweep_frequency_hz = 3; // Hz
double duration_sec = 4; //
}
// =========================================================
//
//
// Ubuntu
// Windows
// =========================================================
message MotionPrimitiveRequest {
.agv.calibration.common.RequestHeader header = 1; //
string test_case_id = 2; // ID
enum TaskPurpose {
CHASSIS_TASK_PURPOSE_UNSPECIFIED = 0; //
DATA_COLLECTION = 1; //
VALIDATION = 2; //
DIRECT_CHECK = 3; //
}
TaskPurpose task_purpose = 3; //
oneof primitive {
StraightLineCommand straight_line = 4; // 线
ArcCommand arc = 5; //
InPlaceRotationCommand in_place_rotation = 6;//
SteeringSweepCommand steering_sweep = 7; //
}
bool brake_when_finished = 8; //
double timeout_sec = 9; //
string source_iteration_id = 10; //
}
// =========================================================
//
//
// =========================================================
message StreamChassisTelemetryRequest {
.agv.calibration.common.RequestHeader header = 1; //
uint32 expected_hz = 2; //
bool include_odom = 3; //
bool include_wheel_state = 4; //
bool include_driver_state = 5; //
}
// =========================================================
// /
//
// =========================================================
message WheelModuleState {
string module_id = 1; // ID fl / fr / drive_center
int64 encoder_ticks = 2; //
double wheel_speed_rpm = 3; // RPM
double steer_angle_deg = 4; // 0
double motor_current_amp = 5; // A
}
// =========================================================
//
// Linux
// Windows
// Ubuntu
// =========================================================
message ChassisTelemetry {
int64 hardware_timestamp_us = 1; //
.agv.calibration.vehicle.profile.ChassisType chassis_type = 2; //
double odom_x_m = 3; // Xm
double odom_y_m = 4; // Ym
double odom_yaw_rad = 5; // rad
double linear_velocity_ms = 6; // 线m/s
double angular_velocity_rads = 7; // rad/s
repeated WheelModuleState modules = 8; // /
bool estop_engaged = 9; //
uint32 driver_error_code = 10; //
string active_job_id = 11; // ID
}
// =========================================================
//
// /
// =========================================================
message CommonChassisCalibrationParams {
optional double effective_wheel_base_m = 1; // m
optional double effective_track_width_m = 2; // m
optional double longitudinal_scale = 3; //
optional double lateral_scale = 4; //
optional double yaw_scale = 5; //
optional double straight_line_bias = 6; // 线
}
// =========================================================
//
//
// =========================================================
message AckermannCalibrationParams {
optional double front_left_steer_zero_offset_deg = 1; //
optional double front_right_steer_zero_offset_deg = 2; //
optional double rear_left_wheel_radius_m = 3; //
optional double rear_right_wheel_radius_m = 4; //
optional double steering_ratio = 5; //
}
// =========================================================
//
// AGV
// =========================================================
message DifferentialCalibrationParams {
optional double left_wheel_radius_m = 1; //
optional double right_wheel_radius_m = 2; //
optional double axle_track_width_m = 3; //
optional double left_encoder_scale = 4; //
optional double right_encoder_scale = 5; //
}
// =========================================================
//
// AGV
// =========================================================
message SingleSteerWheelCalibrationParams {
optional double drive_wheel_radius_m = 1; //
optional double steer_zero_offset_deg = 2; //
optional double steering_ratio = 3; //
optional double drive_encoder_scale = 4; //
}
// =========================================================
//
//
// =========================================================
message SteeringModuleCalibrationParam {
string module_id = 1; // ID
optional double wheel_radius_m = 2; //
optional double steer_zero_offset_deg = 3; //
optional double module_pos_x_m = 4; // base_link X
optional double module_pos_y_m = 5; // base_link Y
}
// =========================================================
//
// AGV
// =========================================================
message MultiSteerWheelCalibrationParams {
repeated SteeringModuleCalibrationParam modules = 1; //
}
// =========================================================
//
//
// =========================================================
message ChassisCalibrationParameterSet {
.agv.calibration.vehicle.profile.ChassisType chassis_type = 1; //
CommonChassisCalibrationParams common = 2; //
oneof specific_params {
AckermannCalibrationParams ackermann = 3; //
DifferentialCalibrationParams differential = 4;//
SingleSteerWheelCalibrationParams single_steer = 5; //
MultiSteerWheelCalibrationParams multi_steer = 6; //
}
}
// =========================================================
//
// Linux
// =========================================================
message CommitChassisCalibrationParametersRequest {
.agv.calibration.common.RequestHeader header = 1; //
string parameter_version = 2; //
ChassisCalibrationParameterSet params = 3; //
string commit_reason = 4; //
.agv.calibration.common.FileDigest digest = 5; //
bool persistent_write = 6; //
}
// =========================================================
//
// =========================================================
message GetAppliedChassisCalibrationParametersRequest {
.agv.calibration.common.RequestHeader header = 1; //
}
// =========================================================
//
// =========================================================
message AppliedChassisCalibrationParametersResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
string parameter_version = 4; //
ChassisCalibrationParameterSet params = 5; //
int64 applied_timestamp_us = 6; //
}
// =========================================================
//
//
// =========================================================
message ChassisJobResult {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
string job_id = 4; // ID
bool data_quality_passed = 5; //
bool suitable_for_commit = 6; //
string recommended_parameter_version = 7; //
double max_lateral_error_m = 8; //
double max_yaw_error_rad = 9; //
double estimated_straight_line_bias = 10; // 线
repeated .agv.calibration.common.FileReference artifacts = 11; //
}
@@ -0,0 +1,325 @@
syntax = "proto3";
package agv.calibration.control;
import "calibration_common.proto";
import "vehicle_profile.proto";
// =========================================================
//
//
// Ubuntu (Linux) -> Windows
//
// 1) Linux
// 2) Windows /
// 3)
// =========================================================
// =========================================================
//
// Windows
// Ubuntu
// =========================================================
service AgvCalibControlService {
//
rpc Heartbeat(.agv.calibration.common.HeartbeatRequest)
returns (.agv.calibration.common.HeartbeatResponse);
//
rpc SetControlWorkMode(ControlWorkModeRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc InjectControllerParameters(InjectControllerParametersRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc StartControllerEvaluation(ControllerEvaluationRequest)
returns (.agv.calibration.common.JobAccepted);
//
rpc GetControlJobStatus(.agv.calibration.common.JobQuery)
returns (.agv.calibration.common.JobStatus);
//
rpc GetControlJobResult(.agv.calibration.common.JobQuery)
returns (ControlJobResult);
//
rpc CancelControlJob(.agv.calibration.common.JobQuery)
returns (.agv.calibration.common.StandardResponse);
//
rpc StreamControlTelemetry(StreamControlTelemetryRequest)
returns (stream ControlTelemetry);
//
rpc CommitControllerParameters(CommitControllerParametersRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc GetActiveControllerParameters(GetActiveControllerParametersRequest)
returns (ActiveControllerParametersResponse);
//
rpc EmergencyStop(.agv.calibration.common.Empty)
returns (.agv.calibration.common.StandardResponse);
}
// =========================================================
//
//
// =========================================================
message ControlWorkModeRequest {
.agv.calibration.common.RequestHeader header = 1; //
enum Mode {
CONTROL_MODE_UNSPECIFIED = 0; //
NORMAL_MODE = 1; //
TUNING_READY_MODE = 2; //
EVALUATION_MODE = 3; //
VALIDATION_MODE = 4; //
}
Mode target_mode = 2; //
string reason = 3; //
}
// =========================================================
// PID
// PID
// =========================================================
message PIDParams {
string loop_name = 1; // lateral / heading / speed
double kp = 2; //
double ki = 3; //
double kd = 4; //
optional double integral_limit = 5; //
optional double output_limit = 6; //
}
// =========================================================
// MPC
// MPC
// =========================================================
message MPCParams {
uint32 prediction_horizon = 1; //
uint32 control_horizon = 2; //
double model_dt_s = 3; //
double q_lateral = 4; //
double q_heading = 5; //
double q_speed = 6; //
double r_control = 7; //
double r_control_rate = 8; //
optional double output_limit = 9; //
}
// =========================================================
// LQR
// LQR
// =========================================================
message LQRParams {
repeated double q_state_weights = 1; // Q
repeated double r_input_weights = 2; // R
optional double preview_time_s = 3; //
}
// =========================================================
// Pure Pursuit
// Pure Pursuit
// =========================================================
message PurePursuitParams {
double lookahead_m = 1; //
optional double min_lookahead_m = 2; //
optional double max_lookahead_m = 3; //
optional double curvature_gain = 4; //
optional double steering_limit_deg = 5; //
}
// =========================================================
//
//
//
// 1) control_axis
// 2) algorithm_type
// =========================================================
message ControllerParameterPack {
.agv.calibration.vehicle.profile.ControlAxisType control_axis = 1; //
.agv.calibration.vehicle.profile.ControllerAlgorithmType algorithm_type = 2; //
oneof params {
PIDParams pid = 3; // PID
MPCParams mpc = 4; // MPC
LQRParams lqr = 5; // LQR
PurePursuitParams pure_pursuit = 6; // Pure Pursuit
}
}
// =========================================================
//
//
//
// 1) MPC + PID
// 2) PP + PID
// =========================================================
message ControllerParameterSet {
repeated ControllerParameterPack items = 1; //
}
// =========================================================
//
// Linux
// =========================================================
message InjectControllerParametersRequest {
.agv.calibration.common.RequestHeader header = 1; //
string parameter_version = 2; //
ControllerParameterSet parameter_set = 3; //
bool apply_immediately = 4; //
string source_iteration_id = 5; //
}
// =========================================================
//
// 使
// =========================================================
message TrajectoryPoint {
double x_m = 1; // X
double y_m = 2; // Y
double yaw_rad = 3; //
double target_speed_ms = 4; //
}
// =========================================================
//
//
// =========================================================
message TrajectoryTrackingTask {
repeated TrajectoryPoint path = 1; //
bool stop_at_end = 2; //
double timeout_sec = 3; //
}
// =========================================================
//
//
// =========================================================
message VelocityStepTask {
double target_velocity_ms = 1; //
double hold_time_sec = 2; //
double settle_before_step_sec = 3; //
}
// =========================================================
//
//
// Ubuntu
// Windows
// =========================================================
message ControllerEvaluationRequest {
.agv.calibration.common.RequestHeader header = 1; //
string test_case_id = 2; // ID
enum TaskPurpose {
CONTROL_TASK_PURPOSE_UNSPECIFIED = 0; //
DATA_COLLECTION = 1; //
TUNING_EVALUATION = 2; //
VALIDATION = 3; //
}
TaskPurpose task_purpose = 3; //
oneof task {
TrajectoryTrackingTask trajectory_tracking = 4; //
VelocityStepTask velocity_step = 5; //
}
string source_iteration_id = 6; //
}
// =========================================================
//
//
// =========================================================
message StreamControlTelemetryRequest {
.agv.calibration.common.RequestHeader header = 1; //
uint32 expected_hz = 2; //
bool include_tracking_error = 3; //
bool include_control_output = 4; //
bool include_vehicle_feedback = 5; //
}
// =========================================================
//
//
// Windows
// Ubuntu
// =========================================================
message ControlTelemetry {
int64 hardware_timestamp_us = 1; //
double odom_x_m = 2; // X
double odom_y_m = 3; // Y
double odom_yaw_rad = 4; //
double linear_velocity_ms = 5; // 线
double angular_velocity_rads = 6; //
double lateral_error_m = 7; //
double heading_error_rad = 8; //
double speed_error_ms = 9; //
double steering_output = 10; //
double throttle_output = 11; //
bool saturation_flag = 12; //
string active_job_id = 13; // ID
}
// =========================================================
//
//
// =========================================================
message CommitControllerParametersRequest {
.agv.calibration.common.RequestHeader header = 1; //
string parameter_version = 2; //
ControllerParameterSet parameter_set = 3; //
string commit_reason = 4; //
.agv.calibration.common.FileDigest digest = 5; //
bool persistent_write = 6; //
}
// =========================================================
//
// =========================================================
message GetActiveControllerParametersRequest {
.agv.calibration.common.RequestHeader header = 1; //
}
// =========================================================
//
// =========================================================
message ActiveControllerParametersResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
string parameter_version = 4; //
ControllerParameterSet parameter_set = 5; //
int64 applied_timestamp_us = 6; //
}
// =========================================================
//
// /
// =========================================================
message ControlJobResult {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
string job_id = 4; // ID
bool data_quality_passed = 5; //
bool suitable_for_commit = 6; //
string recommended_parameter_version = 7; //
double rms_lateral_error_m = 8; //
double rms_heading_error_rad = 9; //
double rms_speed_error_ms = 10; //
double overshoot_ratio = 11; //
double settle_time_sec = 12; //
repeated .agv.calibration.common.FileReference artifacts = 13; //
}
@@ -0,0 +1,255 @@
syntax = "proto3";
package agv.calibration.localization.external;
import "calibration_common.proto";
// =========================================================
// /
//
// Ubuntu (Linux) -> Windows /
//
// 1) Linux
// 2) / 姿
// =========================================================
// =========================================================
//
// Windows Linux
// Ubuntu
// =========================================================
service AgvCalibExternalLocalizationService {
//
rpc Heartbeat(.agv.calibration.common.HeartbeatRequest)
returns (.agv.calibration.common.HeartbeatResponse);
//
rpc SetExternalLocalizationWorkMode(ExternalLocalizationWorkModeRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc GetExternalLocalizationCapability(ExternalLocalizationCapabilityRequest)
returns (ExternalLocalizationCapabilityResponse);
//
rpc StartExternalLocalizationTask(ExternalLocalizationTaskRequest)
returns (.agv.calibration.common.JobAccepted);
//
rpc GetExternalLocalizationJobStatus(.agv.calibration.common.JobQuery)
returns (.agv.calibration.common.JobStatus);
//
rpc GetExternalLocalizationJobResult(.agv.calibration.common.JobQuery)
returns (ExternalLocalizationJobResult);
//
rpc CancelExternalLocalizationJob(.agv.calibration.common.JobQuery)
returns (.agv.calibration.common.StandardResponse);
//
rpc StreamExternalLocalizationTelemetry(StreamExternalLocalizationTelemetryRequest)
returns (stream ExternalLocalizationTelemetry);
//
rpc CommitExternalLocalizationResult(CommitExternalLocalizationResultRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc GetAppliedExternalLocalizationResult(GetAppliedExternalLocalizationResultRequest)
returns (AppliedExternalLocalizationResultResponse);
//
rpc EmergencyStop(.agv.calibration.common.Empty)
returns (.agv.calibration.common.StandardResponse);
}
// =========================================================
//
//
// =========================================================
message ExternalLocalizationWorkModeRequest {
.agv.calibration.common.RequestHeader header = 1; //
enum Mode {
EXTERNAL_LOCALIZATION_MODE_UNSPECIFIED = 0; //
NORMAL_MODE = 1; //
CALIBRATION_READY_MODE = 2; //
REFERENCE_COLLECTION_MODE = 3; //
VALIDATION_MODE = 4; //
}
Mode target_mode = 2; //
string reason = 3; //
}
// =========================================================
//
// /
// =========================================================
message ExternalLocalizationCapabilityRequest {
.agv.calibration.common.RequestHeader header = 1; //
}
// =========================================================
//
//
// =========================================================
message ExternalLocalizationCapabilityResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
bool supports_marker_alignment = 4; //
bool supports_reference_pose_collection = 5; // 姿
bool supports_repeatability_validation = 6; //
string localization_source_name = 7; //
}
// =========================================================
// 姿
// 姿
// =========================================================
message ReferencePoseCollectionTask {
uint32 sample_count = 1; //
bool require_vehicle_static = 2; //
double timeout_sec = 3; //
}
// =========================================================
//
// /
// =========================================================
message MarkerAlignmentTask {
string target_board_id = 1; // / ID
uint32 min_valid_observation_count = 2; //
double timeout_sec = 3; //
}
// =========================================================
//
//
// =========================================================
message ConsistencyValidationTask {
uint32 sample_count = 1; //
double max_position_stddev_m = 2; //
double max_yaw_stddev_rad = 3; //
double timeout_sec = 4; //
}
// =========================================================
//
//
// Ubuntu
// Windows / Linux
// =========================================================
message ExternalLocalizationTaskRequest {
.agv.calibration.common.RequestHeader header = 1; //
string test_case_id = 2; // ID
enum TaskPurpose {
EXTERNAL_LOCALIZATION_TASK_PURPOSE_UNSPECIFIED = 0; //
DATA_COLLECTION = 1; //
SOLVING = 2; //
VALIDATION = 3; //
}
TaskPurpose task_purpose = 3; //
oneof task {
ReferencePoseCollectionTask reference_pose_collection = 4; // 姿
MarkerAlignmentTask marker_alignment = 5; //
ConsistencyValidationTask consistency_validation = 6; //
}
string source_iteration_id = 7; //
}
// =========================================================
//
//
// =========================================================
message StreamExternalLocalizationTelemetryRequest {
.agv.calibration.common.RequestHeader header = 1; //
uint32 expected_hz = 2; //
bool include_pose = 3; // 姿
bool include_quality_metrics = 4; //
}
// =========================================================
//
//
// Windows / Linux
// Ubuntu
// =========================================================
message ExternalLocalizationTelemetry {
int64 hardware_timestamp_us = 1; //
bool pose_valid = 2; // 姿
.agv.calibration.common.Pose3D workshop_pose = 3; // 姿
double position_stddev_m = 4; //
double yaw_stddev_rad = 5; //
uint32 observed_target_count = 6; //
string reference_source_name = 7; //
string active_job_id = 8; // ID
}
// =========================================================
//
//
// =========================================================
message ExternalLocalizationCalibrationResult {
string workshop_frame_id = 1; // ID
string localization_frame_id = 2; // ID
.agv.calibration.common.Pose3D workshop_to_localization = 3; // 姿
double position_repeatability_m = 4; //
double yaw_repeatability_rad = 5; //
double residual_error_m = 6; //
double residual_error_rad = 7; // 姿
}
// =========================================================
//
// Linux
// =========================================================
message CommitExternalLocalizationResultRequest {
.agv.calibration.common.RequestHeader header = 1; //
string parameter_version = 2; //
ExternalLocalizationCalibrationResult result = 3; //
string commit_reason = 4; //
.agv.calibration.common.FileDigest digest = 5; //
bool persistent_write = 6; //
}
// =========================================================
//
// =========================================================
message GetAppliedExternalLocalizationResultRequest {
.agv.calibration.common.RequestHeader header = 1; //
}
// =========================================================
//
// =========================================================
message AppliedExternalLocalizationResultResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
string parameter_version = 4; //
ExternalLocalizationCalibrationResult result = 5; //
int64 applied_timestamp_us = 6; //
}
// =========================================================
//
// /
// =========================================================
message ExternalLocalizationJobResult {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
string job_id = 4; // ID
bool data_quality_passed = 5; //
bool suitable_for_commit = 6; //
string recommended_parameter_version = 7; //
ExternalLocalizationCalibrationResult result = 8; //
repeated .agv.calibration.common.FileReference artifacts = 9; //
}
@@ -0,0 +1,324 @@
syntax = "proto3";
package agv.calibration.sensor;
import "calibration_common.proto";
import "vehicle_profile.proto";
// =========================================================
//
//
// Ubuntu (Linux) -> Windows /
//
// 1) Linux / / IMU
// 2) /
// 3) IMU
// =========================================================
// =========================================================
//
// Windows Linux
// Ubuntu
// =========================================================
service AgvCalibSensorService {
//
rpc Heartbeat(.agv.calibration.common.HeartbeatRequest)
returns (.agv.calibration.common.HeartbeatResponse);
//
rpc SetSensorCalibrationWorkMode(SensorCalibrationWorkModeRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc GetSensorCalibrationCapability(SensorCalibrationCapabilityRequest)
returns (SensorCalibrationCapabilityResponse);
//
rpc StartSensorCalibrationTask(SensorCalibrationTaskRequest)
returns (.agv.calibration.common.JobAccepted);
//
rpc GetSensorCalibrationJobStatus(.agv.calibration.common.JobQuery)
returns (.agv.calibration.common.JobStatus);
//
rpc GetSensorCalibrationJobResult(.agv.calibration.common.JobQuery)
returns (SensorCalibrationJobResult);
//
rpc CancelSensorCalibrationJob(.agv.calibration.common.JobQuery)
returns (.agv.calibration.common.StandardResponse);
//
rpc StreamSensorCalibrationTelemetry(StreamSensorCalibrationTelemetryRequest)
returns (stream SensorCalibrationTelemetry);
//
rpc CommitSensorCalibrationParameters(CommitSensorCalibrationParametersRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc GetAppliedSensorCalibrationParameters(GetAppliedSensorCalibrationParametersRequest)
returns (AppliedSensorCalibrationParametersResponse);
//
rpc EmergencyStop(.agv.calibration.common.Empty)
returns (.agv.calibration.common.StandardResponse);
}
// =========================================================
//
//
// =========================================================
message SensorCalibrationWorkModeRequest {
.agv.calibration.common.RequestHeader header = 1; //
enum Mode {
SENSOR_CALIBRATION_MODE_UNSPECIFIED = 0; //
NORMAL_MODE = 1; //
CALIBRATION_READY_MODE = 2; //
DATA_CAPTURE_MODE = 3; //
VALIDATION_MODE = 4; //
}
Mode target_mode = 2; //
string reason = 3; //
}
// =========================================================
//
//
// =========================================================
message SensorCalibrationCapabilityRequest {
.agv.calibration.common.RequestHeader header = 1; //
}
// =========================================================
//
//
// =========================================================
message SensorCalibrationCapabilityResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
bool supports_camera_intrinsic = 4; //
bool supports_imu_intrinsic = 5; // IMU
bool supports_sensor_to_base_extrinsic = 6; // base_link
bool supports_hand_eye = 7; //
}
// =========================================================
//
//
// =========================================================
message CameraIntrinsicCalibrationTask {
string sensor_id = 1; // ID
uint32 required_image_count = 2;//
double timeout_sec = 3; //
}
// =========================================================
// IMU
// IMU /
// =========================================================
message IMUIntrinsicCalibrationTask {
string sensor_id = 1; // IMU ID
uint32 required_static_segment_count = 2; //
uint32 required_motion_segment_count = 3; //
double timeout_sec = 4; //
}
// =========================================================
// base_link
// base_link 姿
// =========================================================
message SensorToBaseExtrinsicCalibrationTask {
string sensor_id = 1; // ID
string base_frame_id = 2; // base_link / frame
uint32 required_sample_count = 3;//
double timeout_sec = 4; //
}
// =========================================================
//
//
// =========================================================
enum HandEyeCalibrationMode {
HAND_EYE_CALIBRATION_MODE_UNSPECIFIED = 0; //
EYE_IN_HAND = 1; //
EYE_TO_HAND = 2; //
}
// =========================================================
//
// /
// =========================================================
message HandEyeCalibrationTask {
string sensor_id = 1; // ID
string arm_id = 2; // ID
HandEyeCalibrationMode mode = 3; //
uint32 required_pose_count = 4; // 姿
double timeout_sec = 5; //
}
// =========================================================
//
//
// Ubuntu
// Windows / Linux
// =========================================================
message SensorCalibrationTaskRequest {
.agv.calibration.common.RequestHeader header = 1; //
string test_case_id = 2; // ID
enum TaskPurpose {
SENSOR_TASK_PURPOSE_UNSPECIFIED = 0; //
DATA_COLLECTION = 1; //
SOLVING = 2; //
VALIDATION = 3; //
}
TaskPurpose task_purpose = 3; //
oneof task {
CameraIntrinsicCalibrationTask camera_intrinsic = 4; //
IMUIntrinsicCalibrationTask imu_intrinsic = 5; // IMU
SensorToBaseExtrinsicCalibrationTask sensor_to_base_extrinsic = 6; //
HandEyeCalibrationTask hand_eye = 7; //
}
string source_iteration_id = 8; //
}
// =========================================================
//
//
// =========================================================
message StreamSensorCalibrationTelemetryRequest {
.agv.calibration.common.RequestHeader header = 1; //
uint32 expected_hz = 2; //
bool include_observation_progress = 3; //
bool include_quality_metrics = 4; //
}
// =========================================================
//
//
// =========================================================
message SensorCalibrationTelemetry {
int64 hardware_timestamp_us = 1; //
string sensor_id = 2; // ID
.agv.calibration.vehicle.profile.SensorType sensor_type = 3; //
uint32 collected_sample_count = 4; //
uint32 target_sample_count = 5; //
bool target_detected = 6; //
double quality_score = 7; //
string active_job_id = 8; // ID
}
// =========================================================
//
//
// =========================================================
message CameraIntrinsics {
uint32 image_width = 1; //
uint32 image_height = 2; //
double fx = 3; // fx
double fy = 4; // fy
double cx = 5; // cx
double cy = 6; // cy
repeated double distortion_coeffs = 7; //
string distortion_model = 8; //
}
// =========================================================
// IMU
// IMU
//
// 1) accel_bias
// 2) gyro_bias
// =========================================================
message IMUIntrinsics {
.agv.calibration.common.Vector3D accel_bias = 1; //
.agv.calibration.common.Vector3D gyro_bias = 2; //
}
// =========================================================
//
// base_link 姿
// =========================================================
message SensorExtrinsics {
string parent_frame_id = 1; // base_link
string child_frame_id = 2; // sensor frame
.agv.calibration.common.Pose3D parent_to_child = 3; // 姿
}
// =========================================================
//
// /
// =========================================================
message SensorCalibrationParameter {
string sensor_id = 1; // ID
.agv.calibration.vehicle.profile.SensorType sensor_type = 2; //
CameraIntrinsics camera_intrinsics = 3; //
IMUIntrinsics imu_intrinsics = 4; // IMU IMU
SensorExtrinsics extrinsics = 5; //
}
// =========================================================
//
//
// =========================================================
message SensorCalibrationParameterSet {
repeated SensorCalibrationParameter items = 1; //
}
// =========================================================
//
// Linux
// =========================================================
message CommitSensorCalibrationParametersRequest {
.agv.calibration.common.RequestHeader header = 1; //
string parameter_version = 2; //
SensorCalibrationParameterSet params = 3; //
string commit_reason = 4; //
.agv.calibration.common.FileDigest digest = 5; //
bool persistent_write = 6; //
}
// =========================================================
//
// =========================================================
message GetAppliedSensorCalibrationParametersRequest {
.agv.calibration.common.RequestHeader header = 1; //
}
// =========================================================
//
// =========================================================
message AppliedSensorCalibrationParametersResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
string parameter_version = 4; //
SensorCalibrationParameterSet params = 5; //
int64 applied_timestamp_us = 6; //
}
// =========================================================
//
// /
// =========================================================
message SensorCalibrationJobResult {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
string job_id = 4; // ID
bool data_quality_passed = 5; //
bool suitable_for_commit = 6; //
string recommended_parameter_version = 7; //
double reprojection_error_px = 8; //
double translation_residual_m = 9; //
double rotation_residual_deg = 10; //
repeated .agv.calibration.common.FileReference artifacts = 11; //
}
@@ -0,0 +1,251 @@
syntax = "proto3";
package agv.calibration.vehicle.profile;
import "calibration_common.proto";
// =========================================================
//
// 使
// 1)
// 2)
// =========================================================
// =========================================================
//
// Ubuntu
//
// =========================================================
service VehicleProfileService {
//
rpc Heartbeat(.agv.calibration.common.HeartbeatRequest)
returns (.agv.calibration.common.HeartbeatResponse);
// /
rpc RegisterOrUpdateVehicleProfile(RegisterOrUpdateVehicleProfileRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc GetVehicleProfile(VehicleProfileQuery)
returns (VehicleProfileResponse);
//
rpc EvaluateVehicleCalibrationApplicability(
EvaluateVehicleCalibrationApplicabilityRequest)
returns (VehicleCalibrationApplicabilityResponse);
}
// =========================================================
//
//
// =========================================================
enum ChassisType {
CHASSIS_TYPE_UNSPECIFIED = 0; //
ACKERMANN = 1; //
DIFFERENTIAL = 2; //
SINGLE_STEER_WHEEL = 3; //
MULTI_STEER_WHEEL = 4; //
}
// =========================================================
//
//
// =========================================================
enum SensorType {
SENSOR_TYPE_UNSPECIFIED = 0; //
DOWNWARD_CAMERA = 1; //
FRONT_CAMERA = 2; //
ARM_CAMERA = 3; //
LIDAR_3D = 4; // 3D
LIDAR_2D = 5; // 2D
IMU = 6; // IMU
}
// =========================================================
//
// /
// =========================================================
enum CameraMountType {
CAMERA_MOUNT_TYPE_UNSPECIFIED = 0; //
FIXED_ON_BASE = 1; //
DOWNWARD_MOUNTED = 2; //
FRONT_MOUNTED = 3; //
EYE_IN_HAND = 4; //
EYE_TO_HAND = 5; //
}
// =========================================================
//
//
// =========================================================
enum ControlAxisType {
CONTROL_AXIS_UNSPECIFIED = 0; //
LATERAL_CONTROL = 1; //
LONGITUDINAL_CONTROL = 2; //
}
// =========================================================
//
//
// =========================================================
enum ControllerAlgorithmType {
CONTROLLER_ALGORITHM_UNSPECIFIED = 0; //
PID = 1; // PID
MPC = 2; // MPC
LQR = 3; // LQR
PURE_PURSUIT = 4; // Pure Pursuit
}
// =========================================================
//
//
// =========================================================
enum CalibrationAbilityType {
CALIBRATION_ABILITY_UNSPECIFIED = 0; //
EXTERNAL_LOCALIZATION_CALIBRATION = 1; // /
CHASSIS_CALIBRATION = 2; //
CONTROL_CALIBRATION = 3; //
SENSOR_CALIBRATION = 4; //
HAND_EYE_CALIBRATION = 5; //
}
// =========================================================
//
//
// =========================================================
message VehicleBaseInfo {
string vehicle_id = 1; // ID
string vehicle_name = 2; //
string model_name = 3; // /
string serial_number = 4; //
string manufacturer = 5; //
string description = 6; //
}
// =========================================================
//
//
// =========================================================
message MechanicalArmProfile {
bool has_mechanical_arm = 1; //
string arm_id = 2; // ID
string arm_model = 3; //
uint32 dof = 4; //
string arm_base_frame = 5; //
string tool_frame = 6; //
}
// =========================================================
//
//
// =========================================================
message SensorProfile {
string sensor_id = 1; // ID
SensorType sensor_type = 2; //
string sensor_name = 3; //
string frame_id = 4; // frame_id
CameraMountType camera_mount_type = 5;//
bool enabled = 6; //
bool needs_intrinsic_calibration = 7; //
bool needs_extrinsic_calibration = 8; //
string device_hint = 9; // / topic / IP /
}
// =========================================================
//
//
// =========================================================
message ControllerProfile {
ControlAxisType control_axis = 1; //
repeated ControllerAlgorithmType supported_algorithms = 2; //
ControllerAlgorithmType default_algorithm = 3; //
}
// =========================================================
//
//
// =========================================================
message CalibrationCapability {
CalibrationAbilityType ability_type = 1; //
bool supported = 2; //
string message = 3; //
}
// =========================================================
//
//
// /
// /
// =========================================================
message VehicleProfile {
VehicleBaseInfo base_info = 1; //
ChassisType chassis_type = 2; //
MechanicalArmProfile arm_profile = 3; //
repeated SensorProfile sensors = 4; //
repeated ControllerProfile controllers = 5; //
.agv.calibration.common.FileReference urdf_file = 6; // URDF
string base_link_frame = 7; // base_link
repeated CalibrationCapability capabilities = 8; //
repeated .agv.calibration.common.KeyValuePair metadata = 9; //
}
// =========================================================
// /
//
// =========================================================
message RegisterOrUpdateVehicleProfileRequest {
.agv.calibration.common.RequestHeader header = 1; //
VehicleProfile profile = 2; //
}
// =========================================================
//
// vehicle_id
// =========================================================
message VehicleProfileQuery {
.agv.calibration.common.RequestHeader header = 1; //
string vehicle_id = 2; // ID
}
// =========================================================
//
//
// =========================================================
message VehicleProfileResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
VehicleProfile profile = 4; //
}
// =========================================================
//
//
// =========================================================
message EvaluateVehicleCalibrationApplicabilityRequest {
.agv.calibration.common.RequestHeader header = 1; //
VehicleProfile profile_snapshot = 2; //
}
// =========================================================
//
// / /
// =========================================================
message ApplicabilityIssue {
CalibrationAbilityType ability_type = 1; //
bool blocking = 2; //
string message = 3; //
}
// =========================================================
//
//
// =========================================================
message VehicleCalibrationApplicabilityResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
repeated CalibrationCapability capabilities = 4; //
repeated ApplicabilityIssue issues = 5; //
bool overall_supported = 6; //
}
@@ -0,0 +1,475 @@
syntax = "proto3";
package agv.calibration.workshop;
import "calibration_common.proto";
import "vehicle_profile.proto";
// =========================================================
//
// 使
// 1)
// 2)
// 3) / / /
// =========================================================
// =========================================================
//
// Ubuntu
//
// =========================================================
service WorkshopOrchestrationService {
//
rpc Heartbeat(.agv.calibration.common.HeartbeatRequest)
returns (.agv.calibration.common.HeartbeatResponse);
//
rpc CreateWorkshopSession(CreateWorkshopSessionRequest)
returns (CreateWorkshopSessionResponse);
//
rpc GetWorkshopSession(WorkshopSessionQuery)
returns (WorkshopSessionResponse);
//
rpc BuildExecutionPlan(BuildExecutionPlanRequest)
returns (BuildExecutionPlanResponse);
// / readiness
rpc RunWorkshopPrecheck(RunWorkshopPrecheckRequest)
returns (.agv.calibration.common.JobAccepted);
//
rpc GetLastWorkshopPrecheckResult(WorkshopSessionQuery)
returns (WorkshopPrecheckResponse);
//
rpc StartWorkshopSession(StartWorkshopSessionRequest)
returns (.agv.calibration.common.JobAccepted);
//
rpc GetWorkshopJobStatus(.agv.calibration.common.JobQuery)
returns (.agv.calibration.common.JobStatus);
//
rpc PauseWorkshopSession(PauseWorkshopSessionRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc ResumeWorkshopSession(ResumeWorkshopSessionRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc CancelWorkshopSession(CancelWorkshopSessionRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc StreamWorkshopEvent(WorkshopSessionQuery)
returns (stream WorkshopEvent);
//
rpc AcknowledgeManualStep(ManualStepAckRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc ApproveStageResult(ApproveStageResultRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc RollbackStageParameters(RollbackStageParametersRequest)
returns (.agv.calibration.common.StandardResponse);
//
rpc GetWorkshopReport(WorkshopSessionQuery)
returns (WorkshopReportResponse);
}
// =========================================================
//
//
// =========================================================
enum WorkshopSessionState {
WORKSHOP_SESSION_STATE_UNSPECIFIED = 0; //
DRAFT = 1; // 稿
PLANNING = 2; //
READY = 3; //
RUNNING = 4; //
PAUSED = 5; //
WAITING_MANUAL_ACTION = 6; //
WAITING_APPROVAL = 7; //
SUCCEEDED = 8; //
FAILED = 9; //
CANCELED = 10; //
ROLLED_BACK = 11; //
}
// =========================================================
//
//
// =========================================================
enum WorkshopStageType {
WORKSHOP_STAGE_TYPE_UNSPECIFIED = 0; //
STAGE_EXTERNAL_LOCALIZATION = 1; // /
STAGE_CHASSIS_CALIBRATION = 2; //
STAGE_CONTROL_CALIBRATION = 3; //
STAGE_SENSOR_CALIBRATION = 4; //
STAGE_VALIDATION = 5; //
STAGE_MANUAL_CHECK = 6; // / /
STAGE_REPORT_FINALIZATION = 7; //
}
// =========================================================
//
//
// =========================================================
enum StageExecutionPolicy {
STAGE_EXECUTION_POLICY_UNSPECIFIED = 0; //
REQUIRED = 1; //
OPTIONAL = 2; //
SKIP_IF_UNSUPPORTED = 3; //
}
// =========================================================
//
//
// =========================================================
enum ApprovalState {
APPROVAL_STATE_UNSPECIFIED = 0; //
APPROVAL_NOT_REQUIRED = 1; //
APPROVAL_PENDING = 2; //
APPROVED = 3; //
REJECTED = 4; //
}
// =========================================================
//
//
// =========================================================
enum ManualActionType {
MANUAL_ACTION_TYPE_UNSPECIFIED = 0; //
PLACE_TARGET_BOARD = 1; // /
CONFIRM_WORKCELL_CLEAR = 2; //
CONFIRM_VEHICLE_POSE = 3; // 姿
CONFIRM_ARM_HOME = 4; //
CONFIRM_SENSOR_INSTALLATION = 5; //
CONFIRM_READY_TO_CONTINUE = 6; //
OTHER_MANUAL_ACTION = 7; //
}
// =========================================================
//
// /
// =========================================================
enum WorkshopEventType {
WORKSHOP_EVENT_TYPE_UNSPECIFIED = 0; //
SESSION_STATE_CHANGED = 1; //
STAGE_STARTED = 2; //
STAGE_COMPLETED = 3; //
STAGE_FAILED = 4; //
MANUAL_ACTION_REQUIRED = 5; //
APPROVAL_REQUIRED = 6; //
SAFETY_TRIGGERED = 7; //
REPORT_READY = 8; //
}
// =========================================================
//
//
// =========================================================
message WorkshopOperatorInfo {
string operator_id = 1; //
string operator_name = 2; //
string workstation_id = 3;// ID
string shift_id = 4; // ID
}
// =========================================================
//
//
// =========================================================
message RequestedCalibrationTask {
WorkshopStageType stage_type = 1; //
bool enabled = 2; //
bool require_manual_approval = 3; //
string reason = 4; // /
}
// =========================================================
//
//
// =========================================================
message WorkshopSessionConfig {
repeated RequestedCalibrationTask requested_tasks = 1; //
bool auto_commit_parameters = 2; //
bool require_manual_approval_before_commit = 3; //
bool run_validation_after_each_stage = 4; //
bool stop_on_first_failure = 5; //
bool allow_optional_stage_skip = 6; //
bool enable_auto_rollback_on_validation_failure = 7; //
}
// =========================================================
//
//
// =========================================================
message StagePlan {
string stage_id = 1; // ID
WorkshopStageType stage_type = 2; //
string display_name = 3; //
uint32 order_index = 4; //
StageExecutionPolicy execution_policy = 5; //
repeated string depends_on_stage_ids = 6; // ID
bool requires_manual_confirmation_before_start = 7; //
bool requires_approval_before_commit = 8; //
ManualActionType manual_action_type = 9; //
string executor_service_name = 10; //
string description = 11; //
uint32 retry_limit = 12; //
}
// =========================================================
//
//
// =========================================================
message WorkshopSession {
string session_id = 1; // ID
WorkshopSessionState state = 2; //
.agv.calibration.vehicle.profile.VehicleProfile vehicle_profile_snapshot = 3; //
WorkshopOperatorInfo operator_info = 4; //
WorkshopSessionConfig config = 5; //
repeated StagePlan stage_plan = 6; //
int64 created_timestamp_us = 7; //
int64 updated_timestamp_us = 8; //
string active_stage_id = 9; // ID
}
// =========================================================
//
//
// =========================================================
message CreateWorkshopSessionRequest {
.agv.calibration.common.RequestHeader header = 1; //
.agv.calibration.vehicle.profile.VehicleProfile vehicle_profile_snapshot = 2; //
WorkshopOperatorInfo operator_info = 3; //
WorkshopSessionConfig config = 4; //
string workshop_line_id = 5; // 线 / 线 ID
}
// =========================================================
//
//
// =========================================================
message CreateWorkshopSessionResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
string session_id = 4; // ID
WorkshopSessionState state = 5; //
}
// =========================================================
//
// session_id
// =========================================================
message WorkshopSessionQuery {
.agv.calibration.common.RequestHeader header = 1; //
string session_id = 2; // ID
}
// =========================================================
//
//
// =========================================================
message WorkshopSessionResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
WorkshopSession session = 4; //
}
// =========================================================
//
//
// =========================================================
message BuildExecutionPlanRequest {
.agv.calibration.common.RequestHeader header = 1; //
string session_id = 2; // ID
}
// =========================================================
//
//
// =========================================================
message BuildExecutionPlanResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
repeated StagePlan stage_plan = 4; //
repeated string warnings = 5; //
}
// =========================================================
//
//
// =========================================================
message RunWorkshopPrecheckRequest {
.agv.calibration.common.RequestHeader header = 1; //
string session_id = 2; // ID
}
// =========================================================
//
// readiness / /
// =========================================================
message PrecheckItem {
string item_code = 1; //
string display_name = 2; //
bool passed = 3; //
bool blocking = 4; //
.agv.calibration.common.ErrorCode error_code = 5; //
string message = 6; //
}
// =========================================================
//
//
// =========================================================
message WorkshopPrecheckResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
bool all_passed = 4; //
repeated PrecheckItem items = 5; //
int64 checked_timestamp_us = 6; //
}
// =========================================================
//
//
// =========================================================
message StartWorkshopSessionRequest {
.agv.calibration.common.RequestHeader header = 1; //
string session_id = 2; // ID
}
// =========================================================
//
//
// =========================================================
message PauseWorkshopSessionRequest {
.agv.calibration.common.RequestHeader header = 1; //
string session_id = 2; // ID
string reason = 3; //
}
// =========================================================
//
//
// =========================================================
message ResumeWorkshopSessionRequest {
.agv.calibration.common.RequestHeader header = 1; //
string session_id = 2; // ID
string reason = 3; //
}
// =========================================================
//
//
// =========================================================
message CancelWorkshopSessionRequest {
.agv.calibration.common.RequestHeader header = 1; //
string session_id = 2; // ID
string reason = 3; //
}
// =========================================================
//
// /
// =========================================================
message WorkshopEvent {
int64 server_timestamp_us = 1; //
string session_id = 2; // ID
string stage_id = 3; // ID
WorkshopEventType event_type = 4; //
WorkshopSessionState session_state = 5; //
.agv.calibration.common.JobState stage_job_state = 6; //
bool requires_manual_ack = 7; //
string message = 8; //
}
// =========================================================
//
//
// =========================================================
message ManualStepAckRequest {
.agv.calibration.common.RequestHeader header = 1; //
string session_id = 2; // ID
string stage_id = 3; // ID
bool confirmed = 4; //
string note = 5; //
}
// =========================================================
//
// /
// =========================================================
message ApproveStageResultRequest {
.agv.calibration.common.RequestHeader header = 1; //
string session_id = 2; // ID
string stage_id = 3; // ID
ApprovalState decision = 4; //
string reviewer_id = 5; // ID
string comment = 6; //
}
// =========================================================
//
//
// =========================================================
message RollbackStageParametersRequest {
.agv.calibration.common.RequestHeader header = 1; //
string session_id = 2; // ID
string stage_id = 3; // ID
string target_parameter_version = 4; //
string reason = 5; //
}
// =========================================================
//
//
// =========================================================
message StageResultSummary {
string stage_id = 1; // ID
WorkshopStageType stage_type = 2; //
bool success = 3; //
.agv.calibration.common.JobState final_job_state = 4; //
ApprovalState approval_state = 5; //
string parameter_version = 6; //
repeated .agv.calibration.common.FileReference artifacts = 7; //
string summary = 8; //
}
// =========================================================
//
//
// =========================================================
message WorkshopReport {
string session_id = 1; // ID
bool overall_success = 2; //
int64 started_timestamp_us = 3; //
int64 finished_timestamp_us = 4; //
repeated StageResultSummary stage_results = 5; //
repeated .agv.calibration.common.FileReference report_files = 6; //
string summary = 7; //
}
// =========================================================
//
//
// =========================================================
message WorkshopReportResponse {
bool success = 1; //
.agv.calibration.common.ErrorCode error_code = 2; //
string message = 3; //
WorkshopReport report = 4; //
}
@@ -1,6 +0,0 @@
string task_id
win_ubuntu_bridge/CameraIntrinsic[] updated_intrinsics
win_ubuntu_bridge/SensorExtrinsic[] updated_extrinsics
---
bool success
string message
@@ -1,26 +0,0 @@
bool has_wheel_radius_fl
float64 wheel_radius_fl_m
bool has_wheel_radius_fr
float64 wheel_radius_fr_m
bool has_wheel_radius_rl
float64 wheel_radius_rl_m
bool has_wheel_radius_rr
float64 wheel_radius_rr_m
bool has_steer_zero_offset_front
float64 steer_zero_offset_front_deg
bool has_steer_zero_offset_rear
float64 steer_zero_offset_rear_deg
bool has_effective_track_width
float64 effective_track_width_m
bool has_effective_wheel_base
float64 effective_wheel_base_m
bool has_icr_offset_x
float64 icr_offset_x_m
bool has_icr_offset_y
float64 icr_offset_y_m
---
bool success
string message
@@ -1,7 +0,0 @@
float64 left_motor_cmd
float64 right_motor_cmd
float64 steering_angle
float64 duration_sec
---
bool success
string message
@@ -1,9 +0,0 @@
string test_case_id
float64 fl_motor_rpm
float64 fr_motor_rpm
float64 rl_motor_rpm
float64 rr_motor_rpm
float64 duration_sec
---
bool success
string message
@@ -1,10 +0,0 @@
string test_case_id
float64 front_steer_angle_deg
float64 rear_steer_angle_deg
bool has_sweep
float64 sweep_amplitude_deg
float64 sweep_frequency_hz
float64 duration_sec
---
bool success
string message
@@ -1,5 +0,0 @@
float64 target_velocity_ms
float64 duration_sec
---
bool success
string message
@@ -1,5 +0,0 @@
string test_case_id
win_ubuntu_bridge/TrajectoryPoint[] path
---
bool success
string message
@@ -0,0 +1,24 @@
# =========================================================
# 文件作用:心跳服务
# 对应 protoHeartbeatRequest + HeartbeatResponse
# 作用:保持 Linux 与 Windows 车端代理 / Linux 子服务之间的在线状态
# 调用方:Ubuntu 车间电脑 或 上位服务
# 服务方:Windows 车端代理 / Linux 子服务
# =========================================================
# =========================
# 请求部分
# =========================
RequestHeader header # 请求头
string agent_name # 服务名 / 代理名
int32 expect_next_heartbeat_ms # 下次期望心跳间隔(ms)
---
# =========================
# 响应部分
# =========================
bool success # 是否正常
uint16 error_code # 错误码,取值参考 ErrorCode.msg
string message # 说明文字
int64 server_timestamp_us # 响应时间戳
bool vehicle_ready # 车辆 / 服务是否准备好接任务
@@ -1,32 +0,0 @@
bool has_wheel_radius_left_ratio
float64 wheel_radius_left_ratio
bool has_wheel_radius_right_ratio
float64 wheel_radius_right_ratio
bool has_effective_track_width_m
float64 effective_track_width_m
bool has_steering_zero_offset_deg
float64 steering_zero_offset_deg
bool has_pid_kp_lateral
float64 pid_kp_lateral
bool has_pid_ki_lateral
float64 pid_ki_lateral
bool has_pid_kd_lateral
float64 pid_kd_lateral
bool has_pid_kp_heading
float64 pid_kp_heading
bool has_pid_ki_heading
float64 pid_ki_heading
bool has_pid_kd_heading
float64 pid_kd_heading
bool has_pure_pursuit_lookahead_m
float64 pure_pursuit_lookahead_m
bool has_mpc_weight_q_lateral
float64 mpc_weight_q_lateral
bool has_mpc_weight_r_steering
float64 mpc_weight_r_steering
---
bool success
string message
@@ -1,7 +0,0 @@
float64 target_x_m
float64 target_y_m
float64 target_yaw_deg
bool is_relative
---
bool success
string message
@@ -1,4 +0,0 @@
uint8 target_mode # 0: NORMAL, 1: OPEN_LOOP, 2: TUNING_MODE
---
bool success
string message
@@ -1,4 +0,0 @@
uint8 target_mode # 0: NORMAL_KINEMATICS, 1: DIRECT_RAW_DRIVE
---
bool success
string message
@@ -1,5 +0,0 @@
string[] sensor_ids
---
bool success
int64 capture_timestamp_us # 🚨 极其关键的“取件码”
string error_message
File diff suppressed because it is too large Load Diff