
> 基于 SeeedStudio 开源机械臂自主抓取项目 rebot_grasp 改编。本文记录一次使用 Orbbec Gemini 305 RGB-D 相机 复现机械臂纯视觉抓取物体的完整流程,包括环境准备、手眼标定、视觉检测、抓取执行、夹爪控制改进和常见避坑点。🔗GitHub
1. 项目背景
本次复现基于 SeeedStudio 开源机械臂自主抓取物体项目 rebot_grasp 进行改编。原项目主要围绕 reBot Arm B601 机械臂、RGB-D 相机、YOLO 目标检测、手眼标定和自主抓取流程展开。
本次复现中的主要差异是:
- 原项目相机配置逻辑经过适配;
– 实际使用的是 Orbbec Gemini 305 相机;
- 对 Orbbec 相机的 D2C 对齐失败问题做了自动降级;
- 对机械臂 SDK 的
src/布局导入路径做了兼容;
- 增加了夹爪初始化闭合、按
P键释放物体并进入下一轮识别的交互逻辑。
最终目标是实现:
RGB-D 相机观察物体
→ YOLO/YOLOE 识别目标
→ 深度图恢复目标 3D 坐标
→ 手眼标定转换到机械臂基坐标系
→ 机械臂移动并夹取
→ 回到静止位
→ 按 P 释放物体
→ 继续下一轮识别与抓取
2. 系统整体流程图

3. 硬件与软件准备
3.1 硬件
建议准备:
- reBot Arm B601 机械臂;
- Orbbec Gemini 305 RGB-D 相机;
- USB 数据线,尽量使用稳定供电的数据口;
- ArUco 标定板;
- 演示物体,例如:
- 香蕉;
- 水瓶;
- 纸杯;
- 颜色鲜明的小盒子;
- 轻质泡沫块。
初次复现不建议使用透明杯、金属反光物体、过黑物体或太小的物体,因为深度相机和夹爪都会比较容易失效。
3.2 软件环境
建议使用 Linux 环境,例如 Ubuntu / Jetson 系统,并准备 Python 虚拟环境。
基础依赖包括:
pip install numpy opencv-python pyyaml ultralytics
pip install pyorbbecsdk2
如果项目依赖机械臂 SDK,还需要确保:
pip install -e sdk/reBotArm_control_py
如果运行时报:
ModuleNotFoundError: No module named 'motorbridge'
说明机械臂 SDK 依赖没有装完整,需要回到 SDK 文档或项目环境文件中补齐依赖。
4. 配置文件说明
核心配置文件是:
config/default.yaml
4.1 相机配置
本次实际使用 Orbbec Gemini 305,但代码中仍复用了 Orbbec Gemini 系列驱动入口,例如:
camera:
type: orbbec_gemini2
serial: null
color_width: 1280
color_height: 720
fps: 30
这里的 orbbec_gemini2 更像是项目内部相机驱动名称,并不严格等于实际硬件型号。只要 Orbbec SDK 能正常打开 Gemini 305,就可以复用这一套驱动。
4.2 YOLO 模型配置
示例:
yolo:
model_name: "yoloe-26s-seg.pt"
device: "cpu"
use_world: true
custom_classes:
- "yellow banana"
- "water bottle"
- "light blue coffee cup"
- "cup"
- "green object"
- "tool"
注意:如果使用 YOLOE 的开放词汇能力,即:
use_world: true
程序会调用:
model.set_classes(custom_classes)
这可能触发额外下载:
mobileclip2_b.ts
如果设备无法访问 GitHub,建议提前准备该文件,或者先关闭开放词汇:
use_world: false
4.3 夹爪配置
示例:
robot:
gripper:
dm:
angle_open: 5.0
counterclockwise: true
tau_max: 1.5
close_torque: 1.0
default_force: 0.50
rs:
angle_open: 5.0
counterclockwise: false
tau_max: 1.5
close_torque: 1.5
default_force: 0.50
参数含义:
| 参数 | 作用 |
|---|---|
angle_open |
最大张开角度 |
close_torque |
真正抓取物体时的闭合力矩 |
default_force |
启动闭合休息位、默认保持力 |
tau_max |
夹爪力矩上限 |
如果夹爪启动后闭合不彻底,优先调大:
default_force
如果抓取时夹不紧,再调:
close_torque
5. 手眼标定

