把当前控制栈收成一份可克隆的源码:腕部看不见时用顶视一次定到基座,二阶段对孔和手外标定跟着进同一份树。

现场 site.yaml、权重和编译产物仍不入库;模板只留占位符。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
arm_dev
2026-10-09 14:51:03 +08:00
parent 16d2912a9a
commit 7d22f777c8
67 changed files with 8597 additions and 409 deletions
+317
View File
@@ -0,0 +1,317 @@
# 环境安装(按本机快照复现)
本文是 **2026-10-09** 在这台工控机上核对过的软件栈,用来在另一台电脑上装出同一套能跑本仓库的环境。
核对来源:`/etc/os-release`、`conda-meta/history`、`conda list`、`~/flexiv_install` 的 cmake 版本文件、`nvidia-smi`。
日常怎么用已经装好的环境,看 [QUICKSTART.md](QUICKSTART.md)。本文只回答「从一台空的 Ubuntu 装到能 `source scripts/env.sh`」。
---
## 1. 本机现在是什么
| 项 | 本机实测 |
|----|----------|
| 系统 | Ubuntu 22.04.5 LTS,x86_64,内核 `6.8.0-138-generic` |
| GPU | NVIDIA GeForce RTX 3090 24GB,驱动 **595.91.07**(DKMS)。`nvidia-smi` 报告的 CUDA 是驱动上限 **13.2** |
| 推理用的 CUDA | **不装系统 CUDA toolkit**。PyTorch / TensorRT 用 pip wheel 自带的 **CUDA 12.4** 用户态库 |
| ROS | **Humble**。本机 **没有** `/opt/ros/humble`,ROS 在 conda 里(RoboStack) |
| Conda | Anaconda3 装在 `~/anaconda3`,conda **25.11.1** |
| 真机环境 | conda 环境 `ros_flexiv`,Python **3.11.15**,约 15GB |
| 仿真环境 | conda 环境 `ros_gz_sim`,Python **3.11.6**,Gazebo Sim **6.16.0**(Fortress / `ign gazebo`),约 5.1GB |
| 工作区 | 仓库在 `~/ros2`(ASCII)。`~/文档/ros2` 只是指向它的符号链接 |
| Flexiv C++ | `~/flexiv_install`,`flexiv_rdk` **1.9.1**(spdlog 1.9.2,Boost 1.83.0),约 194MB |
| Flexiv Python | `~/rdk_py310/bin/python` → 系统 `/usr/bin/python3.10`(3.10.12)+ `flexivrdk==1.9.1` + `numpy==1.26.4` |
| DDS 域 | 真机 `ROS_DOMAIN_ID=0`,仿真强制 `77` |
真机和仿真是两套 conda、两套 colcon 产物,互不覆盖:
| | 真机 | 仿真 |
|--|------|------|
| 激活 | `source ~/ros2/scripts/env.sh` | `source ~/ros2/scripts/sim_env.sh` |
| conda | `ros_flexiv` | `ros_gz_sim` |
| 产物 | `build/` `install/` | `build_sim/` `install_sim/` |
| 域 | 0 | 77 |
同一只 shell 里来回 `source` 会残留 `PATH` / `PYTHONPATH`。换环境时开新终端。
---
## 2. 新机器先对齐的路径
下面这些路径被脚本写死为用户 `cc` 的家目录。新机器用**同一个用户名** `cc`(家目录 `/home/cc`)时,后面的命令可以原样复制。
| 路径 | 谁在用 |
|------|--------|
| `/home/cc/anaconda3` | `scripts/run_real.sh`、`scripts/run_mock_demo.sh` 直接 `source` 这个 `conda.sh`。`scripts/env.sh` 用的是 `$HOME/anaconda3`,两边要是同一个目录 |
| `/home/cc/rdk_py310/bin/python` | `run_real.sh`、`test_rdk_python.sh`、`rdk_switch_tool.sh`、`scripts/lib/real_preflight.py` 的默认 `RDK_PYTHON` |
| `/home/cc/flexiv_install` | `run_real.sh` 用 `$HOME/flexiv_install`(随用户走) |
| `/home/cc/ros2` | 工作区。`colcon build` 必须在这条 ASCII 路径下,不要在中文目录里编 |
用户名不是 `cc` 时,至少要改 `scripts/run_real.sh` 里的 conda 路径,并在启动前 `export RDK_PYTHON=$HOME/rdk_py310/bin/python`。否则真机入口会去找 `/home/cc`。
建议磁盘空闲 **40GB 以上**(两个 conda 环境加下载缓存)。
---
## 3. 系统包与显卡
```bash
sudo apt update
sudo apt install -y \
build-essential git curl wget \
python3.10 python3-pip \
v4l-utils usbutils \
dkms linux-headers-$(uname -r)
sudo usermod -aG dialout "$USER"
```
`dialout` 给夹爪的 USB–RS485(`/dev/ttyUSB*`)。加完组要重新登录。
显卡驱动装到能让 `nvidia-smi` 跑起来即可。本机是 **595.91.07**。驱动只要够跑 CUDA 12.4(远低于 595 的驱动也可以),PyTorch 的 12.4 wheel 就能用。不要再装一套系统级 CUDA toolkit,避免和 wheel 里的 `libcudart` 抢。
Orisys 视触觉相机会被 USB 自动休眠卡死。本机规则文件是 `/etc/udev/rules.d/99-orisys-uvc.rules`:
```
# Orisys 视触觉相机 (1bcf:2281):禁止 USB 自动休眠(autosuspend 会把 UVC 相机搞到内核态卡死)
ACTION=="add|bind", SUBSYSTEM=="usb", ATTR{idVendor}=="1bcf", ATTR{idProduct}=="2281", ATTR{power/control}="on", ATTR{power/autosuspend}="-1"
```
写好后 `sudo udevadm control --reload-rules && sudo udevadm trigger`。
---
## 4. Anaconda
装到 **`$HOME/anaconda3`**(安装器里选 Just Me、路径填这个)。不要装成 `~/miniconda3` 或其他目录:`run_real.sh` 写死了 `/home/cc/anaconda3/etc/profile.d/conda.sh`。
本机 conda 版本是 25.11.1。装完新开一个终端:
```bash
source "$HOME/anaconda3/etc/profile.d/conda.sh"
conda --version
```
频道不要写进 `~/.condarc` 当成全局默认。本机 `~/.condarc` 只有 `defaults`,RoboStack 的频道是**每次安装命令显式带上**的:`-c robostack-staging -c conda-forge`。
---
## 5. 真机环境 `ros_flexiv`
下面三条 `conda` 命令来自本机 `~/anaconda3/envs/ros_flexiv/conda-meta/history`(2026-08-21 创建,2026-08-25 装 ROS)。pip 那一段是按同日 `conda list` 里的 pypi 版本还原的;当初的 `pip install` 原文没有留在 conda 历史里。
```bash
source "$HOME/anaconda3/etc/profile.d/conda.sh"
conda create -n ros_flexiv python=3.11 pip -y
conda install -y -n ros_flexiv -c robostack-staging -c conda-forge \
ros-humble-ros-base \
ros-humble-robot-state-publisher \
ros-humble-xacro \
ros-humble-tf2-ros \
ros-humble-joint-state-publisher \
compilers cmake make pkg-config catkin_pkg empy lark-parser numpy \
colcon-common-extensions
conda install -y -n ros_flexiv -c robostack-staging -c conda-forge \
ros-humble-rviz2
conda install -y -n ros_flexiv -c robostack-staging -c conda-forge \
ros-humble-moveit \
ros-humble-moveit-configs-utils \
ros-humble-control-msgs \
ros-humble-controller-manager \
ros-humble-joint-trajectory-controller \
ros-humble-ros2-control \
ros-humble-ros2-controllers
```
然后钉住本机正在用的 pip 包。Humble 的 `rosidl` 要 **empy 3.x**(conda 有时会给 4.x,构建消息包时会报 `AttributeError: NoneType object has no attribute shutdown`)。TensorRT 装 `tensorrt-cu12`,不要装元包 `tensorrt`。
```bash
conda activate ros_flexiv
python -m pip install \
'empy==3.3.4' \
'lark==1.3.1' \
'numpy==2.4.6' \
'opencv-python-headless==4.10.0.84' \
'pyrealsense2==2.58.3.10794' \
'pyserial==3.5' \
'minimalmodbus==2.1.1' \
'onnx==1.22.0' \
'onnxruntime==1.30.0' \
'onnxruntime-gpu==1.29.0' \
'tensorrt-cu12==10.13.3.9' \
'ultralytics==8.4.123'
python -m pip install \
'torch==2.6.0+cu124' \
'torchvision==0.21.0+cu124' \
--index-url https://download.pytorch.org/whl/cu124
```
相机走 **`pyrealsense2`**,本机没有装 `ros-humble-realsense2-camera`。夹爪 Modbus 走 `pyserial` + `minimalmodbus`。
Orisys SDK 不在 git 里。把本机这个 wheel 拷过去再装(`--no-deps`,避免它改写上面的 torch / numpy):
```bash
# 源文件:~/sensor_ws/0.4.2/orisys-0.4.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
conda activate ros_flexiv
python -m pip install --no-deps \
"$HOME/sensor_ws/0.4.2/orisys-0.4.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"
```
本机这个环境里还有 Jupyter、PySide6、`flexiv-assembly` 等包,**本仓库脚本不用它们**,新机器不必装。
激活后应看到:
```bash
conda activate ros_flexiv
python -c 'import sys; print(sys.version)' # 3.11.x
echo "ROS_DISTRO=$ROS_DISTRO" # humble
python -c 'import rclpy, cv2, pyrealsense2, torch; import tensorrt as trt; print("torch", torch.__version__); print("tensorrt", trt.__version__); print("cuda", torch.cuda.is_available())'
```
本机对应输出:`torch 2.6.0+cu124`,`tensorrt 10.13.3.9`,`torch.cuda.is_available()` 为真。
---
## 6. Flexiv RDK(与 conda 分开)
运行中的库是 **1.9.1**。仓库里的 `src/flexiv_rdk` 工程版本是 **2.1**(`project(flexiv_rdk VERSION 2.1 ...)`)。`scripts/build_flexiv_rdk.sh` 编的是这份当前源码,产物和本机 `~/flexiv_install` 不是同一个版本。
新机器要对齐**正在跑的**这套,把本机这两个目录原样拷过去:
```bash
# 在新机器上,从本机拉(把 user@old-host 换成这台工控机)
rsync -a user@old-host:~/flexiv_install/ ~/flexiv_install/
rsync -a user@old-host:~/rdk_py310/ ~/rdk_py310/
```
拷完核对:
```bash
grep 'set(PACKAGE_VERSION' ~/flexiv_install/lib/cmake/flexiv_rdk/flexiv_rdk-config-version.cmake
# 应有 set(PACKAGE_VERSION "1.9.1")
~/rdk_py310/bin/python -c 'import flexivrdk; print(flexivrdk.__version__)'
# 1.9.1
```
`~/rdk_py310/bin/python` 是一个壳:清掉 `PYTHONHOME`(避免 conda 污染),再用系统 Python 3.10 加上自己的 `site-packages`。`flexivrdk` 1.9.1 的 ABI 是 cp310,**不要**装进 `ros_flexiv`(那是 Python 3.11)。
壳脚本内容(拷目录时已经带上;若要手写,注意把路径改成新机器的家目录):
```bash
#!/bin/bash
export PYTHONPATH="${HOME}/rdk_py310/lib/python3.10/site-packages${PYTHONPATH:+:$PYTHONPATH}"
unset PYTHONHOME
exec /usr/bin/python3.10 "$@"
```
---
## 7. 仿真环境 `ros_gz_sim`
用仓库脚本,可重复执行:
```bash
bash ~/ros2/scripts/install_sim_env.sh
bash ~/ros2/scripts/install_sim_env.sh --verify
```
脚本会创建 `ros_gz_sim`(Python 3.11),从 `robostack-staging` + `conda-forge` 安装 `ros-humble-ros-base`、`ros-humble-ros-gz`、`ros-humble-gz-ros2-control`、`ros2_control` / `ros2_controllers`、`controller_manager`、`xacro`、`tf2_ros`、`camera-info-manager`、`image-transport-plugins`,并把 **empy 钉在 3.3.4**。
本机 `--verify` 能通过时的关键版本:
| 包 | 版本 |
|----|------|
| Gazebo Sim(`ign gazebo --version`) | 6.16.0 |
| `libignition-gazebo6` | 6.16.0 |
| `ros-humble-ros-gz` | 0.244.20 |
| `ros-humble-gz-ros2-control` | 0.7.15 |
| `empy` | 3.3.4(conda-forge) |
| `numpy` | 1.26.4(conda-forge) |
Humble 配的是 Gazebo Fortress,命令是 `ign gazebo`,不是 `gz sim`。
仿真环境里**没有**真机那套 `torch` / `tensorrt` / `pyrealsense2` / `onnx`。本机仿真环境额外有一个 pip 的 `onnxruntime==1.30.0`。缺 `onnx` 时部分单测会红,这是现况;不要把真机的 pip 依赖灌进 `ros_gz_sim`,两个环境是故意隔开的。
---
## 8. 工作区、现场配置、编译
```bash
git clone <本仓库> ~/ros2
cd ~/ros2
# 若习惯从中文路径进: ln -s ~/ros2 ~/文档/ros2
```
`config/site.yaml`、YOLO 权重(`.pt` / `.onnx` / `.engine`)不在 git 里。
```bash
cp ~/ros2/config/site.example.yaml ~/ros2/config/site.yaml
# 按新机器填写 robot.sn、robots[].ip、相机序列号、权重路径
# 权重放到 models/<分组>/ ,见 models/README.md
```
TensorRT 的 `.engine` 跟 GPU 绑定。换了显卡(哪怕仍是 3090 以外的型号)要在新机器的 `ros_flexiv` 里重新 `bash ~/ros2/scripts/weights.sh convert --to engine`。直接拷本机的 `.engine` 经常加载失败。
手眼结果 `handeye/results/` 在 git 里,但是**这台机器、这个相机安装位**的矩阵。新机器克隆后要重新标定,见 [HANDEYE.md](HANDEYE.md)。
在 **`ros_flexiv`** 里编真机(先确认 `conda info --envs` 里星号在 `ros_flexiv`,python 指向 `~/anaconda3/envs/ros_flexiv/bin/python`):
```bash
cd ~/ros2
conda activate ros_flexiv
bash scripts/build_all.sh
source scripts/env.sh
echo "ROS_DISTRO=$ROS_DISTRO ROS_DOMAIN_ID=$ROS_DOMAIN_ID"
# humble 与 0
```
编完查 shebang 没有被仿真环境污染(正常应是 0):
```bash
grep -l ros_gz_sim install/*/lib/*/* | wc -l
```
仿真另开一个终端:
```bash
cd ~/ros2
bash scripts/build_sim.sh
source scripts/sim_env.sh
# 打印里应有 CONDA=ros_gz_sim ROS_DISTRO=humble ROS_DOMAIN_ID=77
```
---
## 9. 装完怎么确认
```bash
# 真机环境
source ~/ros2/scripts/env.sh
python -c 'import rclpy, torch, tensorrt, pyrealsense2, cv2, serial, minimalmodbus, onnx'
echo "$ROS_DISTRO $ROS_DOMAIN_ID" # humble 0
~/rdk_py310/bin/python -c 'import flexivrdk; print(flexivrdk.__version__)' # 1.9.1
# 仿真环境(新终端)
bash ~/ros2/scripts/install_sim_env.sh --verify
```
有机器人、示教器已切到 AUTO + Remote Ethernet、并且 `config/site.yaml` 已填好之后,再跑:
```bash
bash ~/ros2/scripts/test_rdk_python.sh
bash ~/ros2/scripts/run_real_preflight.sh --arm 89
```
这两条会访问真机网络,空机器上失败不代表环境没装上。
---
## 10. 本机还有、但本仓库不用的 conda 环境
`conda env list` 里还有 `flexiv`、`flexiv_rdk_py312`、`orisys`、`py311`、`ros2`。`scripts/env.sh` 和 `scripts/sim_env.sh` 都不会激活它们。新机器只建 `ros_flexiv` 和 `ros_gz_sim` 即可。
+18 -3
View File
@@ -68,6 +68,21 @@ bash ~/ros2/scripts/run_camera_d405.sh --arm 89 --preview # T2 相机预览,
bash ~/ros2/scripts/run_all.sh --arm 89 # T3 三段串联(默认不拧入)
```
一阶段默认 **D405 主跟踪**:画面里没有 conf≥`perception.wrist_handoff_conf`(0.5)的目标,
马上用顶视 D435 **拍一张** 把工件 / k0 定到基座系(眼在手外 T,不再乘法兰),把臂迁过去
让 D405 重新看见。D405 跟住后,左臂法兰 XY 连续 `servo.lock_still_sec`(3 s)位移不超过
`lock_still_move_m`(15 mm)→ `/visual_servo/locked` → 二阶段对孔。
```bash
bash ~/ros2/scripts/run_real.sh --arm 89
bash ~/ros2/scripts/run_camera_d405.sh --arm 89 --preview
bash ~/ros2/scripts/run_camera_d435.sh --preview # 混合跟踪必须两台都开
bash ~/ros2/scripts/run_visual_servo_rt.sh --arm 89 # 先只看框
bash ~/ros2/scripts/run_visual_servo_rt.sh --arm 89 --control
```
`track_camera: overhead` 仍是整段只用 D435 的实验开关。非法值启动即报错。
细节、参数、退出码见 [`HOLE_DETECT.md`](HOLE_DETECT.md)。
### 1.4 现在到哪一步了(如实标注,别信印象)
@@ -78,7 +93,7 @@ bash ~/ros2/scripts/run_all.sh --arm 89 # T3 三段串联(
| 按螺距旋入(螺纹规) | ✅ 有参数,`--depth`/`--pitch` 控制 | `run_stage3_search.sh --arm 89 --depth 6 --pitch 1.0` |
| Orisys 双指触觉(法向 + 两路切向) | ⚠️ 能读,但**轴向映射尚未实测确认** | 见 [`TACTILE.md`](TACTILE.md) |
| Orisys 的深度图 / 力矢量图 | ⚠️ 消息结构与话题已在,真机值待确认 | 同上 |
| D435 手外相机(S03 粗定位兜底) | ❌ **还没做手眼标定**,链路不可用 | 见 `devices/cameras/README.md` |
| D435 手外相机(一阶段一次性 + S03) | ⚠️ **89 已标定**;一阶段 D405 看不见时拍一张定 K0(混合跟踪);S03 粗定位真机未对过 | `handeye/results/89_d435_eye_to_hand.yaml` |
| 自动启动全部服务(一条命令拉起所有进程) | ❌ 还没做,目前靠三个终端手工起 | 见第 6 节待办 |
| 「规有没有被夹住」自检 | ❌ 还没做 | 见第 6 节待办 |
| 装配合格判定(通/止) | ⚠️ **通止规装配流程**能按力矩判据拧到底/拧出来,但"通/止合格"结论还没做 | `run_gauge_assembly.sh --arm 89 --dry-run` |
@@ -357,7 +372,7 @@ bash scripts/weights.sh doctor --check-engines
### 4.3 单元测试怎么跑
**每个功能包都有自己的测试**,改完对应包就跑一下。当前 pytest 收集 **1128 项**
**每个功能包都有自己的测试**,改完对应包就跑一下。当前 pytest 收集 **1301 项**
(另有 1 项按环境 skip)。
```bash
@@ -486,7 +501,7 @@ bash scripts/run_unit_tests.sh -k gauge # 只跑名字带 gauge 的
且顺序错了会出"控制器没起来但脚本继续跑"这类问题。
2. **夹爪持规自检** —— 启动后先用触觉判断"规真的被夹着",没有就报错停机
(现在没有这一步,规掉了会一路跑到撞工件才发现)。
3. **D435 手眼标定** —— 标完 S03 粗定位链路才可用(工件不在 D405 视野里时靠它先粗居中)。
3. **D435 手眼标定(91)** —— 89 已写入 `handeye/results/89_d435_eye_to_hand.yaml`(18 视角)。91 还没标。S03 粗定位要用这份 TF 在真机上对一次工件才算链路验过。
4. **装配合格判定** —— 把力曲线转成"通/止"结论。当前只输出原始几何与力,
判定逻辑(含螺纹旋入圈数)还没写。
+57 -25
View File
@@ -1,50 +1,82 @@
# 手眼标定(眼在手上)
# 手眼标定
每台机械臂一份结果,**禁止混用**:
两种装法,两块板,两台相机,两份结果。**禁止混用**:
| 臂 | 结果 | 示教关节 | 工作目录 |
|----|------|----------|----------|
| 89 左 | `handeye/results/89_eye_in_hand.yaml` | `handeye/poses/89_eye_in_hand.yaml` | `handeye/tmp/89/` |
| 91 右 | `handeye/results/91_eye_in_hand.yaml` | `handeye/poses/91_eye_in_hand.yaml` | `handeye/tmp/91/` |
| 模式 | 相机 | 板 | 板怎么摆 | 结果文件 | `T_4x4` 含义 |
|------|------|----|----------|----------|--------------|
| `eye_in_hand` | 腕部 D405 | A5,内角点 8×11,15 mm | 板放台面不动 | `handeye/results/<臂号>_eye_in_hand.yaml` | 相机 → 法兰 |
| `eye_to_hand` | 顶置 D435 | A4,内角点 11×8,20 mm | **板装到法兰上** | `handeye/results/<臂号>_d435_eye_to_hand.yaml` | 相机 → 这条臂的基座 |
文件名统一为 `<臂号>_<模式>.yaml`(模式 = `eye_in_hand` | `eye_to_hand`),
全部集中在仓库根 `handeye/`(`poses/` 输入、`results/` 输出、`tmp/` 临时产物)。
目录结构、板尺寸三处一致性与全部选项见 `handeye/README.md`。
| 臂 | 眼在手上结果 | 眼在手外结果 | 示教关节 | 工作目录 |
|----|--------------|--------------|----------|----------|
| 89 左 | `89_eye_in_hand.yaml` | `89_d435_eye_to_hand.yaml` | `poses/89_<模式>.yaml` | `tmp/89/` |
| 91 右 | `91_eye_in_hand.yaml` | `91_d435_eye_to_hand.yaml` | `poses/91_<模式>.yaml` | `tmp/91/` |
`T_4x4` = **相机 → 法兰**。跟踪和静态 TF 都读当前 `--arm` 对应的文件。
目录结构、按键、稳定性参数见 `handeye/README.md`。棋盘尺寸只改
`config/site.yaml` → `handeye.boards.<模式>`(内角点 = 方格数 − 1)。
没配过的模式会被 `run_handeye.sh` 当场拦下,不会静默用另一块板的尺寸。
棋盘尺寸只改 `site.yaml` → `handeye.board_*` / `square_size_m`(内角点 = 方格数 − 1)。两臂可共用同一块板。
## 流程
## 眼在手上(腕部 D405 + A5 板)
`teach`(RDK 软拖拽)与 `run_real.sh` **互斥**。全程带同一 `--arm`。
`--mode eye_in_hand|eye_to_hand` 选模式(读写对应 `<臂号>_<模式>.yaml`,两种模式互不覆盖);
`teach --points N` 设定目标点数;`teach --ros --auto-solve` 采完自动 merge 并接着标定。
```bash
# 1) 停掉 run_real.sh 和视觉伺服
# 2) 示教 8–12 个姿态(棋盘清晰、占画面 30–70%,姿态拉开)
bash ~/ros2/scripts/run_handeye.sh --arm 89 teach
# 窗口:d=拖拽 Enter=拍照 q=结束
# 窗口:d=拖拽 Enter=拍照 q=结束(窗口只显示画面,不做棋盘检测)
# 接着采、不清空旧点: teach --append
# 3) 写入该臂 handeye/poses/<臂号>_<模式>.yaml
# 3) 写入该臂 handeye/poses/89_eye_in_hand.yaml
bash ~/ros2/scripts/run_handeye.sh --arm 89 merge
# 4) 再开实机 + 相机,棋盘不要动
# 4) 再开实机 + 腕部相机,棋盘不要动
bash ~/ros2/scripts/run_real.sh --arm 89
bash ~/ros2/scripts/run_camera_d405.sh --arm 89 --preview
bash ~/ros2/scripts/run_handeye.sh --arm 89 calibrate
```
91 把 `--arm 89` 改成 `--arm 91`。91 的结果文件初始是 `uncalibrated: true`,标定成功后才会写出 `T_4x4`。
91 把 `--arm 89` 改成 `--arm 91`。
查看路径与板参数:`bash ~/ros2/scripts/run_handeye.sh --arm 89 show`
## 眼在手外(顶置 D435 + A4 板)
物理摆法与上面**反过来**:相机固定,板跟着臂动。板若还摆在台面上,
`target→camera` 不变,方程退化(解不出来或解出垃圾)。
```bash
# 1) 把 A4 板刚性固定在法兰 / 夹爪上(夹在正中间,标定过程不要松)
# 2) 起臂 + 顶置相机(两个终端,常驻)
bash ~/ros2/scripts/run_real.sh --arm 89
bash ~/ros2/scripts/run_camera_d435.sh --preview
# 3) 采点:推着臂走,让板在 D435 画面里出现在不同位置、不同角度
# teach 与 run_real.sh 互斥 —— 用 RDK 拖拽前先停掉 run_real.sh
bash ~/ros2/scripts/run_handeye.sh --arm 89 teach --mode eye_to_hand --points 12
# 4) 合并 + 自动走点求解(此时再开 run_real.sh + D435)
bash ~/ros2/scripts/run_handeye.sh --arm 89 merge --mode eye_to_hand
bash ~/ros2/scripts/run_real.sh --arm 89
bash ~/ros2/scripts/run_camera_d435.sh --preview
bash ~/ros2/scripts/run_handeye.sh --arm 89 calibrate --mode eye_to_hand
```
查看当前模式选中的板 / 相机 / 文件:
```bash
bash ~/ros2/scripts/run_handeye.sh --arm 89 show --mode eye_to_hand
```
应看到 `camera=d435`、`board 11x8 inner, square=0.02 m`、
结果路径 `handeye/results/89_d435_eye_to_hand.yaml`。不是这三样就停,
不要带着错配置开标定。
## 结果怎么用
`visual_servo.launch.py` 读当前臂 `T_4x4`,发布 `{ee_frame}` → `{camera_frame}` 的 static TF。
`servo_node` 用同一份 T 做 `hover` / `lookat`。换结果后**重启** `run_visual_servo_rt.sh --arm …`。
装配 HMI(`ros_flexiv`)选左/右臂后,读同一份 `handeye/results/89_eye_in_hand.yaml` / `91_eye_in_hand.yaml`。
- **眼在手上**:`visual_servo.launch.py` / `stage2_locate.launch.py` /
`gauge_assembly.launch.py` 读 `paths.handeye_file`,发布
`{法兰}` → `{腕部相机}_color_optical_frame`。`servo_node` 用同一份 T
做 `hover` / `lookat`。换结果后**重启**对应节点。
- **眼在手外**:`gauge_assembly.launch.py` 按
`handeye/results/<臂号>_<相机>_eye_to_hand.yaml` 自己找,发布
`{基座}` → `{手外相机}_color_optical_frame`。S03 粗定位查的就是这条 TF。
**不要**改 `paths.handeye_file` 去指向它 —— 那会把腕部精拍定位的 TF 换掉。
+12 -1
View File
@@ -99,15 +99,26 @@ bash ~/ros2/scripts/run_all.sh --arm 89 --depth 6 --pitch 1.0
## 二阶段一次性拍照定位(stage2,与一阶段跟踪分开跑)
先悬停垂直朝下 → 拍一张照 → `detect_best_1.onnx` 框工件 → Hough 解 k1/k2 孔心 →
深度(孔外工作面中值)+ 内参 + 手眼换算基座坐标 → 移动夹爪/通止规轴线到目标孔正上方 →
深度(孔外工作面中值)+ 内参 + 手眼换算基座坐标 → 移动**规尖**到目标孔正上方 →
复拍验证(≤`align_max_iters` 次,收敛判据 `align_tol_m`)→ 锁定保持。
> **对的是"规尖",不是"法兰轴线"**(2026-10-08 定稿)。通止规夹在爪里,相对法兰轴线
> 可以整体偏出若干毫米;旧写法把**法兰**对到孔上,规尖就偏着压在孔沿上,轻降一压
> 腕力蹦到 63N。现在每一帧同时解「规尖基座 XY」和「孔基座 XY」——两者走同一条手眼
> 链,链路误差与装夹变动一起抵消。规尖偏移是**刚体量**,量准一份(`tip_calib_n` 拍
> 一致)就冻结。
> 真机验证(arm 89,独立重测那一帧):规尖到 k1 **0.34mm**、到 k2 61.15mm、
> 反投影工具长 222mm(与 site 的 223mm 一致),偏移冻结值 (2.6, −1.0)mm。
> 相关开关在 `site.yaml → hole_detect.stage2:`(`align_tip_vision` / `tip_*`)。
> 量不到规尖偏移时二阶段**判失败**(exit 8),不会拿"法兰对上了"冒充成功。
```bash
# 前置:T1 run_real.sh --arm 89;T3 run_camera_d405.sh --arm 89 --preview
bash ~/ros2/scripts/run_stage2_locate.sh --arm 89 # 完整流程(自动拉起触觉服务,仅左臂)
bash ~/ros2/scripts/run_stage2_locate.sh --arm 89 --hover-only # 只垂直+悬停锁定,用来调高度
bash ~/ros2/scripts/run_stage2_locate.sh --arm 89 --hole k2 # 对准 k2(默认 k1)
bash ~/ros2/scripts/run_stage2_locate.sh --arm 89 --hover-z 0.28 # 临时改悬停高,不动 yaml
bash ~/ros2/scripts/run_all.sh --arm 89 --no-descent # 只验证精对准、不轻降
```
悬停锁高接口:`site.yaml → robots[89].servo.hole_hover_z_m`(91 在同名段)。
+3 -1
View File
@@ -4,7 +4,9 @@
## 依赖
- Ubuntu 22.04 + ROS 2 Humble
从一台空机器装到和本机一样:见 **[ENVIRONMENT.md](ENVIRONMENT.md)**(2026-10-09 快照:RoboStack Humble、`ros_flexiv` / `ros_gz_sim`、RDK 1.9.1)。
- Ubuntu 22.04 + ROS 2 Humble(本机 ROS 在 conda 里,没有 `/opt/ros`)
- Conda 环境 `ros_flexiv`(本仓库脚本默认这个名字)
- Flexiv RDK Python:`/home/cc/rdk_py310/bin/python`(flexivrdk 1.9.x)
- 工控机与机器人在同一网段(例:`192.168.2.10` ↔ `192.168.2.101`)
+2 -1
View File
@@ -84,7 +84,7 @@ devices/<kind>/<brand>/
| `run_camera_d405.sh --arm … --preview` | 该臂腕部 D405。89→`/d405_left/`,91→`/d405_right/` |
| `run_camera_d435.sh --preview` | 顶视 D435(手外,可选) |
| `list_realsense.sh` | 列出已接 RealSense 序列号 |
| `run_visual_servo_rt.sh --arm … [--control]` | YOLO + 伺服。无 `--control` 只看框;有则动臂。从当前姿态开始,不回 home |
| `run_visual_servo_rt.sh --arm … [--control]` | YOLO + 伺服。无 `--control` 只看框;有则动臂。`wrist`=D405 主跟踪,看不见则 D435 一次性定 K0;`overhead`=整段顶视 |
| `go_home.sh --arm …` | 回 named pose `home`(会先停伺服) |
| `show_site_config.sh` | 打印当前臂解析出的路径/锁高/相机 |
@@ -183,6 +183,7 @@ devices/<kind>/<brand>/
| `FRAMEWORK_RULES.md` | **框架规则**(硬规矩,改代码前先读;第 8 节是按症状查的反例表) |
| `WORKSPACE.md` | 本页,目录与职责 |
| `QUICKSTART.md` | 环境、编译、第一次实机 |
| `ENVIRONMENT.md` | **按本机快照在另一台电脑上装同一套环境**(conda / RDK / 显卡 / 路径) |
| `ARCHITECTURE.md` | 进程与数据流 |
| `SITE_CONFIG.md` | `site.yaml` 字段 |
| `HANDEYE.md` | 示教 / merge / 标定 |