ROS下UR+Realsense相机进行手眼标定

本文介绍了手眼标定的基本原理,区分了Eye-to-hand和Eye-on-hand两种情况,详细指导了如何使用Aruco_ros和easy_handeye工具包进行手眼标定,包括下载工具、配置参数和实际操作步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

手眼标定主要是用来确定相机坐标系和机械臂坐标系之间的转换关系,从而实现后续的抓取等任务。

手眼标定原理

手眼标定有两个类型:

  1. Eye-to-hand 眼在手外:即相机和机械臂分离,需要确认相机坐标系和机器人的基坐标系之间的转换关系。
  2. Eye-on-hand 眼在手上:即相机在机械臂上,需要确认相机坐标系和机器人的末端之间的转换关系。
    在这里插入图片描述

在眼在手上的标定系统中,四个坐标系根据图示的关系,可以得到等式
b a s e t o o l T t o o l c a m T c a m c a l T = b a s e c a l T _{base}^{tool}T{}_{tool}^{cam}T{}_{cam}^{cal}T = {}_{base}^{cal}T basetoolTtoolcamTcamcalT=basecalT
在标定过程中,标定板固定不动,机械臂末端进行移动,然后通过记录各个位姿之间的变化关系进行求解,由于标定板和基坐标系之间的转换关系在移动过程中保持不变。在两次变化间:
b a s e t o o l 1 T t o o l 1 c a m 1 T c a m 1 c a l T = b a s e c a l T b a s e t o o l 2 T t o o l 2 c a m 2 T c a m 2 c a l T = b a s e c a l T {}_{base}^{tool1}T{}_{tool1}^{cam1}T{}_{cam1}^{cal}T = {}_{base}^{cal}T\\ {}_{base}^{tool2}T{}_{tool2}^{cam2}T{}_{cam2}^{cal}T = {}_{base}^{cal}T basetool1Ttool1cam1Tcam1calT=basecalTbasetool2Ttool2cam2Tcam2calT=basecalT
两式联立,就可以求出相机和机械臂末端之间的转换关系。

功能实现

下载Aruco_ros工具包:

git clone https://github.com/pal-robotics/aruco_ros.git

这个包主要是生成给定大小的 AR 标记,利用该包生成标定纸,生成网址:https://chev.me/arucogen/。

安装easy_handeye:

git clone https://github.com/IFL-CAMP/easy_handeye.git

安装visip:

git clone  https://github.com/lagadic/vision_visp.git

编译完成后,修改easy_handeye下的launch文件,自己创建一个Ur_realsensed45.launch文件:

<?xml version="1.0"?>
<launch>
    <arg name="namespace_prefix" default="auboi5_realsense_handeyecalibration" />

    <arg name="robot_ip" doc="The IP address of the UR5 robot" default="192.168.31.84" />

    <arg name="marker_size" doc="Size of the ArUco marker used, in meters" default="0.12"/>
	<arg name="marker_id" doc="The ID of the ArUco marker used" default="100"/>

	<!--start realsense-->
	<!-- <include file="$(find realsense2_camera)/launch/demo_pointcloud.launch">
		<arg name="filters" value="pointcloud"/>
	</include> -->

    <!-- start ArUco -->
    <node name="aruco_tracker" pkg="aruco_ros" type="single">
        <remap from="/camera_info" to="/camera/color/camera_info" />
        <remap from="/image" to="/camera/color/image_raw" />
        <param name="image_is_rectified" value="true"/>
        <param name="marker_size"        value="$(arg marker_size)"/>
        <param name="marker_id"          value="$(arg marker_id)"/>
        <param name="reference_frame"    value="camera_link"/>
        <param name="camera_frame"       value="camera_color_optical_frame"/>
        <param name="marker_frame"       value="camera_marker" />
    </node>

    <!-- start the robot -->
  <!-- <include file="$(find ur_robot_driver)/launch/ur3_bringup.launch">
        <arg name="robot_ip" value="192.168.31.84" />
    </include>
    <include file="$(find ur3_moveit_config)/launch/moveit_planning_execution.launch">
    </include> -->


    <!-- start easy_handeye -->
    <include file="$(find easy_handeye)/launch/calibrate.launch" >
        <arg name="namespace_prefix" value="$(arg namespace_prefix)" />
        <arg name="eye_on_hand" value="true" />

        <arg name="tracking_base_frame" value="camera_link" />
        <arg name="tracking_marker_frame" value="camera_marker" />
        <arg name="robot_base_frame" value="base_link" />
        <arg name="robot_effector_frame" value="wrist_3_link" />

        <arg name="freehand_robot_movement" value="false" />
        <arg name="robot_velocity_scaling" value="0.5" />
        <arg name="robot_acceleration_scaling" value="0.2" />
    </include>
