基于ROS的机械臂手眼标定-Aruco使用与相机标定
你好,我是小智。
折腾了一段时间的机械臂的手眼标定,相关资料挺多的,但使用起来都比较复杂,新手一般比较难搞懂。于是想做一个比较简单易懂易用的手眼标定程序。 程序都是Copy的开源代码,所以也开源出来:
开源地址
github: https://github.com/aiotrobot/handeye-calib gitee: https://gitee.com/ohhuo/handeye-calib
作为手眼标定中的重要一环,相机中标定板位姿关系获取直接影响标定精度,我们使用aruco这个开源程序来实现坐标的获取。
#mermaid-svg-5SGAdG2K3ljM4rNb .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .label text{fill:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .node rect,#mermaid-svg-5SGAdG2K3ljM4rNb .node circle,#mermaid-svg-5SGAdG2K3ljM4rNb .node ellipse,#mermaid-svg-5SGAdG2K3ljM4rNb .node polygon,#mermaid-svg-5SGAdG2K3ljM4rNb .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-5SGAdG2K3ljM4rNb .node .label{text-align:center;fill:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .node.clickable{cursor:pointer}#mermaid-svg-5SGAdG2K3ljM4rNb .arrowheadPath{fill:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-5SGAdG2K3ljM4rNb .flowchart-link{stroke:#333;fill:none}#mermaid-svg-5SGAdG2K3ljM4rNb .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-5SGAdG2K3ljM4rNb .edgeLabel rect{opacity:0.9}#mermaid-svg-5SGAdG2K3ljM4rNb .edgeLabel span{color:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-5SGAdG2K3ljM4rNb .cluster text{fill:#333}#mermaid-svg-5SGAdG2K3ljM4rNb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-5SGAdG2K3ljM4rNb .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-5SGAdG2K3ljM4rNb text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-5SGAdG2K3ljM4rNb .actor-line{stroke:grey}#mermaid-svg-5SGAdG2K3ljM4rNb .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-5SGAdG2K3ljM4rNb #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .sequenceNumber{fill:#fff}#mermaid-svg-5SGAdG2K3ljM4rNb #sequencenumber{fill:#333}#mermaid-svg-5SGAdG2K3ljM4rNb #crosshead path{fill:#333;stroke:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .messageText{fill:#333;stroke:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-5SGAdG2K3ljM4rNb .labelText,#mermaid-svg-5SGAdG2K3ljM4rNb .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-5SGAdG2K3ljM4rNb .loopText,#mermaid-svg-5SGAdG2K3ljM4rNb .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-5SGAdG2K3ljM4rNb .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-5SGAdG2K3ljM4rNb .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-5SGAdG2K3ljM4rNb .noteText,#mermaid-svg-5SGAdG2K3ljM4rNb .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-5SGAdG2K3ljM4rNb .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-5SGAdG2K3ljM4rNb .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-5SGAdG2K3ljM4rNb .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-5SGAdG2K3ljM4rNb .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-5SGAdG2K3ljM4rNb .section{stroke:none;opacity:0.2}#mermaid-svg-5SGAdG2K3ljM4rNb .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-5SGAdG2K3ljM4rNb .section2{fill:#fff400}#mermaid-svg-5SGAdG2K3ljM4rNb .section1,#mermaid-svg-5SGAdG2K3ljM4rNb .section3{fill:#fff;opacity:0.2}#mermaid-svg-5SGAdG2K3ljM4rNb .sectionTitle0{fill:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .sectionTitle1{fill:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .sectionTitle2{fill:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .sectionTitle3{fill:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-5SGAdG2K3ljM4rNb .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-5SGAdG2K3ljM4rNb .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-5SGAdG2K3ljM4rNb .grid path{stroke-width:0}#mermaid-svg-5SGAdG2K3ljM4rNb .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-5SGAdG2K3ljM4rNb .task{stroke-width:2}#mermaid-svg-5SGAdG2K3ljM4rNb .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-5SGAdG2K3ljM4rNb .taskText:not([font-size]){font-size:11px}#mermaid-svg-5SGAdG2K3ljM4rNb .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-5SGAdG2K3ljM4rNb .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-5SGAdG2K3ljM4rNb .task.clickable{cursor:pointer}#mermaid-svg-5SGAdG2K3ljM4rNb .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-5SGAdG2K3ljM4rNb .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-5SGAdG2K3ljM4rNb .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-5SGAdG2K3ljM4rNb .taskText0,#mermaid-svg-5SGAdG2K3ljM4rNb .taskText1,#mermaid-svg-5SGAdG2K3ljM4rNb .taskText2,#mermaid-svg-5SGAdG2K3ljM4rNb .taskText3{fill:#fff}#mermaid-svg-5SGAdG2K3ljM4rNb .task0,#mermaid-svg-5SGAdG2K3ljM4rNb .task1,#mermaid-svg-5SGAdG2K3ljM4rNb .task2,#mermaid-svg-5SGAdG2K3ljM4rNb .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-5SGAdG2K3ljM4rNb .taskTextOutside0,#mermaid-svg-5SGAdG2K3ljM4rNb .taskTextOutside2{fill:#000}#mermaid-svg-5SGAdG2K3ljM4rNb .taskTextOutside1,#mermaid-svg-5SGAdG2K3ljM4rNb .taskTextOutside3{fill:#000}#mermaid-svg-5SGAdG2K3ljM4rNb .active0,#mermaid-svg-5SGAdG2K3ljM4rNb .active1,#mermaid-svg-5SGAdG2K3ljM4rNb .active2,#mermaid-svg-5SGAdG2K3ljM4rNb .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-5SGAdG2K3ljM4rNb .activeText0,#mermaid-svg-5SGAdG2K3ljM4rNb .activeText1,#mermaid-svg-5SGAdG2K3ljM4rNb .activeText2,#mermaid-svg-5SGAdG2K3ljM4rNb .activeText3{fill:#000 !important}#mermaid-svg-5SGAdG2K3ljM4rNb .done0,#mermaid-svg-5SGAdG2K3ljM4rNb .done1,#mermaid-svg-5SGAdG2K3ljM4rNb .done2,#mermaid-svg-5SGAdG2K3ljM4rNb .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-5SGAdG2K3ljM4rNb .doneText0,#mermaid-svg-5SGAdG2K3ljM4rNb .doneText1,#mermaid-svg-5SGAdG2K3ljM4rNb .doneText2,#mermaid-svg-5SGAdG2K3ljM4rNb .doneText3{fill:#000 !important}#mermaid-svg-5SGAdG2K3ljM4rNb .crit0,#mermaid-svg-5SGAdG2K3ljM4rNb .crit1,#mermaid-svg-5SGAdG2K3ljM4rNb .crit2,#mermaid-svg-5SGAdG2K3ljM4rNb .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-5SGAdG2K3ljM4rNb .activeCrit0,#mermaid-svg-5SGAdG2K3ljM4rNb .activeCrit1,#mermaid-svg-5SGAdG2K3ljM4rNb .activeCrit2,#mermaid-svg-5SGAdG2K3ljM4rNb .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-5SGAdG2K3ljM4rNb .doneCrit0,#mermaid-svg-5SGAdG2K3ljM4rNb .doneCrit1,#mermaid-svg-5SGAdG2K3ljM4rNb .doneCrit2,#mermaid-svg-5SGAdG2K3ljM4rNb .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-5SGAdG2K3ljM4rNb .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-5SGAdG2K3ljM4rNb .milestoneText{font-style:italic}#mermaid-svg-5SGAdG2K3ljM4rNb .doneCritText0,#mermaid-svg-5SGAdG2K3ljM4rNb .doneCritText1,#mermaid-svg-5SGAdG2K3ljM4rNb .doneCritText2,#mermaid-svg-5SGAdG2K3ljM4rNb .doneCritText3{fill:#000 !important}#mermaid-svg-5SGAdG2K3ljM4rNb .activeCritText0,#mermaid-svg-5SGAdG2K3ljM4rNb .activeCritText1,#mermaid-svg-5SGAdG2K3ljM4rNb .activeCritText2,#mermaid-svg-5SGAdG2K3ljM4rNb .activeCritText3{fill:#000 !important}#mermaid-svg-5SGAdG2K3ljM4rNb .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-5SGAdG2K3ljM4rNb g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-5SGAdG2K3ljM4rNb g.classGroup text .title{font-weight:bolder}#mermaid-svg-5SGAdG2K3ljM4rNb g.clickable{cursor:pointer}#mermaid-svg-5SGAdG2K3ljM4rNb g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-5SGAdG2K3ljM4rNb g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-5SGAdG2K3ljM4rNb .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-5SGAdG2K3ljM4rNb .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-5SGAdG2K3ljM4rNb .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-5SGAdG2K3ljM4rNb .dashed-line{stroke-dasharray:3}#mermaid-svg-5SGAdG2K3ljM4rNb #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-5SGAdG2K3ljM4rNb #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-5SGAdG2K3ljM4rNb #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-5SGAdG2K3ljM4rNb #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-5SGAdG2K3ljM4rNb #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-5SGAdG2K3ljM4rNb #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-5SGAdG2K3ljM4rNb #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-5SGAdG2K3ljM4rNb #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-5SGAdG2K3ljM4rNb .commit-id,#mermaid-svg-5SGAdG2K3ljM4rNb .commit-msg,#mermaid-svg-5SGAdG2K3ljM4rNb .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-5SGAdG2K3ljM4rNb .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-5SGAdG2K3ljM4rNb .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-5SGAdG2K3ljM4rNb g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-5SGAdG2K3ljM4rNb g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-5SGAdG2K3ljM4rNb g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-5SGAdG2K3ljM4rNb g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-5SGAdG2K3ljM4rNb g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-5SGAdG2K3ljM4rNb g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-5SGAdG2K3ljM4rNb .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-5SGAdG2K3ljM4rNb .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-5SGAdG2K3ljM4rNb .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-5SGAdG2K3ljM4rNb .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-5SGAdG2K3ljM4rNb .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-5SGAdG2K3ljM4rNb .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-5SGAdG2K3ljM4rNb .edgeLabel text{fill:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-5SGAdG2K3ljM4rNb .node circle.state-start{fill:black;stroke:black}#mermaid-svg-5SGAdG2K3ljM4rNb .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-5SGAdG2K3ljM4rNb #statediagram-barbEnd{fill:#9370db}#mermaid-svg-5SGAdG2K3ljM4rNb .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-5SGAdG2K3ljM4rNb .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-5SGAdG2K3ljM4rNb .statediagram-state .divider{stroke:#9370db}#mermaid-svg-5SGAdG2K3ljM4rNb .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-5SGAdG2K3ljM4rNb .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-5SGAdG2K3ljM4rNb .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-5SGAdG2K3ljM4rNb .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-5SGAdG2K3ljM4rNb .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-5SGAdG2K3ljM4rNb .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-5SGAdG2K3ljM4rNb .note-edge{stroke-dasharray:5}#mermaid-svg-5SGAdG2K3ljM4rNb .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-5SGAdG2K3ljM4rNb .error-icon{fill:#522}#mermaid-svg-5SGAdG2K3ljM4rNb .error-text{fill:#522;stroke:#522}#mermaid-svg-5SGAdG2K3ljM4rNb .edge-thickness-normal{stroke-width:2px}#mermaid-svg-5SGAdG2K3ljM4rNb .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-5SGAdG2K3ljM4rNb .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-5SGAdG2K3ljM4rNb .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-5SGAdG2K3ljM4rNb .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-5SGAdG2K3ljM4rNb .marker{fill:#333}#mermaid-svg-5SGAdG2K3ljM4rNb .marker.cross{stroke:#333}
:root { --mermaid-font-family: "trebuchet ms", verdana, arial;}
#mermaid-svg-5SGAdG2K3ljM4rNb {
color: rgba(0, 0, 0, 0.75);
font: ;
}
机械臂位姿
手眼标定程序
相机中标定板位姿
末端与相机位姿关系
本教程一共包含五个部分:
- 手眼标定-基础使用
- 手眼标定-JAKA机械臂
- 手眼标定-AUBO机械臂
- 手眼标定-Aruco使用与相机标定
- 手眼标定-注意事项
如果上述程序使用过程中遇到问题,可以参考:
如果你对手眼标定原理感兴趣,可以参考以下文章:
- 机器人手眼标定原理介绍(含详细推导过程)使用Tsai-Lenz算法
- 手眼标定算法TSAI_LENZ,眼在手外python代码实现
- 手眼标定算法Tsai-Lenz代码实现(Python、C++、Matlab)
原理介绍
aruco采用2D图像计算深度信息,需要提前在程序中输入标记物的宽度,根据识别标记物在图像中的像素数量与实际宽度的比值即可计算出深度等信息。
使用指南
1.安装
aruco安装
Kinetic:
sudo apt-get install ros-kinetic-aruco*
Melodic:
sudo apt-get install ros-melodic-aruco*
其他版本
sudo apt-get install ros-版本名称-aruco*
usb-cam安装
Kinetic:
sudo apt-get install ros-kinetic-usb-cam
Melodic:
sudo apt-get install ros-melodic-usb-cam
其他版本
sudo apt-get install ros-melodic-版本名称-cam
2.修改参数
可以直接使用本仓库中handeye-calib所提供的aruco_start_usb_cam.launch
或者aruco_start_realsense_sdk.launch
分别使用realsense和usb相机的驱动来运行,这个视你的相机而定。
需要修改的参数如下:
- camera_info_url 相机标定文件所在位置
- video_device: 设备位置
- image_width: 图片宽度
- image_height: 图片高度
- markerId: 标定板编号,就是你所用的标定板的id,可以通过
在线生成标定板:https://chev.me/arucogen/
进行生成并打印 - markerSize:标定板的宽度 单位m
3.开始运行
source devel/setup.bash
roslaunch handeye-calib aruco_start_usb_cam.launch
4.运行结果

5.位姿订阅
程序运行之后如果检测到标定板的markerId为我们设置的id则会输出标定板在相机中的位姿数据,通过/aruco_signal/pose 话题输出,话题类型为PoseStamped。 可以使用下面的命令订阅输出话题数据。
打开终端
rostopic echo /aruco_signal/pose
6.关于识别误差的校验方法
我们可以利用标记不动,移动机械臂的方法进行检测(假设机械臂是精准的)。由于机械臂与相机末端是固结的,所以当我们让机械臂的末端在空间中移动10mm,那相机在空间中也会移动10mm,同样相机中标记物的位置也应当移动10mm,我们可以通过示教器移动机械臂的方式,观察位姿数据的变化来测量精度。
如果有不明白和有错误的地方可以留言,下期更新使用JAKA机械臂来实现自动标定。
作者介绍:
我是小智,机器人领域资深玩家,现深圳某独脚兽机器人算法工程师一枚
初中学习编程,高中开始学习机器人,大学期间打机器人相关比赛实现月入2W+(比赛奖金)
目前在输出机器人学习指南、论文注解、工作经验,欢迎大家关注小智,一起交流技术,学习机器人 