从理论到实践:基于Python与RealSense的JAKA机械臂手眼标定全流程解析
1. 手眼标定的基本原理与场景
手眼标定是机器人视觉领域的基础技术,简单来说就是确定相机"眼睛"和机械臂"手"之间的相对位置关系。想象一下你闭着眼睛摸桌上的水杯,如果不知道手和眼睛的坐标对应关系,很容易把杯子打翻。机械臂同样需要这种标定才能准确抓取摄像头看到的物体。
在实际项目中,我常用Intel RealSense这类深度相机配合JAKA机械臂工作。RealSense能同时提供RGB和深度信息,而JAKA的Python SDK对开发者非常友好。标定过程需要解决的核心问题是:当机械臂末端移动时,如何将相机坐标系下的物体坐标转换到机械臂基坐标系。
数学上这涉及到两个关键变换:
- 相机到标定板的变换(通过ArUco码计算)
- 机械臂末端到基座的变换(通过SDK获取) 最终通过这两个变换链求解相机到机械臂末端的固定变换关系。这里会频繁用到旋转矩阵、欧拉角、四元数等表示方法,后续章节会详细解释它们的转换关系。
2. 环境搭建与硬件连接
2.1 硬件准备清单
- JAKA机械臂(测试用Zu 7为例)
- Intel RealSense D435i深度相机
- 打印好的ArUco标定板(建议边长14cm)
- 固定支架(将相机安装在机械臂末端)
2.2 Python环境配置
推荐使用Anaconda创建虚拟环境:
conda create -n handeye python=3.8 conda activate handeye pip install numpy opencv-python pyrealsense2 transforms3d jkrc安装jkrc时可能会遇到权限问题,这是我踩过的坑:
# 如果直接pip安装失败 git clone https://github.com/JAKA-Robotics/JAKA-Python-SDK.git cd JAKA-Python-SDK python setup.py install2.3 设备连接验证
先测试机械臂通信:
import jkrc robot = jkrc.RC("192.168.0.104") # 替换实际IP robot.login() print(robot.get_tcp_position())再测试相机流:
import pyrealsense2 as rs pipeline = rs.pipeline() config = rs.config() config.enable_stream(rs.stream.color, 848, 480, rs.format.bgr8, 30) pipeline.start(config)3. 核心数学原理详解
3.1 旋转表示的四种形式
在标定过程中会遇到四种旋转表示:
旋转矩阵:3x3正交矩阵,适合计算但不够直观
# 欧拉角转旋转矩阵 R = tfs.euler.euler2mat(rx, ry, rz, 'sxyz')欧拉角:绕XYZ轴旋转的角度值(注意顺序)
# 机械臂常用弧度制 rx, ry, rz = 0.1, -0.2, 1.57旋转向量:OpenCV常用形式
rvec = np.array([0.1, 0.2, 0.3]) # 方向为旋转轴,模长为角度 R, _ = cv2.Rodrigues(rvec) # 转旋转矩阵四元数:[w,x,y,z]形式,避免万向节锁
q = tfs.quaternions.euler2quat(rx, ry, rz, 'sxyz')
3.2 手眼标定的数学模型
核心方程是AX=XB,其中:
- A:机械臂末端运动
- B:相机观测到的运动
- X:待求的手眼变换
在代码中对应:
R_cam2gripper, t_cam2gripper = cv2.calibrateHandEye( R_Hgs, T_Hgs, R_Hcs, T_Hcs, method=cv2.CALIB_HAND_EYE_TSAI )4. 完整标定流程实现
4.1 数据采集步骤
- 固定标定板在机械臂工作范围内
- 移动机械臂到不同位姿(建议10组以上)
- 在每个位姿:
- 按'r'键记录机械臂位姿和标定板位姿
- 确保标定板在相机视野内
while True: rgb, depth, intr_matrix, intr_coeffs = get_aligned_images() cv2.imshow('RGB', rgb) key = cv2.waitKey(1) if key == ord('r'): hands.append(get_jaka_gripper()) cameras.append(get_realsense_mark(intr_matrix,intr_coeffs)) print(f"已记录 {len(hands)} 组数据")4.2 标定结果验证
计算重投影误差是验证标定质量的关键:
for i in range(len(hands)): # 计算理论机械臂位姿 pred_pose = RT_g2b @ RT_c2g @ RT_t2c # 与实际记录位姿比较 error = np.linalg.norm(pred_pose - actual_pose) print(f"第{i}组误差:{error:.2f}mm")我通常要求平均误差小于3mm,如果误差过大:
- 检查标定板打印尺寸是否准确
- 增加数据采集点位(特别是远距离和旋转姿态)
- 检查相机对焦是否清晰
5. 实际应用中的经验技巧
5.1 提高标定精度的建议
标定板选择:
- 使用高对比度ArUco码
- 实际测量边长并精确输入代码(示例中0.14需替换)
机械臂运动规划:
- 包含X/Y/Z三个方向的平移
- 包含绕各轴的旋转(但避免180°奇异位形)
相机设置:
# 手动设置曝光避免过曝 sensor = profile.get_device().first_color_sensor() sensor.set_option(rs.option.exposure, 100)
5.2 常见问题排查
问题1:cv2.aruco模块找不到
pip install opencv-contrib-python问题2:机械臂位姿跳动大
- 检查机械臂重复定位精度
- 在机械臂静止后延迟0.5秒再采集数据
问题3:标定板检测不稳定
- 调整detector参数:
parameters = aruco.DetectorParameters_create() parameters.cornerRefinementMethod = aruco.CORNER_REFINE_SUBPIX6. 进阶应用与扩展
6.1 眼在手外(Eye-to-Hand)配置
如果相机固定在工作台,只需修改计算逻辑:
# 使用不同的求解方法 R_cam2base, t_cam2base = cv2.calibrateHandEye( R_Hcs, T_Hcs, R_Hgs, T_Hgs, method=cv2.CALIB_HAND_EYE_PARK )6.2 多相机协同标定
对于多相机系统,可以先分别标定各相机到机械臂的关系,再通过共同视野物体计算相机间变换:
# 已知cam1到机械臂的变换RT_c1g # 计算cam2到cam1的变换 RT_c1c2 = RT_c1g @ np.linalg.inv(RT_c2g)6.3 自动标定脚本优化
可以结合机械臂SDK实现全自动数据采集:
# 自动移动机械臂到预设点位 positions = [[x1,y1,z1,rx1,ry1,rz1], ...] for pos in positions: robot.linear_move(pos, 1) # 1mm/s速度移动 time.sleep(1) # 等待稳定 # 自动触发采集...