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:
@@ -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 个节点没声明的参数、调参静默失效」那类问题的机制。
|
||||
@@ -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/` |
|
||||
@@ -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(提升无纹理表面深度质量)。
|
||||
@@ -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 会被清理。
|
||||
@@ -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/` 下。
|
||||
@@ -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
|
||||
@@ -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`
|
||||
@@ -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: 防撞安全高度。启动归位时先抬到这个高度再水平移动。
|
||||
@@ -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>`。
|
||||
@@ -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` 清理必须保留。
|
||||
@@ -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: 安全上限。任何时候超过立即抬升并报错。
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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`。
|
||||
@@ -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.*` 下。
|
||||
@@ -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 会互相污染。
|
||||
Reference in New Issue
Block a user