5.1 标定目标
手眼标定用于求解:
camera → gripper
也就是相机坐标系和机械臂末端坐标系之间的固定变换关系。
标定完成后,程序会保存:
config/calibration/orbbec_gemini2/hand_eye.npz
5.2 运行标定脚本
python scripts/collect_handeye_eih.py
成功后会看到类似输出:
[Result] Solving with 18 samples...
[Result] T_cam2gripper translation: x=-0.0834 y=0.0083 z=0.0518 m
[Result] [OK] Saved to /home/r/ws/rebot_grasp/config/calibration/orbbec_gemini2/hand_eye.npz
建议采集样本数不少于 15 组。本次复现采集了 18 组样本,结果较为稳定。
6. Orbbec 相机 D2C 问题与改进
6.1 遇到的问题
Orbbec 相机启动时可能报错:
Current stream profile is not support hardware d2c process
或者:
Hardware D2C unavailable
原因是当前分辨率 / 帧率 / 相机型号组合不支持硬件 D2C。
6.2 代码改进:自动降级 D2C
在 drivers/camera/orbbec_gemini2.py 中增加启动策略:
self._align_mode = "auto"
self._require_depth = True
核心逻辑:
def align_candidates():
if self._align_mode == "auto":
requested = ["hw", "sw", "none"] if self._require_depth else ["hw", "none"]
else:
requested = [self._align_mode]
candidates = []
for mode in requested:
if mode == "hw" and hasattr(OBAlignMode, "HW_MODE"):
candidates.append(("hardware D2C", getattr(OBAlignMode, "HW_MODE")))
elif mode == "sw" and hasattr(OBAlignMode, "SW_MODE"):
candidates.append(("software D2C", getattr(OBAlignMode, "SW_MODE")))
elif mode == "none":
candidates.append(("D2C disabled", None))
return candidates or [("D2C disabled", None)]
这样程序会按顺序尝试:
硬件 D2C → 软件 D2C → 禁用 D2C
如果硬件 D2C 不可用,程序不会直接退出,而是自动降级。
7. YOLO 感知验证
7.1 无窗口模式验证
由于某些 Linux / Jetson 环境下 OpenCV 的 cv2.namedWindow() 可能卡死,可以先用无窗口模式验证视觉链路:
python scripts/ordinary_grasp_pipeline.py --no-window --max-frames 30
成功时会输出:
[G] Best grasp:
class=yellow banana conf=0.663
center_px=(718, 390) angle_deg=86.11
jaw_width_m=0.0224 object_length_m=0.0763
position_xyz=[0.0164, 0.0058, 0.1300]
并保存预览图:
runs/ordinary_grasp_preview.jpg
7.2 判断感知结果是否合理
重点看:
position_xyz=[x, y, z]
这里是相机坐标系下的目标位置。
建议物体距离相机:
z = 0.25 ~ 0.45 m
太近时,例如:
z = 0.13 m
深度容易不稳定,也可能导致机械臂抓取点过低或不可达。
8. 机械臂 dry-run 验证
在真正抓取前,先运行:
python scripts/main.py --dry-run
按 G 后程序会打印:
[Grasp] pregrasp xyz=(+0.482,-0.008,+0.084)
[Grasp] grasp xyz=(+0.529,-0.012,+0.019)
[Grasp] dry run; skip motion
这里的坐标已经是机械臂基坐标系下的目标位姿。
第一次实抓前建议确认:
x = 0.25 ~ 0.45 m
y = -0.15 ~ 0.15 m
z > 0.05 m
如果 z 很低,例如:
z = 0.019 m
不建议直接真抓,容易碰桌面。
9. 手眼标定补偿
如果机械臂抓取点整体偏高、偏低、偏左或偏右,可以通过配置补偿:
calibration:
hand_eye_compensation_m:
x: 0.00
y: 0.00
z: 0.02
相关代码在 utils/camera_utils.py:
def hand_eye_compensation_matrix(cfg: dict[str, Any]) -> np.ndarray:
calibration = cfg.get("calibration") or {}
compensation = calibration.get("hand_eye_compensation_m") or {}
T = np.eye(4, dtype=np.float64)
T[:3, 3] = [
float(compensation.get("x", 0.0)),
float(compensation.get("y", 0.0)),
float(compensation.get("z", 0.0)),
]
return T
def compose_cam_to_base_transform(
T_tcp2base: np.ndarray,
T_hand_eye: np.ndarray,
cfg: dict[str, Any],
) -> np.ndarray:
T_compensation = hand_eye_compensation_matrix(cfg)
return T_compensation @ np.asarray(T_tcp2base) @ np.asarray(T_hand_eye)
含义是:
相机到机械臂基坐标变换
= 手动补偿 × 当前 TCP 到基坐标变换 × 手眼标定结果
第一次真抓建议让 z 保守一点,例如:
z: 0.02
宁可抓空气,也不要一上来碰桌面。
10. 关键代码实现说明
10.1 启动后夹爪自动闭合到休息位
原逻辑中,程序启动后夹爪会保持当前状态。如果启动前夹爪是张开的,初始化后仍然张开。
因此增加了专门的闭合休息位函数:
def close_gripper_rest(self, force: Optional[float] = None, timeout: float = 3.0) -> None:
self._ensure_running()
hold_torque = self._close_sign * float(
np.clip(abs(force if force is not None else self._default_force), 0.05, self._tau_max)
)
with self._state_lock:
self._target_pos = 0.0
self._hold_torque = hold_torque
self._state = self._STATE_HOLDING
self._position_reached = False
self._grasp_result = None
这个函数不会像 release_gripper() 那样先打开再闭合,而是直接闭合到休息位,并使用 default_force 保持。
在 scripts/main.py 初始化后调用:
print("[Robot] Close gripper to rest")
grasp_driver.close_gripper_rest()
print("[Robot] Move ready")
_move_ready(controller, ready_cfg)
10.2 按 P 键释放物体并进入下一轮识别
新增交互逻辑:
if key in (ord("p"), ord("P")):
print("n[P] Release object and resume")
if args.dry_run:
print("[P] dry run; skip gripper motion")
elif grasp_driver is None:
print("[P] Robot is not ready")
else:
grasp_driver.release_gripper()
object_held = False
print("[P] Gripper opened to max, then closed to rest")
frozen = False
last_display = None
continue
release_gripper() 的动作是:
def release_gripper(self, timeout: float = 4.0) -> None:
self._ensure_running()
self.open_gripper(timeout=min(2.0, timeout))
self._set_position_target(0.0)
self._wait_until(self._position_done, timeout)
也就是:
张开到最大量程 → 回到最小量程 / 闭合休息位
这样机械臂抓完物体回到 ready 位后,不会自动释放。操作者可以手动按 P 释放,然后系统恢复 live 识别,等待下一次 G。
10.3 抓取动作流程
简化后的抓取执行逻辑:
print("[Grasp] Open gripper")
grasp_driver.open_gripper()
print("[Grasp] Move to pregrasp")
controller.move_to_traj(xp, yp, zp, rxp, ryp, rzp, duration=2.0)
print("[Grasp] Move to grasp")
controller.move_to_traj(xg, yg, zg, rxg, ryg, rzg, duration=1.5)
print("[Grasp] Closing")
ok = grasp_driver.grasp()
print("[Grasp] Return ready")
_move_ready(controller, ready_cfg)
动作顺序是:
打开夹爪
→ 移动到预抓取位
→ 下降到抓取位
→ 闭合夹爪
→ 携带物体回到 ready 位
→ 等待 P 释放
11. 完整运行步骤
11.1 手眼标定
python scripts/collect_handeye_eih.py
确认生成:
ls -lh config/calibration/orbbec_gemini2/hand_eye.npz
11.2 验证视觉感知
如果 GUI 正常:
python scripts/ordinary_grasp_pipeline.py
如果 OpenCV 窗口卡死:
python scripts/ordinary_grasp_pipeline.py --no-window --max-frames 30
查看输出图:
ls -lh runs/ordinary_grasp_preview.jpg
11.3 机械臂 dry-run
python scripts/main.py --dry-run
按 G,观察打印出来的机械臂目标位姿是否合理。
11.4 真正执行抓取
python scripts/main.py
按键说明:
| 按键 | 功能 |
|---|---|
G |
识别当前帧并执行抓取 |
P |
释放当前夹取物体,然后恢复识别 |
R |
恢复 live 预览 |
Q / Esc |
退出,释放夹爪并回 home |
推荐流程:
放置物体
→ 按 G 抓取
→ 机械臂回 ready 位并保持夹持
→ 移动接物区域或准备下一步
→ 按 P 释放物体
→ 放置新物体
→ 再按 G
12. 常见避坑点
12.1 YOLO 权重明明在本地,却还去 GitHub 下载
如果使用 YOLOE 开放词汇:
use_world: true
程序会额外需要:
mobileclip2_b.ts
这不是主模型 yoloe-26s-seg.pt,而是文本编码器相关资源。
解决方法:
- 提前下载;
- 或关闭开放词汇:
use_world: false
12.2 Orbbec 相机被占用
报错:
uvc_open failed
检查占用进程:
ps aux | grep -E "ordinary_grasp|object_detection|grasp.py|collect_handeye|python"
结束旧进程:
kill <PID>
如果不退出:
kill -9 <PID>
12.3 OpenCV 窗口卡死
如果卡在:
[GUI] namedWindow...
说明 OpenCV GUI 后端可能有问题。
可使用无窗口模式:
python scripts/ordinary_grasp_pipeline.py --no-window --max-frames 30
或者尝试:
QT_QPA_PLATFORM=xcb python scripts/main.py
12.4 抓取点太低
如果 dry-run 输出:
z = 0.019
不建议真抓。可以在配置中提高补偿:
calibration:
hand_eye_compensation_m:
z: 0.02
12.5 物体太小,夹爪夹不到
如果演示道具比夹爪最小闭合间距还小,软件无法突破硬件限制。
建议:
- 换更大的道具;
- 给道具包泡棉或胶带;
- 给夹爪内侧贴软垫;
- 更换 V 型或窄指尖夹爪。
13. 推荐演示物体
优先选择:
- 黄色香蕉;
- 纸杯;
- 矿泉水瓶;
- 彩色泡沫块;
- 轻质塑料盒。
不建议初次使用:
- 透明玻璃杯;
- 金属反光物体;
- 黑色吸光物体;
- 太小或太薄的物体;
- 表面过于光滑的物体。
14. 总结
本次复现的关键工作并不只是“跑通一个抓取脚本”,而是把视觉、深度、标定、机械臂控制和夹爪状态管理串成了一个可重复执行的闭环。
最终系统具备以下能力:
- 使用 Orbbec Gemini 305 获取 RGB-D 信息;
- 使用 YOLOE 识别开放词汇物体;
- 使用深度图估计目标 3D 抓取点;
- 使用手眼标定将相机坐标转换到机械臂基坐标;
- 执行预抓取、抓取、回 ready 位;
- 按
P手动释放物体,并进入下一轮识别抓取。
这个流程跑通后,后续可以继续优化:
- 更稳定的目标选择策略;
- 更安全的桌面高度约束;
- 更好的夹爪指尖结构;
- GraspNet 6D 抓取;
- 无窗口自动抓取模式;
- 多物体连续抓取任务。
机械臂抓取这件事看起来像“看见 → 伸手 → 夹住”这么简单,但背后其实是相机、标定、深度、运动规划和夹爪控制一起排练的一场小型交响乐。只要每个模块都调到靠谱,它就会很自然地动起来。