眼在手上外参标定保姆级教学---离线手眼标定(vscode + opencv)

手眼标定的本质就是记录多组数据。每组数据需记录两个关键信息:

  • 相机拍摄的标定板图像
  • 拍摄该图像时机械臂末端执行器在基坐标系下的位姿(Xt,Yt,Zt, Rx,Ry,Rz)。然后用这些大概20组数据,通过一些算法去变换出相机与末端执行器之间的变换矩阵

所以也可以不用配置那么复杂的环境,直接在实验室中拖动机械臂本体,然后记录当前位姿,然后拍摄图片就行了,然后再把这些数据送到一个简单的算法执行器求解机械臂末端与相机的变换矩阵里就行了

1. 数据采集过程(手动、离线)

  • 硬件准备 :
    • 相机(您的 RealSense D435)固定在机械臂末端执行器上,确保稳定。
    • 标定板(推荐 9x6 棋盘格,每格 25-30mm 方块,打印在平坦亚光纸上)。固定在工作台上,不动。
    • 机械臂(Elfin E03)连接电脑,通过教导器或控制器软件记录位姿。
  • 采集步骤 :
    1. 启动机械臂控制器软件(Han's Robot 教导器或 PC 软件),启用拖动模式(freedrive 或 teach mode)。
    2. 手动拖动机械臂到一个位姿,让标定板完整出现在相机视野中(倾斜 30-45 度,避免正面,覆盖不同距离和角度)。
    3. 记录位姿:用控制器软件读取末端执行器在基坐标系下的位姿 (Xt, Yt, Zt, Rx, Ry, Rz),保存到 TXT 文件或 Excel(如 pose1: 100, 200, 300, 0, 0, 90)。
    4. 立即拍摄图像:用 RealSense Viewer 或简单 Python 脚本捕获 RGB 图像,保存为 JPG(如 image1.jpg)。
    5. 重复 15-20 次:每个位姿变化显著(平移 > 50mm,旋转 > 30°),避免共面位姿(导致奇异矩阵)。
  • 数据格式 :
    • 图像文件夹:images/image1.jpg, image2.jpg...
    • 位姿文件:poses.txt,每行一组位姿,如 "100 200 300 0 0 90" (Xt Yt Zt Rx Ry Rz, 单位 mm/度)。

2. 计算 X 矩阵(用简单 Python 脚本)

用 OpenCV 的 cv2.calibrateHandEye 函数计算(基于 Tsai-Lenz 方法)。脚本输入:图像 + 位姿,输出 X。

  • 安装依赖 (在本地 Python 3 环境):

    Bash

    复制代码
    pip install opencv-python numpy
  • 完整 Python 脚本 (复制到文件 hand_eye_calib.py):

    Python

    python 复制代码
    import cv2
    import numpy as np
    from math import radians
    
    # 标定板参数(调整为您的)
    BOARD_SIZE = (9, 6)  # 棋盘格内角点数(宽x高)
    SQUARE_SIZE = 0.025  # 每格大小 (m)
    
    # 1. 加载图像和位姿数据
    def load_data(image_folder, pose_file):
        images = []  # 图像列表
        poses = []  # 位姿列表
        for i in range(1, 21):  # 假设 20 组
            img = cv2.imread(f"{image_folder}/image{i}.jpg")
            if img is None:
                print(f"Image {i} not found!")
                continue
            images.append(img)
        
            # 读取位姿 (Xt Yt Zt Rx Ry Rz)
            with open(pose_file, 'r') as f:
                lines = f.readlines()
                pose_line = lines[i-1].strip().split()
                t = np.array([float(pose_line[0]), float(pose_line[1]), float(pose_line[2])]) / 1000  # mm to m
                r = np.array([radians(float(pose_line[3])), radians(float(pose_line[4])), radians(float(pose_line[5]))])  # 度 to 弧度
                R = cv2.Rodrigues(r)[0]  # 罗德里格斯公式转旋转矩阵
                poses.append((R, t))
        return images, poses
    
    # 2. 从图像检测标定板角点,计算相机到标定板的位姿
    def get_camera_to_board_poses(images):
        obj_points = np.zeros((BOARD_SIZE[0] * BOARD_SIZE[1], 3), np.float32)
        obj_points[:, :2] = np.mgrid[0:BOARD_SIZE[0], 0:BOARD_SIZE[1]].T.reshape(-1, 2) * SQUARE_SIZE
        
        R_cam_to_board = []  # 旋转矩阵列表
        t_cam_to_board = []  # 平移向量列表
        
        # 相机内参(从您的自定义 YAML 或 RealSense 默认)
        camera_matrix = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]])  # 替换 fx,fy,cx,cy
        dist_coeffs = np.array([k1, k2, p1, p2, k3])  # 畸变系数
        
        for img in images:
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            ret, corners = cv2.findChessboardCorners(gray, BOARD_SIZE, None)
            if ret:
                corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria=(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001))
                ret, rvec, tvec = cv2.solvePnP(obj_points, corners, camera_matrix, dist_coeffs)
                R = cv2.Rodrigues(rvec)[0]  # 旋转矩阵
                R_cam_to_board.append(R)
                t_cam_to_board.append(tvec)
            else:
                print("Chessboard not found in image!")
        return R_cam_to_board, t_cam_to_board
    
    # 3. 计算 X (相机相对末端执行器的变换)
    def compute_X(R_gripper_to_base, t_gripper_to_base, R_cam_to_board, t_cam_to_board):
        # 注意:对于 Eye-in-Hand,A = gripper_to_base, B = cam_to_board 的逆(因为 B 是 board_to_cam 的逆)
        R_board_to_cam = [R.T for R in R_cam_to_board]
        t_board_to_cam = [-np.dot(R.T, t) for R, t in zip(R_cam_to_board, t_cam_to_board)]
        
        R_X, t_X = cv2.calibrateHandEye(R_gripper_to_base, t_gripper_to_base, R_board_to_cam, t_board_to_cam, method=cv2.CALIB_HAND_EYE_TSAI)
        X = np.eye(4)
        X[:3, :3] = R_X
        X[:3, 3] = t_X.flatten()
        return X
    
    # 主函数
    if __name__ == "__main__":
        images, poses = load_data("images", "poses.txt")
        R_gripper_to_base, t_gripper_to_base = zip(*poses)
        R_cam_to_board, t_cam_to_board = get_camera_to_board_poses(images)
        
        X = compute_X(R_gripper_to_base, t_gripper_to_base, R_cam_to_board, t_cam_to_board)
        print("Camera to End-Effector Transformation Matrix X:")
        print(X)
  • 如何运行 :

    1. 替换脚本中的 camera_matrix 和 dist_coeffs 为您的自定义内参值。
    2. 运行 python hand_eye_calib.py。

