眼在手上外参标定保姆级教学---离线手眼标定(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 了。

相关推荐
梦里小白龙6 分钟前
java 通过Minio上传文件
java·开发语言
德育处主任Pro6 分钟前
『NAS』用SSH的方式连上NAS
运维·ssh
名为沙丁鱼的猫7299 分钟前
【MCP 协议层(Protocol layer)详解】:深入分析MCP Python SDK中协议层的实现机制
人工智能·深度学习·神经网络·机器学习·自然语言处理·nlp
m0_5613596710 分钟前
基于C++的机器学习库开发
开发语言·c++·算法
Meaauf11 分钟前
VMware安装中科方德服务器操作系统
运维·服务器·中科方德
bylander12 分钟前
【AI学习】几分钟了解一下Clawdbot
人工智能·智能体·智能体应用
南宫码农12 分钟前
神马影视8.5版本如意伪静态+视频教程
linux·运维·centos
星空露珠17 分钟前
速算24点所有题库公式
开发语言·数据库·算法·游戏·lua
2401_8324027518 分钟前
C++中的类型擦除技术
开发语言·c++·算法
香芋Yu22 分钟前
【机器学习教程】第04章 指数族分布
人工智能·笔记·机器学习