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
+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`。