chore: 框架科学优化前的完整基线

在开始'设备契约层 + 目录重构 + 通止规装配'改造之前,先钉住当前可运行状态,
使后续任何删除/重构都可回退。

本基线包含:
- 现有 ROS 2 工作区全部源码(arm_application / arm_control / arm_bringup 等)
- 官方 flexiv_ros2 栈(含 flexiv_hardware / flexiv_controllers 等符号链接)
- 设备契约层 src/arm_interfaces(新增,46 项单测通过)
- devices/ 设备选型与品牌清单(新增)
- scripts/gen_site_example.py 与由它生成的 config/site.example.yaml

按 .gitignore 排除:build/ / install/ / log/ / config/site.yaml / 模型权重二进制。
This commit is contained in:
arm_dev
2026-09-23 10:53:06 +08:00
commit 6edb8d39ab
635 changed files with 1165230 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
# devices/ —— 设备与外部文件总目录
这个目录存在的唯一目的:**把「换个硬件」从改代码变成改配置**。
## 设计原则(务必先读)
### 1. 选型与数值分离
| 关注点 | 放在哪 | 谁改 |
|--------|--------|------|
| **用哪个品牌的哪个驱动** | `devices/registry.yaml` | 集成工程师(很少改) |
| **设备号 / 速度 / 阈值 / 悬高** | `config/site.yaml` | 现场操作人员(经常改) |
**`devices/` 下不允许出现任何现场数值。** 数值只有 `config/site.yaml` 一个来源。
这样可以避免现在这种「同一个值三处定义、改了不生效」的问题。
### 2. 目录约定
```
devices/
├── registry.yaml # 选型开关(唯一入口)
├── robots/ # 机械臂:RDK + 适配器
├── grippers/ # 夹爪
├── sensors/
│ └── orisys/ # ★ 视触觉核心,长期迭代,单独占一个大文件夹
├── cameras/ # 相机
└── sim/ # 仿真平台(当前 Gazebo)
```
每个品牌一个子目录,子目录内固定结构:
```
<类别>/<品牌>/
├── manifest.yaml # 我是谁 / 支持什么能力 / 需要哪些配置 / 外部文件放哪
├── driver/ # 适配器(把厂商 API 翻译成框架接口)
└── <厂商物料>/ # 如 rdk/、sdk/,原样放置,不要改名
```
### 3. 一个新品牌要接进来,只需要四步
1. **丢物料**:把厂商的 RDK / SDK / 驱动包放进 `<类别>/<品牌>/` 下对应文件夹
(格式和命名要求写在该类别的 `README.md` 里,**必须按它要求放**)。
2. **填 manifest**:复制同类别的 `manifest.yaml` 改四个字段(id / label / driver.package / config_schema)。
3. **写适配器**:在 `driver/` 下实现框架的接口契约(见 `src/arm_interfaces/`)。
这一步的工作量取决于厂商 —— 如果厂商本来就提供符合契约的 ROS 2 驱动,这一步几乎为零。
4. **改一行选型**:把 `registry.yaml` 里对应类别的值改成新品牌 id。
> **诚实说明**:第 3 步无法完全避免。Flexiv 的 RDK 是 C++/Python 库而不是 ROS 2 驱动,
> 所以它外面永远需要一层薄适配器。框架能做的是把这层适配器写到最薄(只做 API 翻译,
> 不含任何业务逻辑),而不是让你零代码接入。
### 4. 启动时的校验
框架启动会依次读 `registry.yaml` → 各 `manifest.yaml` → `config/site.yaml`,
校验三件事:
- 选中的品牌目录存在,且 `manifest.yaml` 可解析;
- manifest 里 `assets` 声明的必需物料文件都在;
- `config_schema` 里标记 `required: true` 的配置键,在 `site.yaml` 里都有值。
任何一项不满足**立即报错并明确指出该去哪个文件补什么**,不允许静默降级。
这是替代现在「`run_handeye.sh` 传了 5 个节点没声明的参数、调参静默失效」那类问题的机制。
+51
View File
@@ -0,0 +1,51 @@
# devices/cameras/ —— 相机
## 厂商物料放置要求
| 项 | 要求 |
|----|------|
| 格式 | 厂商 SDK / 驱动包原始格式。RealSense 用 `pyrealsense2` 或官方 ROS 2 包 |
| 命名 | 保持原样,不要改名 |
| 序列号 | **不要写在这里**。序列号在 `config/site.yaml` 的 `cameras.<名字>.serial` |
| 生效 | 放好物料 → 改 `registry.yaml` 选型 → 改 `site.yaml` 的 serial / 分辨率 / 曝光 → 重启对应相机脚本 |
## 目录结构
```
cameras/
├── README.md
├── realsense/ # Intel RealSense(当前在用)
│ ├── manifest.yaml
│ └── driver/
└── <新品牌>/
```
## 相机角色与命名(重要)
框架里每台相机必须有一个**角色**,因为不同角色参与完全不同的流程:
| 角色 | 当前相机 | 参与流程 | 是否参与手眼标定 |
|------|----------|----------|------------------|
| `wrist_eye_in_hand` | D405(左腕 / 右腕各一台) | 视觉跟踪 k0、拍照推理 k1/k2 | 是(眼在手上) |
| `overhead_eye_to_hand` | D435(双臂中间) | 粗定位兜底(S03) | 是(眼在手外) |
**注意**:D435 当前**还没有做手眼标定**,所以 S03 粗定位链路暂时不可用。
但这不影响 S04 跟踪链路,两条链路用的是同一套权重、同一个跟踪目标 k0。
## 已知的坑(迁移时必须保留的处理)
1. **USB 独占**:RealSense 同时只能被一个进程打开。启动前必须 `pkill` 掉旧进程,
现有脚本里那句 `sleep 2` 是必要的,不要删。
2. **USB2 vs USB3 会自动降分辨率**:现有脚本检测到 USB2 会降到 `640x480@15` 并告警,
这个降级逻辑要保留,否则会在弱链路上直接起不来。
3. **`run_camera_d435.sh` 曾把分辨率/曝光/emitter 硬编码**,与 `site.yaml` 的
`cameras.d435` 字段脱节。迁移后必须全部从 `site.yaml` 读,不允许硬编码。
## 当前状态(迁移参考)
| 现有位置 | 内容 | 迁移去向 |
|----------|------|----------|
| `src/arm_application/arm_application/realsense_color_pub.py` | 采集 + 发布彩图/对齐深度/红外 | `driver/` |
| `src/arm_application/arm_application/realsense_view.py` | 独立预览进程(与采集分离) | `driver/` 或 tools/ |
| `src/arm_application/arm_application/realsense_sdk_view.py` | Qt 四宫格窗口(被上面两者共用) | 保留 |
| `src/arm_application/arm_application/handeye_realsense.py` | 内参查询、话题解析、曝光设置 | 拆开:相机相关的进 `driver/`,标定相关的进 `handeye/` |
+141
View File
@@ -0,0 +1,141 @@
# =============================================================================
# devices/cameras/realsense/manifest.yaml
#
# Intel RealSense 相机驱动清单。覆盖两种型号:
# D405 —— 腕部相机,眼在手上,参与跟踪与手眼标定
# D435 —— 手外相机,顶视,参与粗定位兜底与手眼标定
#
# 这里【不放现场数值】。序列号 / 分辨率 / 曝光一律在 config/site.yaml。
# =============================================================================
id: realsense
label: Intel RealSense D405 / D435
kind: camera
vendor: intel
version: "pyrealsense2"
# 从哪里读出「当前用哪个型号」。值是 config_schema 里的 field key。
model_selector: model
description: >
RealSense 深度相机。D405 装在法兰/夹爪上做腕部视觉(眼在手上),
D435 架在双臂中间做顶视(眼在手外)。USB 独占,同一时刻只能一个进程打开。
# --- 厂商物料放置要求 ------------------------------------------------------
assets:
- path: driver
required: false
what: 厂商驱动(若使用 pyrealsense2 则无需放置,由 conda 环境提供)
format: 厂商原始格式
naming: 保持原样
where_used: driver 通过 pyrealsense2 或官方 ROS 2 包访问相机
# --- 适配器 ----------------------------------------------------------------
driver:
package: arm_driver_realsense
executable: realsense_publisher
build: colcon
# --- 能力声明 --------------------------------------------------------------
capabilities:
- color
- depth
- aligned_depth_to_color
- infrared_left
- infrared_right
- query_intrinsics # 手眼标定必需
- set_exposure
# --- 型号与角色 ------------------------------------------------------------
models:
- id: d405
label: D405 腕部相机
roles: [wrist_eye_in_hand]
default_resolution: "848x480"
default_fps: 30
notes: >
腕部使用,必须关闭红外投射器(emitter_enabled: false),
否则投射的红外点阵会污染跟踪图像。
- id: d435
label: D435 手外顶视相机
roles: [overhead_eye_to_hand]
default_resolution: "1280x720"
default_fps: 30
notes: >
手外使用,通常需要开启红外投射器以提升无纹理表面的深度质量。
当前尚未完成手眼标定。
# --- 配置项声明 ------------------------------------------------------------
config_schema:
- key: model
site_path: cameras[].model
type: str
required: true
doc: >
相机型号。必须与上面 models 列表里的某个 id 一致(d405 或 d435)。
型号决定默认分辨率与是否应开启红外投射器。
- key: serial
site_path: cameras[].serial
type: str
required: true
doc: >
相机序列号。用 scripts/list_realsense.sh 查看当前接入的全部序列号并映射角色。
每个角色必须是不同的序列号,否则两台相机抢同一台设备。
- key: role
site_path: cameras[].role
type: str
required: true
doc: >
相机角色,合法值: left_wrist | right_wrist | overhead_eye_to_hand。
以 _wrist 结尾表示装在法兰上(眼在手上,手眼标定用 eye_in_hand);
overhead_eye_to_hand 表示固定安装(眼在手外,手眼标定用 eye_to_hand)。
角色决定该相机参与哪条流程,所以必须与物理安装位置一致。
- key: width
site_path: cameras[].width
type: int
unit: px
required: true
doc: 彩色流宽度。注意 USB2 链路会自动降到 640 并告警。
- key: height
site_path: cameras[].height
type: int
unit: px
required: true
doc: 彩色流高度。注意 USB2 链路会自动降到 480 并告警。
- key: fps
site_path: cameras[].fps
type: int
unit: Hz
required: false
default: 30
doc: 帧率。
- key: auto_exposure
site_path: cameras[].auto_exposure
type: bool
required: false
default: true
doc: 自动曝光。
- key: ae_limit_us
site_path: cameras[].ae_limit_us
type: float
unit: us
required: false
doc: >
曝光上限。腕部 D405 建议限到 4000us 以内 —— 跟踪时曝光过长会导致运动拖影。
0 表示不封顶。
- key: emitter_enabled
site_path: cameras[].emitter_enabled
type: bool
required: true
doc: >
红外投射器。腕部必须 false(投射点阵污染跟踪图像),
手外通常 true(提升无纹理表面深度质量)。
+52
View File
@@ -0,0 +1,52 @@
# devices/grippers/ —— 夹爪
## 厂商物料放置要求
| 项 | 要求 |
|----|------|
| 格式 | 厂商原始发布的驱动/SDK/协议文档,不要转换 |
| 命名 | 保持原文件名。若厂商只提供 PDF 协议手册,把它放进来即可(适配器按手册实现) |
| 通讯参数 | **不要写在这里**。串口、波特率、从站号、行程全部在 `config/site.yaml` |
| 生效 | 放好物料 → 改 `devices/registry.yaml` 选型 → 改 `site.yaml` 里的串口与行程 → 重启 `scripts/run_gripper.sh` |
## 目录结构
```
grippers/
├── README.md
├── dh_pgea/ # 大寰(当前在用)
│ ├── manifest.yaml
│ ├── protocol/ # 厂商协议手册 / 寄存器表
│ └── driver/
└── <新品牌>/
```
## 适配器必须实现的接口契约
夹爪的接口刻意做成和机械臂对称 —— 上层只调接口,不关心是哪个品牌、走 Modbus 还是走 RDK。
| 能力 | 用途 |
|------|------|
| `open` | 张开 |
| `close` | 夹紧(可带力/速度参数) |
| `move` | 移动到指定开度 |
| `rotate` | 旋转到指定角度(旋转爪) |
| `get_status` | 读开度、力、是否夹持到位 |
**关键设计:`rotate` 与 `open/close` 解耦。** 通止规装配的 S08 循环是
「转 180° → 松爪 → 快回转 → 夹紧」四步反复,这四步必须是四个独立可调用的原语,
不能封装成一个「拧螺丝」复合动作 —— 否则中途掉件时无法精确恢复。
## 当前状态(迁移参考)
大寰现有实现有两套互不复用的 Modbus 栈,P1 之后会合并到 `dh_pgea/driver/` 下:
| 文件 | 内容 | 处置 |
|------|------|------|
| `src/arm_application/arm_application/dh_pgea.py` | PGEA Modbus 客户端 + 自带 CRC 表 + CLI | 保留,收敛为驱动 |
| `src/arm_application/arm_application/dh_official.py` | 另一份寄存器级客户端(含 RGI 旋转寄存器) | 合并进来,消除重复 |
| `src/arm_application/arm_application/dual_gripper.py` | PGEA/RGI 统一抽象 | 改为调用新驱动接口 |
| `src/arm_application/arm_application/dh_pgea_node.py` | ROS 节点封装 | 改为通用夹爪节点的品牌无关实现 |
**注意**:`site.yaml` 里 `gripper.backend: rdk` 这条路径已被 `run_gripper.sh` 明确拒绝,
`scripts/rdk_gripper.py` 是孤儿脚本。这条「走控制器 RS485」的分支在 P2 会被清理。
+135
View File
@@ -0,0 +1,135 @@
# =============================================================================
# devices/grippers/dh_pgea/manifest.yaml
#
# 大寰(DH)夹爪驱动清单。覆盖两种型号:
# PGEA —— 平行夹爪(左臂 89 使用)
# RGI —— 旋转夹爪(右臂 91 使用)
#
# 这里【不放现场数值】。串口 / 波特率 / 从站号 / 行程一律在 config/site.yaml。
# =============================================================================
id: dh_pgea
label: 大寰 PGEA 平行爪 / RGI 旋转爪
kind: gripper
vendor: dh
version: "Modbus RTU(工控机 USB-RS485 直连)"
# 从哪里读出「当前用哪个型号」。值是 config_schema 里的 field key。
# RGI 型号会在此基础上叠加 rotate 能力。
model_selector: model
description: >
大寰夹爪。PGEA 提供开合,RGI 在开合之外提供绕轴旋转。
通讯走工控机 USB 转 RS485,不经过机械臂控制器。
# --- 厂商物料放置要求 ------------------------------------------------------
assets:
- path: protocol
required: false
what: 大寰 Modbus 协议手册与寄存器表
format: 厂商原始 PDF / 文档
naming: 保持原文件名
where_used: 适配器实现寄存器读写时的依据
notes: >
寄存器表已固化进驱动代码(见 driver/)。此目录只作为排查时的对照资料,
缺失不影响运行。
# --- 适配器 ----------------------------------------------------------------
driver:
package: arm_driver_dh_gripper
executable: dh_gripper_driver
build: colcon
# --- 能力声明 --------------------------------------------------------------
capabilities:
- open
- close
- move
- get_status
# RGI 型号额外支持旋转。框架按 manifest 的型号分支决定是否暴露 rotate。
models:
- id: PGEA-100-20-W
label: PGEA 平行爪(左臂)
capabilities: [open, close, move, get_status]
stroke_mm: 20.0
- id: RGI-100-30-O-S-M1
label: RGI 旋转爪(右臂)
capabilities: [open, close, move, rotate, get_status]
stroke_mm: 30.0
notes: >
rotate 是通止规装配 S08 循环的核心原语,必须可独立调用且可随时中止。
单次转角默认 180°(配置项 system.screw_step_deg),因为不确定基于工具系
能转多少度是极限。
# --- 配置项声明 ------------------------------------------------------------
config_schema:
- key: model
site_path: robot.robots[].gripper.model
type: str
required: true
doc: >
当前使用的夹爪型号。必须与上面 models 列表里的某个 id 一致。
型号决定是否具备 rotate 能力(PGEA 没有,RGI 有)。
- key: port
site_path: robot.robots[].gripper.port
type: str
required: true
doc: 串口设备路径(如 /dev/ttyUSB0)。建议用 /dev/serial/by-id/ 下的稳定路径,
因为 ttyUSB 编号会随插拔漂移。
- key: baud
site_path: gripper.baud
type: int
required: false
default: 115200
doc: 波特率。
- key: slave_id
site_path: gripper.slave_id
type: int
required: false
default: 1
doc: Modbus 从站号。
- key: stroke_mm
site_path: robot.robots[].gripper.stroke_mm
type: float
unit: mm
required: true
doc: 该爪的总行程。用于把 0–1000‰ 开度换算成毫米。
- key: force_pct
site_path: gripper.force_pct
type: int
unit: '%'
required: false
default: 20
doc: 夹紧力百分比。低于 20 会被硬件钳位到 20。
- key: speed_pct
site_path: gripper.speed_pct
type: int
unit: '%'
required: false
default: 50
doc: 动作速度百分比。
- key: open_position
site_path: gripper.open_position
type: int
unit: 千分比
required: false
default: 1000
doc: 「张开」对应的开度。1000 = 全开。
- key: close_position
site_path: gripper.close_position
type: int
unit: 千分比
required: false
default: 0
doc: 「夹紧」对应的开度。0 = 全合。
注意:通止规是异形件,全合可能压不到,实际夹紧位需要按件标定后填这里。
@@ -0,0 +1,31 @@
# devices/grippers/dh_pgea/protocol/ —— 大寰 Modbus 协议资料
## 放什么
大寰(DH)夹爪的 Modbus RTU 协议手册、寄存器表、型号规格书。
## 格式与命名要求
| 项 | 要求 |
|----|------|
| 格式 | 厂商原始 PDF / 文档,不要转换 |
| 命名 | 保持原文件名,或改为 `<型号>_<文档类型>.pdf`,如 `PGEA-100-20-W_寄存器表.pdf` |
| 是否必需 | **不必需**。寄存器表已固化进驱动代码,此目录仅供人工排查时对照 |
## 为什么放这里
驱动里硬编码了寄存器地址与 CRC 表。当现场出现「某个动作没反应」时,
需要拿手册逐条核对寄存器定义与从站号,所以把手册放在驱动旁边最方便。
## 已知的两处不一致(迁移时要处理)
1. 现在代码里有**两套独立的 Modbus 实现**,各自定义了一份寄存器常量:
- `src/arm_application/arm_application/dh_pgea.py`(PGEA,自带 CRC 表)
- `src/arm_application/arm_application/dh_official.py`(含 RGI 旋转寄存器)
两份功能重叠,P1 之后合并成一份。
2. `site.yaml` 里 `gripper.backend: rdk` 这条「走机械臂控制器 RS485」的分支
已被 `run_gripper.sh` 明确拒绝,对应的 `scripts/rdk_gripper.py` 是孤儿脚本。
如果你其实需要走控制器 RS485(而不是工控机 USB),请明确告知 ——
这会影响驱动是挂在 `devices/grippers/` 还是 `devices/robots/flexiv/` 下。
+30
View File
@@ -0,0 +1,30 @@
# =============================================================================
# devices/registry.yaml —— 设备选型开关
#
# 这个文件只回答一个问题:每一类设备当前由哪个驱动实现来接管。
# 这里【不放任何现场数值】。数值一律在 config/site.yaml。
#
# 改动任何一个值之后:重启对应的启动脚本即生效,不需要重新编译。
# =============================================================================
version: 1
# 机械臂驱动实现。可选值 = devices/robots/ 下的目录名
# flexiv -> devices/robots/flexiv/ (非夕 Rizon / AICO)
robots: flexiv
# 夹爪驱动实现。可选值 = devices/grippers/ 下的目录名
# dh_pgea -> devices/grippers/dh_pgea/ (大寰 PGEA 平行爪 / RGI 旋转爪)
grippers: dh_pgea
# 传感器驱动实现。可选值 = devices/sensors/ 下的目录名
# orisys -> devices/sensors/orisys/ (视触觉:3 切向力 + 1 法向力 + 深度图 + 力矢量图)
sensors: orisys
# 相机驱动实现。可选值 = devices/cameras/ 下的目录名
# realsense -> devices/cameras/realsense/ (D405 腕部 / D435 手外)
cameras: realsense
# 仿真平台。可选值 = devices/sim/ 下的目录名
# gazebo -> devices/sim/gazebo/
sim: gazebo
+61
View File
@@ -0,0 +1,61 @@
# devices/robots/ —— 机械臂
## 厂商物料放置要求
每个品牌一个子目录,物料放在该子目录下的 `rdk/`(或厂商自定的物料目录,须在 `manifest.yaml`
的 `assets` 里声明)。
**通用要求:**
| 项 | 要求 |
|----|------|
| 格式 | 厂商**原始发布格式**,不要自行转换、不要重新打包成 zip/tar 后不展开 |
| 命名 | **保持厂商原目录名和原文件名,一律不要改名**。改名会导致 RDK 内部的 include 路径、CMake 查找路径、Python 包名全部失效 |
| 版本 | 在 `manifest.yaml` 的 `version` 字段里记录版本号,方便日后排查 |
| 编译产物 | 不要把编译产物(`.a` `.so` `build/`)提交进 git;离线库放 `vendor/` |
| 生效 | 放好物料 → 编译一次 → 改 `devices/registry.yaml` 选型 → 重启 `scripts/run_real.sh` |
## 目录结构
```
robots/
├── README.md
├── flexiv/ # 非夕(当前在用)
│ ├── manifest.yaml
│ ├── rdk/ # 厂商 RDK 源码或安装包,原样放置
│ └── driver/ # 适配器:把 RDK API 翻译成框架接口
└── <新品牌>/
├── manifest.yaml
├── rdk/ 或 sdk/
└── driver/
```
## 适配器必须实现的接口契约
框架上层(伺服、跟踪、装配状态机、手眼标定)**只调用下面这组接口,不认识任何厂商 SDK**。
契约定义在 `src/arm_interfaces/`,每个方法对应的 ROS 2 话题/服务/action 也在那里声明。
| 能力 | 用途 | 谁在用 |
|------|------|--------|
| `get_arm_state` | 读关节角 + 末端位姿 | 手眼标定、状态记录 |
| `follow_joint_trajectory` | 关节轨迹(点到点、多路点) | 手眼标定走点、装配移动 |
| `servo_joint` | 高频关节伺服 | 视觉伺服 `servo_node` |
| `servo_cartesian` | 笛卡尔位姿伺服 | 跟踪、悬停 |
| `compliant_descent` | 柔顺下臂(接触力约束) | 装配接触阶段 |
| `set_tool` / `switch_tool` | 设置/切换工具坐标系(如 tool1) | 全局工具系锁定 |
| `home` | 回到初始位姿 | 每个循环开始、异常复位 |
| `enable` / `clear_fault` | 使能、清故障 | 启动自检、异常恢复 |
**缺哪个能力就在 manifest 的 `capabilities` 里不写哪个**,框架启动时会检查上层要用的能力是否都被支持,
不支持就明确报错,而不是运行时莫名其妙失败。
## 当前状态(迁移参考)
非夕臂的现有实现分散在这些地方,P1 之后会收敛到 `flexiv/driver/` 下:
- 硬件接口:`src/flexiv_hardware/`(ros2_control 插件,上游官方)
- bringup:`src/arm_bringup/stack.launch.py` 的 `backend:=real` 分支
- 轨迹适配:`src/arm_control/arm_control/real_trajectory_adapter.py`
- 几何 IK 与手眼加载:`src/arm_application/arm_application/geom_ik.py`
- 软拖拽:`scripts/rdk_drag_bridge.py`
- 工具系切换:`scripts/rdk_switch_tool.py`
+116
View File
@@ -0,0 +1,116 @@
# =============================================================================
# devices/robots/flexiv/manifest.yaml
#
# 非夕机械臂(Flexiv Rizon / AICO)驱动清单。
# 框架启动时读这个文件,决定加载哪个适配器、校验哪些配置、检查哪些外部物料。
#
# 这里【不放现场数值】。SN / IP / 速度 / 悬高一律在 config/site.yaml。
# =============================================================================
id: flexiv
label: 非夕 Flexiv Rizon / AICO
kind: robot
vendor: flexiv
version: "RDK 1.9.x(与 flexiv_ros2 humble-v1 v1.9.1 配套)"
description: >
非夕七轴力控机械臂。当前项目使用 Rizon4 双臂(SN 尾号 89 / 91)。
上层通过 ros2_control + rizon_arm_controller 下发关节轨迹,柔顺下臂走 RDK 力控原语。
# --- 厂商物料放置要求 ------------------------------------------------------
# 每个条目会在框架启动时被检查;required: true 且文件缺失时直接报错。
assets:
- path: rdk
required: true
what: Flexiv RDK 源码或安装包
format: 厂商原始发布格式,不要转换、不要重新打包
naming: 保持厂商原目录名与文件名,一律不要改名
where_used: driver 通过此路径加载 RDK
notes: >
与 flexiv_ros2 humble-v1 配套的是 RDK 1.9.x。仓库外的已编译安装位于
~/flexiv_install(由 scripts/env.sh 与 run_real.sh 注入 LD_LIBRARY_PATH)。
离线安装库在 vendor/rdk_libs/。
# --- 适配器 ----------------------------------------------------------------
driver:
package: arm_driver_flexiv
executable: flexiv_arm_driver
build: colcon
# --- 能力声明 --------------------------------------------------------------
# 框架上层只会调用这里列出的能力。少写一项,上层用到时会启动即报错。
capabilities:
- get_arm_state
- follow_joint_trajectory
- set_named_pose
- servo_joint
- servo_cartesian
- compliant_descent
- set_tool
- switch_tool
- home
- enable
- clear_fault
- soft_drag # 软拖拽,手眼标定走点用
# --- 配置项声明 ------------------------------------------------------------
# 框架启动时按 site_path 去 config/site.yaml 取值,required 的键缺失即报错。
# unit 会打印在人读的错误信息里,避免「填了毫米但代码按米」这类问题。
config_schema:
- key: sn
site_path: robot.robots[].sn
type: str
required: true
doc: 机械臂完整 SN(如 Rizon4-063989)。必须与示教器上显示的一致。
- key: ip
site_path: robot.robots[].ip
type: str
required: true
doc: 控制柜 IP。同一网段的工控机地址填 pc_ip。
- key: pc_ip
site_path: robot.robots[].pc_ip
type: str
required: true
doc: 工控机在本网段的地址,RDK 白名单用。
- key: iface
site_path: robot.robots[].iface
type: str
required: true
doc: 连接控制柜的网卡名(如 enp4s0)。
- key: tool_name
site_path: robot.tool_name
type: str
required: false
default: ""
doc: 示教器 Studio 里的工具坐标系名。留空表示不切换。
本项目要求 tool1 全程保持垂直向下且位姿锁定,需在此指定。
- key: tool_z_extend_m
site_path: robot.tool_z_extend_m
type: float
unit: m
required: false
default: 0.0
doc: >
工具坐标系沿 Z 的延长量。用于补偿「夹具 + 夹持物」超出原质心的距离。
当前 tool1 质心在未装夹具的平行夹爪末端,装上夹具并夹持通止规后需沿 Z 延长。
标定方法见 docs/TOOL_FRAME.md。
- key: ee_z_m
site_path: robot.robots[].servo.ee_z_m
type: float
unit: m
required: true
doc: 视觉跟踪锁高 —— 法兰相对基座系的 Z。
注意与悬高(相对工具系)区分,两者是不同概念。
- key: safe_z_m
site_path: robot.robots[].servo.safe_z_m
type: float
unit: m
required: true
doc: 防撞安全高度。启动归位时先抬到这个高度再水平移动。
+53
View File
@@ -0,0 +1,53 @@
# devices/robots/flexiv/rdk/ —— 放置非夕 RDK
## 放什么
Flexiv RDK(Robotic Development Kit)的源码或安装包。
## 格式与命名要求(必须遵守)
| 项 | 要求 | 违反的后果 |
|----|------|-----------|
| 格式 | 厂商原始发布格式(`.tar.gz` 需展开,不要留着压缩包) | 驱动加载不到 |
| 目录名 | 保持厂商原目录名(通常是 `flexiv_rdk`) | CMake 的 include 路径失效 |
| 文件名 | 一律不改名 | CMake 查找 `libflexiv_rdk.*.a` 时按原名匹配 |
| 版本 | 在 `../manifest.yaml` 的 `version` 字段记录 | 日后无法排查版本不匹配 |
| 编译产物 | 不要把 `build/` 提交进 git | 仓库膨胀 |
## 版本必须配对(这是已踩过的坑)
项目里同时存在两个 RDK 版本,**不能混用**:
| 用途 | 需要的 RDK 版本 | 位置 |
|------|----------------|------|
| 编译 `flexiv_ros2`(humble-v1,v1.9.1) | **1.9.x** | 已编译安装于 `~/flexiv_install` |
| 单独跑 RDK Python 脚本(软拖拽、切工具) | 2.1.x | `src/flexiv_rdk`(符号链接到仓库外) |
`flexiv_ros2` 是 humble-v1 版本,它期望的 RDK 是 1.9.x。当前 `src/flexiv_rdk` 指向的是
**2.1.x**,两者靠 `~/flexiv_install` 里已编译好的 1.9.1 解耦。
离线安装 1.9.1 需要的静态库在 `vendor/rdk_libs/`,由
`scripts/install_rdk_offline.sh` 校验 sha256 后使用。
## 放好之后怎么让它生效
```bash
# 1. 若 RDK 未编译,先编译并安装到 ~/flexiv_install
bash scripts/build_flexiv_rdk.sh
# 2. 重新编译依赖 RDK 的 ROS 包
source scripts/env.sh
./scripts/build_all.sh
# 3. 确认选型
cat devices/registry.yaml # robots: flexiv
# 4. 实机启动
bash scripts/run_real.sh --arm 89
```
## 排查
- `run_real.sh` 会先调 `scripts/rdk_switch_tool.py` 做预检;失败会直接 `exit 1` 并打印原因。
- 网络不通先跑 `scripts/check_robot_network.sh`。
- RDK Python 冒烟测试:`scripts/test_rdk_python.sh <SN>`。
+80
View File
@@ -0,0 +1,80 @@
# devices/sensors/orisys/ —— Orisys 视触觉(框架核心)
**这是整个框架的核心。** 后续所有 ROS 2 项目都会围绕它开发,它也会持续迭代优化更新,
所以它单独占一个文件夹,不与其它传感器混放。
## 先搞清楚它输出什么
这是上层算法真正依赖的数据契约。**注意单位与坐标系的定义,这是最容易出错的地方。**
| 数据 | 维度 | 说明 | 框架内部话题 |
|------|------|------|--------------|
| 切向力 1 | 标量 | 任意单位(非牛顿),且**基线会漂移** | `~/tangential_1` |
| 切向力 2 | 标量 | 同上,另一正交方向 | `~/tangential_2` |
| 法向力 | 标量 | 同上 | `~/normal` |
| 深度图 | 图像 | 接触面深度 | `~/depth` |
| 力矢量图 | 图像 | 接触点的力矢量可视化 | `~/force_vector` |
### 两个必须记住的坑
**1. 力是任意单位,不是牛顿。** 现有 `site.yaml` 里所有力阈值(`touch_delta`、`press_delta`、
`safe_press_delta` 等)全部是「相对静止均值的**增量**」,不是绝对值。节点启动后会先自动测基线。
改阈值时改的也是增量,不是绝对读数。
**2. 基线会漂移,所以阈值必须相对化。** 写任何新的力判据时,不允许直接比较绝对读数。
**3. 切向力的 X / Y 通道归属尚未确认。** 通止规装配里「接触到面时某一路切向力突变」「拧紧判定时
某一路切向力超阈值」都需要知道是哪个通道。在确认之前,框架按**两个切向通道都监控、任一满足即判定**
来实现,避免猜错通道导致整段流程静默失效。确认后会把配置项收敛为指定通道。
## 厂商物料放置要求
| 项 | 要求 |
|----|------|
| SDK 格式 | 厂商**原始发布格式**,不要转换、不要重新打包 |
| SDK 命名 | **保持厂商原目录名与文件名**,一律不要改名。SDK 内部常按自己包名做导入与资源查找 |
| SDK 版本 | 在 `manifest.yaml` 的 `version` 记录,并写进 `docs/CHANGELOG_ORISYS.md` |
| 型号配置 | 每个型号一份配置放 `models/<型号名>/`,不要混在一起 |
| 相机路径 | **必须用 `/dev/v4l/by-path/` 稳定路径**,不要写 `/dev/video0` —— video 编号会随插拔漂移,这是现有框架已经踩过的坑 |
| 生效 | 放好 SDK → `pip install -e` 或加进 `PYTHONPATH` → 改 `registry.yaml` 选 `orisys` → 改 `site.yaml` 的 `tactile.models.<型号>` → 重启 `scripts/run_tactile.sh` |
## 目录结构
```
sensors/orisys/
├── README.md # 本文件
├── manifest.yaml # 型号清单 + 能力 + 配置项声明
├── sdk/ # ★ 厂商 SDK 原样放置
│ └── README.md # 放进来之后怎么让它生效(见下)
├── models/ # ★ 多个传感器型号
│ ├── README.md
│ └── <型号名>/ # 每个型号一份配置
│ └── config.json
└── driver/ # 适配器:统一成上面的数据契约
```
## 多型号怎么切换
传感器有多个型号,切换只改两处,**不改任何代码**:
1. `devices/registry.yaml` → `sensors: orisys`(选厂商,通常不用改)
2. `config/site.yaml` → `tactile.model: "<型号名>"`,指向 `models/<型号名>/config.json`
型号之间的差异(通道数、内参、标定参数、拼接参数)全部由 `models/<型号名>/` 承载。
上层只订阅统一话题,换型号不影响上层代码。
## 当前状态(迁移参考)
| 现有位置 | 内容 | 迁移去向 |
|----------|------|----------|
| `src/arm_application/arm_application/orisys_tactile.py` | SDK 适配 + 多进程 worker + `load_orisys_from_site` | `driver/` |
| `src/arm_application/arm_application/tactile_node.py` | ROS 节点,双指各一个进程 | `driver/` |
| `src/arm_application/arm_application/tactile_cli.py` | 读数量级 CLI(调试阈值用) | 保留为调试工具 |
| `src/arm_application/arm_application/tactile_interface.py` | 数据类 | `driver/`,其中的 `MockTactile` 类已死,P2 删除 |
| `src/arm_application/config/orisys/dd03.json` | 当前型号 dd03 的厂商配置 | `models/dd03/config.json` |
**已解决的历史坑,迁移时必须保留这些做法:**
- 左右指各起一个独立进程,**不要合并成一个进程双路并行** —— 会抢 CUDA 上下文互相拖慢。
- `tactile.cuda` 保持 `false`,`infer_backend` 用 `cpu`。
- 退出时残留的孤儿 worker 会占死 `/dev/video`,`run_tactile.sh` 里那段 `pkill` 清理必须保留。
+162
View File
@@ -0,0 +1,162 @@
# =============================================================================
# devices/sensors/orisys/manifest.yaml
#
# Orisys 视触觉传感器驱动清单(框架核心)。
#
# 这里【不放现场数值】。video 路径 / 型号 / 发布率一律在 config/site.yaml。
# =============================================================================
id: orisys
label: Orisys 视触觉传感器
kind: sensor
vendor: orisys
version: "待填 —— 放入 SDK 后在此记录实际版本号"
# 从哪里读出「当前用哪个型号」。值是 config_schema 里的 field key。
# 框架据此叠加该型号的额外能力(型号能力与顶层能力取并集)。
model_selector: model
description: >
视触觉传感器,输出两路切向力 + 一路法向力,以及深度图与力矢量图。
采用双指方案,左右指各一个独立进程(防止抢 CUDA 上下文)。
是本框架的长期核心,后续所有项目围绕它开发。
# 重要:上层算法依赖的数据契约
outputs:
- topic: tangential_1
type: scalar
unit: 任意单位(非牛顿)
notes: 基线会漂移,所有阈值必须按「相对静止均值的增量」使用
- topic: tangential_2
type: scalar
unit: 任意单位(非牛顿)
notes: 与 tangential_1 正交。通止规装配需要确认哪一路对应哪个方向,见 README
- topic: normal
type: scalar
unit: 任意单位(非牛顿)
notes: 法向力。拧紧判定需要「法向力突增」这一条件
- topic: depth
type: image
unit: null
notes: 接触面深度图
- topic: force_vector
type: image
unit: null
notes: 接触点力矢量可视化
# --- 厂商物料放置要求 ------------------------------------------------------
assets:
- path: sdk
required: true
what: Orisys 厂商 SDK(Python 包或原生库)
format: 厂商原始发布格式,不要转换、不要重新打包
naming: 保持厂商原目录名与文件名,一律不要改名
where_used: driver 通过 PYTHONPATH 或 pip install -e 加载
notes: >
SDK 内部常按自己的包名做导入与资源查找,改名会直接导致 ImportError。
- path: models
required: true
what: 各传感器型号的配置
format: 每个型号一个子目录,内含 config.json
naming: 子目录名即型号名,会被 site.yaml 的 tactile.model 引用
where_used: driver 按 site.yaml 选中的型号加载对应配置
# --- 适配器 ----------------------------------------------------------------
driver:
package: arm_driver_orisys
executable: orisys_tactile_driver
build: colcon
notes: >
必须保持「左右指各一个独立进程」的架构,不要合并。
必须保留退出时清理孤儿 worker 的逻辑(否则占死 /dev/video)。
# --- 能力声明 --------------------------------------------------------------
capabilities:
- read_tangential
- read_normal
- read_depth
- read_force_vector
- measure_baseline
# --- 配置项声明 ------------------------------------------------------------
config_schema:
- key: model
site_path: tactile.model
type: str
required: true
doc: 当前使用的传感器型号名。必须与 devices/sensors/orisys/models/ 下的子目录名一致。
- key: left_video
site_path: tactile.left_video
type: str
required: true
doc: >
左指相机路径。必须用 /dev/v4l/by-path/ 下的稳定路径,
不要写 /dev/video0(编号会随插拔漂移)。
- key: right_video
site_path: tactile.right_video
type: str
required: true
doc: 右指相机路径,同样用 by-path 稳定路径。
- key: baseline_seconds
site_path: tactile.baseline_seconds
type: float
unit: s
required: false
default: 2.0
doc: 启动后采集静止基线的时间。所有力阈值都是相对这个基线的增量。
- key: publish_hz
site_path: tactile.publish_hz
type: float
unit: Hz
required: false
default: 20.0
doc: 数据发布率。
- key: cuda
site_path: tactile.cuda
type: bool
required: false
default: false
doc: >
双指并行时保持 false —— 开 GPU 会让两路互相抢 CUDA 上下文,反而更慢。
- key: infer_backend
site_path: tactile.infer_backend
type: str
required: false
default: cpu
doc: 推理后端。cpu / cupy / opencl。双指场景建议 cpu。
# --- 力阈值(通止规装配用)-------------------------------------------------
# 全部是「相对静止均值的增量」,不是绝对值。见 README 的坑点说明。
force_thresholds:
- key: touch_delta
site_path: hole_detect.stage3.touch_delta
unit: 增量
doc: 触面判定 —— 超过这个增量认为接触到了工件表面
- key: screw_done_shear_delta
site_path: system.screw_done_shear_delta
unit: 增量
doc: >
拧紧判定条件之一 —— 切向力超过这个增量。
必须与 screw_done_normal_delta 同时满足才判定拧紧到位。
- key: screw_done_normal_delta
site_path: system.screw_done_normal_delta
unit: 增量
doc: 拧紧判定条件之二 —— 法向力突增。
- key: safe_delta
site_path: hole_detect.stage3.safe_press_delta
unit: 增量
doc: 安全上限。任何时候超过立即抬升并报错。
+59
View File
@@ -0,0 +1,59 @@
# devices/sensors/orisys/models/ —— 传感器型号配置
## 放什么
每个传感器型号一个子目录,子目录内存放该型号的厂商配置。
## 格式与命名要求(必须遵守)
| 项 | 要求 | 违反的后果 |
|----|------|-----------|
| 子目录名 | **就是型号名**,会被 `config/site.yaml` 的 `tactile.model` 引用 | 选型找不到配置 |
| 配置文件名 | 固定为 `config.json` | 驱动按固定名加载 |
| 内容格式 | 厂商原始 JSON 结构,不要自己重排字段 | 解析失败 |
| 新增型号 | 从现有型号复制整个目录再改,不要在原型号上改 | 污染在用型号 |
| 生效 | 改 `site.yaml` 的 `tactile.model` → 重启 `scripts/run_tactile.sh` | — |
## 目录结构
```
models/
├── README.md # 本文件
├── dd03/ # 当前在用型号
│ └── config.json # 从 src/arm_application/config/orisys/dd03.json 迁来
└── <新型号>/
└── config.json
```
## 型号之间的差异由这里承载
不同型号可能在以下方面不同,**全部通过这个目录承载,上层代码不感知**:
- 相机分辨率与内参(影响深度图与力矢量图的标定)
- 力的通道数(有的型号只有 1 路切向力)
- 基线与噪声特性(影响阈值标定)
- 左右指的拼接参数
- 是否需要旋转配置(现有 `site.yaml` 里的 `left_rotation` / `right_rotation`)
## 新增一个型号的完整步骤
```bash
# 1. 复制现有型号作为模板
cp -r devices/sensors/orisys/models/dd03 devices/sensors/orisys/models/<新型号>
# 2. 按厂商资料修改 config.json 里的分辨率/内参/通道数
# 3. 在 site.yaml 里切换型号
# tactile:
# model: "<新型号>"
# 4. 重启并对比读数
bash scripts/run_tactile.sh --cli
```
## 关键提醒
换型号之后,**必须重新测基线并重新标定所有力阈值**。
`site.yaml` 里的 `touch_delta` / `screw_done_shear_delta` / `screw_done_normal_delta` /
`safe_delta` 都是「相对静止均值的增量」,不同型号的噪声水平不同,直接沿用旧阈值会导致
接触判定误触发或完全不触发。
@@ -0,0 +1,35 @@
{
"name": "dd03",
"type": "single_camera",
"version": "1.0",
"camera": {
"resolution_raw": [1280, 720],
"fps": 30,
"resolution_stitch": [420, 450],
"source_scale": 0.4,
"old": [672, 728, 1, 0.5],
"work": [504, 546, 1],
"work2": [420, 450, 0.8],
"work3": [350, 375, 4, 0.7],
"work4": [280, 300, 4, 0.4, 0.2]
},
"tracker": {
"adaptive": true,
"lower_threshold": 12.0,
"upper_threshold": 18.0,
"loop_closure_interval": 1,
"algorithm": "dis",
"dis_preset": "medium",
"dis_mode": 3,
"algorithm_list": ["raft", "dis"],
"hhd_downsample": 3
},
"contact": {
"min_contour_area": 3000,
"flow_threshold": 0.2,
"div_threshold": 0.022,
"expansion_threshold": 0.01,
"kernel_size": [5, 5],
"denoise_sigma": 12
}
}
+70
View File
@@ -0,0 +1,70 @@
# devices/sensors/orisys/sdk/ —— 放置 Orisys 厂商 SDK
## 放什么
Orisys 视触觉传感器的厂商 SDK(Python 包、原生库、或 ROS 包)。
## 格式与命名要求(必须遵守)
| 项 | 要求 | 违反的后果 |
|----|------|-----------|
| 格式 | 厂商原始发布格式。若是 `.whl`/`.tar.gz`,展开后放进来,不要留压缩包 | 无法 import |
| 目录名 | 保持厂商原包名(如 `orisys_sdk/`),**一律不要改名** | Python 内部按包名做相对导入与资源查找,改名直接 `ImportError` |
| 文件名 | 一律不改名 | 原生库按 `.so` 原名加载 |
| 版本 | 记录进 `../manifest.yaml` 的 `version` | 日后无法排查版本问题 |
| 原生库 | 若含 `.so`,确认架构是 x86_64 | 加载报错 |
| 编译产物 | 不要把 `build/` 提交进 git | 仓库膨胀 |
## 放好之后怎么让它生效
```bash
# 方式 A:可编辑安装(推荐,方便日后迭代 SDK)
conda activate ros_flexiv
pip install -e devices/sensors/orisys/sdk
# 方式 B:加入 PYTHONPATH(不改环境,只在当前 shell 生效)
export PYTHONPATH="$PWD/devices/sensors/orisys/sdk:$PYTHONPATH"
# 冒烟验证
python3 -c "import orisys_sdk; print(orisys_sdk.__file__)"
```
然后:
```bash
# 1. 确认选型
cat devices/registry.yaml # sensors: orisys
# 2. 在 site.yaml 里选中型号
# tactile:
# model: "dd03" # 必须与 models/ 下的子目录名一致
# left_video: "..." # 必须用 /dev/v4l/by-path/ 稳定路径
# right_video: "..."
# 3. 确认型号配置存在
ls devices/sensors/orisys/models/dd03/config.json
# 4. 启动
bash scripts/run_tactile.sh # 图传 + 状态话题
bash scripts/run_tactile.sh --cli # 只读数值,用来调阈值
```
## 迭代 SDK 时的注意事项
这个 SDK 会持续迭代更新,升级时按下面顺序做:
1. 把新版本放在原目录名旁边(如 `sdk_new/`),**不要直接覆盖旧版本**;
2. 用 `--cli` 对比新旧版本的读数(量纲、基线漂移、噪声水平是否变化);
3. 读数一致后再切换;
4. 更新 `../manifest.yaml` 的 `version` 字段;
5. **如果量纲变了**,`site.yaml` 里所有力阈值(`touch_delta` / `screw_done_shear_delta` /
`screw_done_normal_delta` / `safe_delta`)都需要重新标定 —— 这些阈值是相对基线的增量,
单位一变全部失效。
## 已踩过的坑(不要重犯)
1. **左右指必须各一个独立进程**,不要合并成单进程双路 —— 会抢 CUDA 上下文互相拖慢。
2. **`cuda` 保持 `false`**,`infer_backend` 用 `cpu`。
3. **退出时残留的孤儿 worker 会占死 `/dev/video`**,导致下次启动失败。
`run_tactile.sh` 里那段 `pkill` 清理逻辑必须保留。
4. **不要用 `/dev/video0`**,用 `/dev/v4l/by-path/pci-...-video-index0`。
+95
View File
@@ -0,0 +1,95 @@
# devices/sim/gazebo/ —— Gazebo 仿真平台
## 先读这段:当前仿真是假的
**现在项目里的「仿真」并不是 Gazebo。** 实际情况是:
- `src/arm_description/urdf/arm.gazebo.xacro` 是零引用的死文件;
- `arm_bringup/stack.launch.py` 的 `isaac` 分支只有一行日志,没有任何实际动作;
- `run_sim.sh` 走的是 `sim.launch.py`,底层是 **mock 驱动**(`mock_arm_driver.py`),
即用假数据模拟机器人状态,不是物理仿真;
- Gazebo 从未真正接线。
所以这份文档分两部分:**第一部分是怎么把 Gazebo 真正跑起来**(当前必须做的前置工作),
**第二部分才是日常怎么用**。不要跳过第一部分。
---
## 第一部分:把 Gazebo 真正接起来(前置工作)
要让 Gazebo 跑起来,缺的是这几块,按顺序补齐:
### 1. URDF 加 Gazebo 标签
现有 `arm_description/urdf/arm.gazebo.xacro` 是死文件,需要重写。每个 link 必须补:
```xml
<!-- 视觉碰撞体 -->
<gazebo reference="${prefix}link1">
<material>Gazebo/Grey</material>
</gazebo>
```
并在 `arm.ros2_control.xacro` 里把 `ros2_control` 标签的 hardware 插件从
`flexiv_hardware/FlexivHardware` 换成 `gazebo_ros2_control/GazeboSystem`。
### 2. 关节传动与控制器
Gazebo 需要 `transmission` 标签把关节映射到 `ros2_control`。控制器配置可以直接复用
`arm_control/config/controllers.yaml` 里的 `rizon_arm_controller`,只需把关节名前缀
从真实 SN(如 `Rizon4-063989_`)换成仿真专用的前缀(如 `sim_`)。
### 3. 传感器插件
| 仿真对象 | 插件 | 对应真实设备 |
|----------|------|--------------|
| 腕部相机 | `libgazebo_ros_camera.so` | D405 |
| 手外相机 | `libgazebo_ros_camera.so` | D435 |
| 深度 | `libgazebo_ros_depth_camera.so` | 对齐深度流 |
| 触觉力 | **需要自己写插件** | Orisys |
**最关键的一条:Orisys 视触觉没有现成的 Gazebo 插件。**
3 路切向力 + 1 路法向力 + 深度图 + 力矢量图 这套数据需要自定义 Gazebo 插件来模拟。
在它做出来之前,仿真里**无法验证通止规装配的力控部分**(S07 接触、S08 拧紧判定)。
建议的自定义插件做法:订阅 Gazebo 的接触传感器数据,按接触深度和相对滑动速度合成
出一组「类触觉」读数,再按真实传感器的量纲(任意单位增量)发布到同样的内部话题上。
### 4. 工件与通止规模型
需要给工件(含 k0 中心孔、k1/k2 螺纹孔)和通止规建简化网格,并保证:
- 工件的 k0/k1/k2 三个点在模型坐标系里的位置与实际一致 —— 否则推理出的坐标映射到仿真里会对不上;
- 螺纹孔要能表达「拧入」的几何阻力,否则 S08 的力反馈无法触发。
---
## 第二部分:日常怎么用
### 放置格式与命名要求
| 类型 | 放哪 | 格式 | 命名要求 |
|------|------|------|----------|
| 世界文件 | `worlds/` | `.world` 或 `.sdf` | `<场景名>.world`,如 `assembly.world` |
| 机器人模型 | `urdf/` | `.urdf` / `.xacro` | 必须以 `sim_` 为关节前缀,与真实臂区分 |
| 工件/夹具网格 | `models/<模型名>/` | `<模型名>.sdf` + `meshes/` | **模型名必须与推理用的类别名一致**,如 `workpiece` |
| 传感器插件 | `sensor_plugins/` | `.so` 源码 + `CMakeLists.txt` | 插件类名以 `Orisys` 开头 |
| 网格文件 | `models/<模型名>/meshes/` | `.stl` / `.dae` | 单位必须是米。用毫米建模的必须先缩放到米 |
### 导入一个新场景
1. 把 `.world` 放进 `worlds/`,网格放进 `models/<模型名>/`
2. 改 `config/site.yaml` 的 `sim.world` 指向该文件
3. 改 `devices/registry.yaml` 的 `sim: gazebo`(通常已是)
4. 启动 `scripts/run_sim.sh`
### 修改哪个配置才会生效
| 想改什么 | 改哪里 |
|----------|--------|
| 用哪个场景 | `site.yaml` → `sim.world` |
| 仿真是否启用物理 | `site.yaml` → `sim.physics_enable` |
| 仿真里的悬高/速度 | **复用真实的那套参数**(`servo.*` / `hole_detect.stage2.*`),不要另建一套,否则仿真调好的值搬到真机不生效 |
| 关节前缀 | `site.yaml` → `sim.joint_prefix`(默认 `sim_`) |
> **设计约束**:仿真与真机必须共用同一套 `site.yaml` 参数。任何「仿真专用参数」都是未来
> 仿真调参搬到真机失效的隐患。只允许物理引擎本身的参数(如重力、步长)放在 `sim.*` 下。
+91
View File
@@ -0,0 +1,91 @@
# =============================================================================
# devices/sim/gazebo/manifest.yaml
#
# Gazebo 仿真平台清单。
#
# 重要:本项目的 Gazebo 目前【尚未接线】。详见本目录 README.md。
# 这份清单声明的是「接线完成之后」应有的配置项,并把 world 设为非必填,
# 这样在仿真还没搭建好时不会阻塞 device_check。
# =============================================================================
id: gazebo
label: Gazebo 仿真
kind: sim
vendor: openrobotics
version: "待填 —— 接线后记录 Gazebo 版本"
description: >
物理仿真平台。当前项目的「仿真」实际是 mock 驱动(假数据),Gazebo 尚未接线:
arm.gazebo.xacro 零引用、stack.launch.py 的 isaac 分支只有日志、没有 world 文件。
接线步骤见本目录 README.md 的第一部分。
注意:Orisys 视触觉没有现成 Gazebo 插件,自定义插件做出来之前,
仿真里无法验证通止规装配的 S07 接触与 S08 拧紧力判据。
# --- 物料放置要求 ----------------------------------------------------------
assets:
- path: worlds
required: false
what: Gazebo 世界文件(.world / .sdf)
format: Gazebo 原生格式
naming: 场景名.world,如 assembly.world
where_used: 由 site.yaml 的 sim.world 指向
- path: models
required: false
what: 工件 / 夹具 / 通止规模型
format: 每个模型一个子目录,内含 <模型名>.sdf 与 meshes/
naming: >
模型名必须与推理用的类别名一致(如 workpiece),否则仿真里算出的坐标
与推理结果对不上。网格单位必须是米,用毫米建模的必须先缩放。
where_used: Gazebo 场景加载
- path: urdf
required: false
what: 仿真版机器人描述
format: .urdf / .xacro,需含 gazebo 与 ros2_control 标签
naming: 关节前缀必须以 sim_ 开头,与真实臂(Rizon4-xxxxxx_)区分
where_used: 仿真机器人加载
- path: sensor_plugins
required: false
what: 传感器插件(含 Orisys 触觉自定义插件)
format: 插件源码 + CMakeLists.txt
naming: 插件类名以 Orisys 开头
where_used: 仿真传感器数据发布
# --- 适配器 ----------------------------------------------------------------
driver:
package: arm_sim_gazebo
executable: gazebo_sim_launcher
build: colcon
notes: >
当前尚未实现。接线前 run_sim.sh 走的是 arm_bringup 的 mock 驱动路径。
# --- 配置项声明 ------------------------------------------------------------
# 设计约束:仿真与真机共用同一套 site.yaml 参数(servo.* / hole_detect.stage2.*),
# 避免「仿真调好的值搬到真机不生效」。只有物理引擎自身的参数放在 sim.* 下。
config_schema:
- key: world
site_path: sim.world
type: str
required: false
default: ""
doc: >
仿真世界文件路径。留空表示使用 Gazebo 默认空场景。
Gazebo 接线完成后填真实路径,如 devices/sim/gazebo/worlds/assembly.world。
- key: physics_enable
site_path: sim.physics_enable
type: bool
required: false
default: true
doc: 是否启用物理引擎。关闭可用于纯运动学验证。
- key: joint_prefix
site_path: sim.joint_prefix
type: str
required: false
default: sim_
doc: >
仿真关节名前缀。必须与真实臂的前缀(Rizon4-xxxxxx_)不同,
否则仿真与真机的 TF 会互相污染。
View File
View File
View File