如何导出机械臂位姿数据?如何导出相机的拍摄标定板的图片?

大族(Han's Robot)Elfin E03 的示教器(teach pendant)是物理硬件,通常随机器人一起发货(手持平板式设备)。

如何导出机械臂位姿数据(Xt, Yt, Zt, Rx, Ry, Rz)
  • 方法 1: 用物理示教器查看和记录(最简单,手动)
    • 连接示教器到控制柜(专用接口)。
    • 开机后,在示教器界面进入 "坐标" 或 "当前位置" 菜单。
    • 显示末端位姿(通常 XYZWPR 格式,WPR 是欧拉角)。
    • 手动拖动臂到标定位姿,记录屏幕显示的数值到笔记本或 Excel(每组一行)。

如何导出相机拍摄标定板的图片数据(RealSense D435)

最简单方式:用 Intel RealSense Viewer(免费官方工具)捕获图像。

下载 RealSense Viewer
如何捕获标定板图片
  1. 连接 D435 到电脑 USB 3.0 端口。
  2. 打开 RealSense Viewer。
  3. 左侧选择您的 D435 设备。
  4. 开启 Color 流(RGB 图像)和 Depth 流(可选)。
  5. 把标定板放进视野(完整、清晰、无反光)。
  6. 点击右上角 Snapshot 按钮(相机图标):
    • 保存 RGB 图像为 PNG/JPG。
    • 同时可保存 Depth 数据。
  7. 重复移动机械臂,拍摄 15-20 张不同位姿的标定板图像。
  8. 保存到文件夹(如 images/calib1.png)

这样就有图像 + 手动记录位姿数据,可以离线计算 X 了。

相关推荐
AI人工智能+11 小时前
行驶证识别技术融合计算机视觉与自然语言处理,实现对模糊、倾斜、反光等复杂场景下行驶证的高精度结构化数据提取
人工智能·深度学习·自然语言处理·ocr·行驶证识别
我叫洋洋11 小时前
涂鸦 T5+ hashcat 自动化破解 WPA2:从单缺 M2 到全自动值守的踩坑实录 破解wifi 密码
运维·单片机·自动化
Cc.Y13 小时前
Java零基础入门:字符串深度掌握——从基础API到StringBuilder性能优化
java·开发语言·性能优化
weixin_7503302314 小时前
AI获客工具选型:从技术架构看中小企业效率提升方案
大数据·人工智能·架构·ai获客
mpp00715 小时前
36氪《2026 中国 AI Agent 行业发展报告》:当 Agent 进入「交付」主战场
大数据·人工智能
吃饱了得干活17 小时前
Agent 的决策与规划:ReAct、Plan-and-Execute、Reflexion 与 Tree of Thoughts
人工智能·llm·agent
阿明617 小时前
小白入门机器学习基础【AI】
人工智能·机器学习
java资料站17 小时前
十一、评估测试
开发语言·人工智能·python
东方佑17 小时前
从《事件发生与智能》的框架看中国古代命理学
人工智能·深度学习·语言模型·自然语言处理·架构
天远API18 小时前
零信任架构实战:基于天远运营商三要素简版V即时版查询构建自动化司乘安全绑定网关
人工智能·安全·架构·自动化