</launch>

原始的文件是打开相机、机械臂、和手眼标定这三个程序,但是同时打开电脑容易出现卡死的现象。因此我的建议是提前打开相机、机械臂的运行文件。下面重点讲解一下关键的部分:

    <arg name="robot_ip" doc="The IP address of the UR5 robot" default="192.168.31.84" />

    <arg name="marker_size" doc="Size of the ArUco marker used, in meters" default="0.12"/>
	<arg name="marker_id" doc="The ID of the ArUco marker used" default="100"/>

这个需要改成自己的机械臂的网址,同时将标定文件的大小和id号进行修改,修改成和自己相匹配的。

  <include file="$(find easy_handeye)/launch/calibrate.launch" >
        <arg name="namespace_prefix" value="$(arg namespace_prefix)" />
        <arg name="eye_on_hand" value="true" />

        <arg name="tracking_base_frame" value="camera_link" />
        <arg name="tracking_marker_frame" value="camera_marker" />
        <arg name="robot_base_frame" value="base_link" />
        <arg name="robot_effector_frame" value="wrist_3_link" />

        <arg name="freehand_robot_movement" value="false" />
        <arg name="robot_velocity_scaling" value="0.5" />
        <arg name="robot_acceleration_scaling" value="0.2" />
    </include>

<arg name="eye_on_hand" value="true" />这个是选择相机是否在机械臂上,true即为眼在手上,flase即为眼在手外。其余相机和机械臂的坐标系参数可以根据自己的设备进行修改。

具体实现

运行相机:

roslaunch realsense2_camera rs_camera.launch

运行机械臂:

roslaunch ur_modern_driver ur3_bringup.launch limited:=true robot_ip:=192.168.31.84
roslaunch ur3_moveit_config ur3_moveit_planning_execution.launch limited:=true

然后运行手眼标定程序:

roslaunch easy_handeye ur_realsense_calibration.launch

打开后有三个界面:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第一个为Rviz界面,第二个为手眼标定的显示和计算界面,第三个为控制机械臂进行运动的界面。
在这里插入图片描述
在第二个界面中,窗口选择plugins->Visualization->image_View,打开image_view界面。选择/aruco_ros/tracker/result话题,确认能检测到aruco码。然后在第三个窗口中检测可行性,进行运动规划,控制机械臂不断发生位姿改变,从而进行采样。
在这里插入图片描述
机械臂每次进行一次运动,都可以进行采样:Take sample。多次采样完成后,就可以点击Compute进行求解。
最后求出标定结果,并点击Save进行保存,数据保存在$HOME/.ros/easy_handeye 目录下的yaml文件中。
在这里插入图片描述

