# dsh-ros2 安全框架 · 本体适配交接文档 > 版本 0.14.0(规划)· 配套 `README.md`(使用)· `architecture.md`(总体架构) > 本文档用于与**后续具体机器人本体适配 agent** 交接,划分清晰的职能边界。 --- ## 1. 职能边界(本交接文档的核心) | 归属 | 范围 | | --- | --- | | **dsh-ros2(本仓库,通用框架)** | 安全框架的整体设计、接口与契约定义、`safety_monitor` 节点骨架、状态机(锁存/恢复)、profile `safety` 段 schema 与校验、VLM 仲裁管线(固定格式化脚本 + 默认 prompt)、几何预检钩子、取证环形缓冲、故障注入测试骨架、工具层集成(锁状态查询/拒绝下发)。**对所有机器人通用,不绑定任何本体。** | | **下游本体适配 agent(具体机器人)** | 本体相关的**数据源**(joint_states/torque 话题名与消息类型)、**数值标定**(阈值/限位/频率/看门狗名单)、**锁动作实现**(zero_velocity 之外:阻尼/柔顺接口)、**力矩前馈动力学参数**(若已有辨识结果)、**轻量触发模型**(如 YOLO,接口已留)、**非 ROS 急停硬件通路**(后续定义)。 | | **预留接口(暂不实现)** | 计算力矩前馈、YOLO 轻量触发、非 ROS 急停。接口与注释先行,本体适配时按需实现。 | **铁律**:通用框架不得为迁就某一本体而改变契约;下游不得修改框架的锁存/失效语义,只允许通过 profile 配置与接口实现进行适配。 --- ## 2. 设计原则(已确认) 1. **分层防御**:几何预检(执行前,µs-ms)→ 反应式监视(执行中,检测 1-10ms、响应 ≤100ms)→ VLM 语义仲裁(方案变更后,秒级)→ 人类裁决(兜底)。 2. **事件驱动慢层**:VLM 绝不进控制回路,不必要时不拉起;仅在任务方案整体大改(A-B-C → A-D-E-C)或 2/3 异常触发时启动。 3. **失效闭环(fail-closed)**:安全系统自身崩溃/心跳丢失 → 默认锁死;绝不默认放行。 4. **锁存(latch)**:锁一旦触发,条件消失也不自动恢复;必须人工确认 + 显式恢复流程(解锁 → 重新回 home)。 5. **非致命不锁**:严重级别分级,WARNING 级只记录/通知,不触发锁,避免反复锁死。 6. **统一走 ROS2**:除非 ROS 急停(仅接口)外,监视、状态、解锁、参数全部走 ROS2 话题/服务/参数,保证 `ros2 node/topic/param` 可自检可调试。 7. **可注册可配置**:所有阈值、话题、名单、锁动作经 profile `safety` 段注册;参数修改走 L2 审批。 8. **兼容有无力矩反馈**:力觉检查器是可选插件,无 torque 话题时自动禁用并在档案中明示。 --- ## 3. 总体架构 ```mermaid flowchart LR subgraph 指令链 CMD[任务指令/策略] --> PRE[几何预检
限位·速度·FK自碰撞] PRE --> CTL[机器人控制器] end subgraph 通用安全框架 dsh-ros2 MON[safety_monitor 节点
motion·torque·feedback_loss·watchdog] FB[(环形缓冲 取证)] ARB[VLM 安全仲裁
固定脚本+可配置prompt] ST[/safety/state 锁存/] end subgraph 本体层 下游适配 JS[(joint_states + 可选 torque)] LK[lock_action 实现
zero_velocity/阻尼/柔顺] FF[计算力矩前馈 预留] end subgraph 人机层 H[人类裁决/解锁] end JS --> MON MON --> ST MON --> FB MON -->|CRITICAL 锁存| LK MON -.事件触发.-> ARB PRE -.事件触发(plan_change).-> ARB ARB -->|非safe| H H -->|确认/恢复| CTL FF -.预留.-> MON CTL --> JS ``` ### 3.1 状态机(锁存语义) ``` NORMAL ──CRITICAL 事件──▶ LOCKED(锁存) ▲ │ └── 人工确认 + 恢复流程 ◀──┘ (解锁 → 重新回 home → 重新注册监视) ``` - `NORMAL`:正常执行;所有运动指令执行前查询 `/safety/state`。 - `LOCKED`:拒绝一切新运动指令(工具层返回锁原因);锁存不自动清除。 - **恢复仅一条路径**:人类通过解锁服务(L2 审批)确认 → 机器人回 home → 恢复监视。 - WARNING 级事件不进入 LOCKED,仅记录 + 通知(含可选 VLM 诊断)。 --- ## 4. 接口契约(dsh-ros2 交付物) ### 4.1 profile `safety` 段(schema,`~/.dsh-ros2/robots/.yaml`) ```yaml safety: enabled: true control_frequency: 200 # Hz;监视器非阻塞、并行运行,响应预算 ≤100ms checkers: [motion, feedback_loss, watchdog] # 可插拔;torque 探测到话题时自动追加 lock_action: zero_velocity # 最小实现:停发新指令并保持;可注册 damping / 本体柔顺接口 feedback: joint_state_topic: /joint_states torque_topic: "" # 空 = 无力矩反馈 → torque_checker 自动禁用并明示 timeout_ms: 100 # 静默判失联(CRITICAL) watchdog: critical_nodes: [] # 掉线即锁(例:controller_manager;safety_monitor 自身由心跳覆盖) observed_nodes: [] # 掉线仅 WARNING,不锁(避免非主要进程意外退出导致全锁) heartbeat_ms: 500 motion: tracking_error_rad: 0.05 # 位置偏差阈值(跟踪误差监视) stall: {window_ms: 200, min_cmd_vel: 0.02, max_actual_vel: 0.005} # 有指令无运动 hysteresis: {min_frames: 3, window: 5} # M-of-K 迟滞,单帧噪声不锁 max_velocity: {} # 每关节 rad/s,缺省取 URDF limit(预检用) max_acceleration: {} # 每关节 rad/s²(预检用) torque: # 无 torque_topic 时整段忽略 enabled: true abs_limit: {} # 每关节 Nm 绝对值上限 dtau_limit: {} # 突变阈值 Nm/s overload_ms: 500 # 持续超限判 CRITICAL feedforward_topic: "" # 计算力矩前馈输入(预留,见 §4.5) semantic: enabled: true arbitrate_script: safety_vlm_arbitrate # 固定格式化脚本(见 §4.6) prompt: | # 可个性化配置的裁决依据;缺省见 §5 你是机器人安全裁决员。…… trigger_on: [plan_change, tracking_error, stall, feedback_loss, watchdog_critical, torque_spike, torque_overload] forensics: ring_buffer_s: 5 # 触发前环形缓冲(关节/力矩/渲染帧) dump_dir: ~/.dsh-ros2/safety-events estop: # 仅接口,不实现(后续硬件就绪时定义) enabled: false path: "" ``` ### 4.2 ROS2 接口(safety_monitor 节点) | 方向 | 名称 | 类型 | 说明 | | --- | --- | --- | --- | | 订阅 | `feedback.joint_state_topic` | sensor_msgs/JointState | 关节反馈(必需) | | 订阅 | `feedback.torque_topic` | 本体定义 | 力矩反馈(可选,无则 torque 禁用) | | 订阅 | `feedback.feedforward_topic` | 本体定义 | 力矩前馈(预留) | | 发布 | `/safety/state` | 自定义 `SafetyState` | 锁存状态 + 触发原因 + 严重级(锁定时所有运动工具拒绝下发) | | 发布 | `/safety/event` | 自定义 `SafetyEvent` | 事件流(含触发原因分类字符串,供取证/仲裁) | | 发布 | `/safety/heartbeat` | std_msgs/Bool | 监视器自身心跳(供外部 watchdog 监控;心跳丢 = fail-closed 锁) | | 服务 | `/safety/get_state` | 查询 | L1 只读 | | 服务 | `/safety/unlock` | 解锁 | **仅经 L2 审批工具调用**,人类确认后才生效 | > 接口消息类型(msg/srv)由 dsh-ros2 定义并随包发布;本体自定义字段一律经 profile 映射,不改变契约。 ### 4.3 checker 插件接口(下游可按需实现) ```python class SafetyChecker: name: str # 例: torque severity_map: dict # 事件→CRITICAL/WARNING def setup(self, params: dict) # profile 段注入 def on_joint_state(self, msg) # 非阻塞回调 def decide(self) -> Event|None # 返回触发事件(含 cause 分类字符串)或 None # 约定:回调不得阻塞;decide 与主循环并行,端到端响应 ≤100ms ``` ### 4.4 lock_action 接口(下游实现) ```python class LockAction: name: str # zero_velocity | damping | <本体柔顺接口> def activate(self) # CRITICAL 触发时调用(锁存) def deactivate(self) # 人工解锁后调用 ``` - **最小实现(dsh-ros2 交付)**:`zero_velocity` —— 停发新运动指令 + 通知控制器保持。 - **理想实现(下游)**:本体支持的阻尼/柔顺控制(如 lite 小组已验证的零力拖拽链路),直接切该接口。 ### 4.5 计算力矩前馈预留接口(暂不实现) - 已确认 lite 上存在动力学参数辨识 + 计算力矩法前馈(零力拖拽效果良好)。 - 预留 `torque.feedforward_topic` 输入 + `SafetyChecker` 中的前馈叠加钩子;本仓库只留接口与注释,**不实现**。下游在有力矩反馈的机器人上接入辨识参数。 - 顺带注释(留给后续维护):**碰撞后关节可能丢信号**,导致力矩突变检测不触发 —— 需结合 feedback_loss 与 watchdog 兜底;此坑位在 `torque_checker` 实现中留文字说明。 ### 4.6 VLM 安全仲裁(固定脚本 + 可配置 prompt) - **固定格式化脚本** `scripts/safety_vlm_arbitrate.py`:输入 `(task_context JSON, trigger_cause 字符串, 新鲜渲染帧路径, joint_state 快照)` → 确定性拼装 prompt(**不允许 LLM 参与拼装**,保证高效与可复现)→ 调用既有 VLM 网关 → 输出 `{verdict: safe|unsafe|uncertain, reason, evidence}`。 - **裁决方向**:非 `safe` 一律触发人类裁决(uncertain 按 unsafe 处理)。 - **画面新鲜度**:触发时先抓一帧新的离屏渲染(30Hz/22Hz 成本可控)再送仲裁。 - **prompt 可配置**:经 `semantic.prompt` 个性化;缺省值见 §5。 ### 4.7 触发原因分类(预设字符串,输入仲裁 prompt) | 预设字符串 | 含义 | 严重级 | 是否锁 | | --- | --- | --- | --- | | `feedback_loss` | 关节反馈静默 | CRITICAL | 锁 | | `watchdog_critical` | 关键节点/进程掉线 | CRITICAL | 锁 | | `tracking_error` | 轨迹跟踪偏差超限(迟滞后) | CRITICAL | 锁 | | `stall` | 堵转:有指令无运动 | CRITICAL | 锁 | | `torque_spike` | 力矩突变 | CRITICAL | 锁 | | `torque_overload` | 力矩持续超限 | CRITICAL | 锁 | | `plan_change` | 任务方案整体变更 | 仲裁 | 不直接锁,拉起 VLM | | `watchdog_observed` | 非关键节点掉线 | WARNING | 不锁,记录+通知 | | `semantic_unsafe` | VLM 判定非 safe | 仲裁 | 锁存/暂停 + 人类裁决 | | 单帧噪声 | 迟滞未达标 | — | 不锁 | --- ## 5. 缺省 VLM 安全裁决 prompt(最小可用,可配置) > 由固定脚本注入 `{task_context}` / `{trigger_cause}` / `{joint_state}` 三个槽位。 ``` 你是机器人安全裁决员。请基于以下信息判断机器人当前是否处于危险状态,需要立即停机或人工干预: - 任务上下文:{task_context} - 触发原因:{trigger_cause} - 机器人当前位形(关节角/末端位姿):{joint_state} - 最新离屏渲染画面:<附带图像> 重点核查: 1. 是否即将发生或正在发生碰撞(与障碍物、人员或机器人自身); 2. 机械臂是否处于失稳、奇异或过伸等危险姿态; 3. 画面中是否出现任务规划未预期的变化(新障碍、人员进入、物体被移动等)。 只输出 JSON,不要任何其他文字: {"verdict": "safe" | "unsafe" | "uncertain", "reason": "一句话原因", "evidence": "画面中支撑判断的依据"} 规则:verdict 为 uncertain 时一律按 unsafe 处理。 ``` --- ## 6. 下游适配清单(本体适配 agent 按此交接) 1. **填写 profile `safety` 段**:话题名/消息类型、控制频率、阈值、watchdog 名单(critical 与 observed 分开)、hysteresis。 2. **实现 `lock_action`**:若本体支持零速保持可用缺省 `zero_velocity`;否则实现阻尼/柔顺接口(含 `activate`/`deactivate`)。 3. **力矩反馈**:有力矩 → 填 `torque` 段(或按 checker 接口实现本体 torque_checker);无力矩 → 保持 `torque_topic: ""`,框架自动禁用并明示。 4. **计算力矩前馈**(若已有辨识参数):接入 `feedforward_topic` 预留接口。 5. **阈值标定**:在故障注入测试 + 实机试验中校准,避免误锁/漏锁。 6. **后续扩展**(接口已留,不急):YOLO 等轻量模型触发(替换/前置 `plan_change` 等触发源)、非 ROS 急停硬件通路(`estop` 段,硬件就绪时实现)。 7. **注册流程**:`robot_register` 成功后自动拉起 `safety_monitor`(带 profile 参数)→ `robot_topology` 记录 → `robot_health_check` 聚合 safety 状态 → 所有 arm 工具查询锁。整条链闭合。 --- ## 7. 测试计划(故障注入) | 用例 | 注入 | 期望 | | --- | --- | --- | | 跟踪偏差 | 注入 |q_actual − q_cmd| 超限持续 N 帧 | 锁存 + 触发原因 `tracking_error` | | 堵转 | 有指令、实际速度≈0 | 锁存 + `stall` | | 反馈丢失 | 停发 joint_states 超 timeout | 锁存 + `feedback_loss` | | 力矩突变/持续超限 | 注入 torque 尖峰/恒高 | 锁存 + `torque_spike`/`torque_overload` | | 关键节点掉线 | kill critical node | 锁存 + `watchdog_critical` | | 监视器自杀 | kill safety_monitor | 心跳丢失 → fail-closed 锁 | | 非致命 | 停 observed node / 单帧噪声 | **不锁**,记录 + 通知 | | 锁存语义 | 条件消失后 | 仍 LOCKED,需人工解锁 | | 恢复流程 | 解锁 → 回 home → 恢复 | 回到 NORMAL | | 工具层 | LOCKED 时调用 arm 工具 | 拒绝下发,返回锁原因 | | VLM 仲裁 | golden prompt / verdict 映射 / uncertain | 非 safe → 人类裁决 | 复用现有 mock 机器人测试架构(vitest + ToolDeps fake)+ `ros2 launch` 集成测试。 --- ## 8. 0.14.0 交付范围(文档通过后实施) **实现(通用框架,本仓库)** - `safety/` ROS2 包(`dsh_ros2_safety`,构建到 vlm 工作区,与 `vlm/` 同模式): - `msg/SafetyState` / `msg/SafetyEvent` / `srv/GetSafetyState` / `srv/Unlock` / `srv/SetLock` - `scripts/safety_core.py`:纯逻辑(无 rclpy)——motion(tracking/stall + 迟滞)、feedback_loss、watchdog(critical/observed 分级)、torque(可选)、锁存状态机、取证环形缓冲;`--selftest` 跑 12 个故障注入场景 - `scripts/safety_monitor`:rclpy 节点封装(订阅关节/指令/力矩 + 慢速 watchdog 扫描 + `/safety/state`(transient-local) + `/safety/heartbeat` + `/safety/lock_active` + 三个服务) - `scripts/safety_vlm_arbitrate`:固定格式化 + 缺省 prompt(§5)+ 既有 `/vlm/describe` 网关 - `scripts/robot_profile.py` 扩展:register 写入 `safety` 段(URDF 限位自动派生)+ `safety show/set` + 校验 - 工具集成(+5 工具,共 50):`robot_safety_start`(L2,后台拉起监视器)/ `robot_safety_state`(L1)/ `robot_safety_arbitrate`(VLM 仲裁,非 safe 提示人工)/ `robot_safety_lock` / `robot_safety_unlock`(L2 人工门);`moveit_move` 执行前查锁(LOCKED 恒拒,`safetyStrict: 'reject'` 时监视器失联 fail-closed);`robot_register` 成功后自动拉起监视器 - 故障注入测试:vitest 工具层(+13 例,共 109)+ `safety_core --selftest`(12 场景)+ 实机 ROS2 链路验证(发布→NORMAL→停发→LOCKED→解锁→恢复) > 注:`robot_health_check` 聚合属于 harness/下游侧(本仓库不含该工具),下游 agent 可读 `/safety/state` 接入。 **仅接口/注释,不实现** - torque_checker 逻辑(随机器人)、damping/柔顺 lock_action、计算力矩前馈、YOLO 触发、非 ROS 急停 **版本**:0.14.0(含 CHANGELOG、README 安全章节、双语文档同步)