``` onShareAppMessage: function () { var a = this; var shareData = a.data.shareUrl || {}; // 确保存在默认值 return { title: decodeURIComponent(shareData.title || '默认标题'), // 解码标题 desc: decodeURIComponent(shareData.desc || '默认描述'), path: "/xlj_scity/pages/my/index?wb_url=" + encodeURIComponent(shareData.link || '') + "&vs=3", // 动态拼接路径 imageUrl: encodeURIComponent(shareData.imgUrl) || '' // 设置图片链接,默认为空字符串 }; },```如何获取页面参数里的用于小程序分享页面,帮我修改代码,以下为页面参数:url: "/xlj_scity/pages/share/index?dat=%7B%22title%22%3A%22%E6%95%B4%E7%A7%9F%C2%B7%E5%8D%95%E9%97%B4%E7%A7%9F%E9%87%911450%E5%85%83%2F%E6%9C%88%E5%8D%8A%E5%B9%B4%E8%B5%B7%E7%A7%9F%E5%85%AC%E5%AF%93%E7%9B%B4%E7%A7%9F%E7%B2%BE%E8%A3%85%E5%85%AC%E5%AF%93401%E6%A5%BC%E4%B8%8D%E5%AD%98%E5%9C%A8%E5%BC%95%E6%B5%81%EF%BC%8C%E8%A7%86%E9%A2%91%E5%92%8C%E4%BB%B7%E6%A0%BC%E6%98%AF%E4%B8%80%E8%87%B4%E7%9A%84%EF%BC%8C%E4%B8%8D%E6%98%AF%E4%B8%B2%E4%B8%B2...%22%2C%22desc%22%3A%22%E4%B8%8D%E5%AD%98%E5%9C%A8%E5%BC%95%E6%B5%81%EF%BC%8C%E8%A7%86%E9%A2%91%E5%92%8C%E4%BB%B7%E6%A0%BC%E6%98%AF%E4%B8%80%E8%87%B4%E7%9A%84%EF%BC%8C%E4%B8%8D%E6%98%AF%E4%B8%B2%E4%B8%B2%E6%88%BF%EF%BC%8C%E8%A3%85%E4%BF%AE%E5%B7%B2%E6%9C%89%E4%B8%80%E5%B9%B4%E5%8D%8A%EF%BC%8C%E5%85%A8%E5%85%89%E7%BA%BF%EF%BC%8C%E5%85%A8%E6%96%B0%E5%AE%B6%E7%94%B5%E5%AE%B6%E7%A7%81%EF%BC%8C%E5%AF%86%E7%A0%81%E9%94%81%E5%BC%80%E9%97%A8%EF%BC%8C%E6%A5%BC%E4%B8%8B%E5%8F%AF%E5%81%9C%E6%94%BE%E7%94%B5%E5%8A%A8%E8%BD%A6%EF%BC%8C%E5%B9%B6%E9%85%8D%E5%85%85%E7%94%B5...%22%2C%22link%22%3A%22https%3A%2F%2Fwww.sinizf2014.com%2Fapp%2Findex.php%3Fi%3D7%26c%3Dentry%26is_wxapp%3D1%26do%3Dindex%26m%3Dxlj_scity%26xcs_sid%3D169%26do%3Dpostdetail%26m%3Dxlj_scity%26pid%3D56572%26vs%3D3%22%2C%22imgUrl%22%3A%22https%3A%2F%2Fsinizf2014cglt.oss-cn-zhangjiakou.aliyuncs.com%2Fimages%2Fscity_tmp%2F20240411%2F171283343127.jpg%22%7D"
03-30
以下是对您提供的代码及需求的分析与优化修改,目的是能够正确提取并解码页面参数中的分享数据 (`dat`) 并返回用于小程序分享的功能配置。 --- ### 分析原代码存在的问题 1. **未解析 `dat` 参数** 当前代码中并未从 URL 中读取 `dat` 参数的内容作为基础数据来源。 2. **硬编码默认值可能导致丢失动态数据** 直接使用固定字符串 `'默认标题'` 或其他占位符,在有实际传参的情况下不够灵活。 3. **未适配已加密或复杂格式化后的 JSON 数据流** 提供的示例 URL 包含的是经过 `%xx` 转义编码的 JSON 内容,需额外步骤还原其真实结构。 --- ### 改进版代码 ```javascript onLoad: function (options) { // 页面加载时初始化数据 if (options.dat) { try { const parsedDat = JSON.parse(decodeURIComponent(options.dat)); // 解码 dat 参数 this.setData({ // 更新当前组件实例的数据上下文 shareUrl: parsedDat // 存储解密后得到的实际共享信息 }); } catch (e) { console.error("无法解析 dat 参数:", e); this.setData({ shareUrl: {} // 若出错,则置空避免报异常影响应用正常运转 }); } } else { this.setData({ shareUrl: {} }); // 如果没有提供 dat 则初始状态为空对象防止后续访问时报 undefined 错误 } }, onShareAppMessage: function () { var a = this; var shareData = a.data.shareUrl || {}; // 获取已经设置好的 shareUrl 属性值 return { title: decodeURIComponent(shareData.title || '默认标题'), // 先尝试取出真正的标题文本,若不存在则退而求其次显示通用文案 desc: decodeURIComponent(shareData.desc || '默认描述'), path: "/xlj_scity/pages/my/index?wb_url=" + encodeURIComponent(shareData.link || '') + "&vs=3", // 继续沿用旧方案构造跳转会话所需的目标地址规则 imageUrl: shareData.imgUrl || '' // 图片链接直接映射过来无需再次 urlencode 因为我们假定原始输入已经是合法 URI 地址形式了 }; }, ``` --- ### 关键改动解释 1. **新增 `onLoad` 钩子函数** 在页面首次渲染之前捕获由上一场景携带的所有 GET 查询参数,并从中特别关注名为 `dat` 的那个成员变量。利用标准库方法完成递归式的逆向百分比解码操作后再借助内置 JSON 解析器恢复它原本的模样存储起来待命备用。 2. **增强健壮性检查机制** 加入异常捕捉块以防备非法格式导致程序崩溃的情况发生;同时设定合理的回退策略当且仅当下游依赖方确实缺失关键素材资源的时候才启用兜底措施而非贸然暴露空白界面给用户造成不良体验印象。 3. **统一入口点管理逻辑流向清晰明了便于维护升级迭代扩展现有功能特性** ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木子Robot

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值