[ROS 系列学习教程] 建模与仿真 - URDF 建模实践

ROS 系列学习教程(总目录)

本文目录

前文介绍了URDF建模与URDF语法,接下来介绍怎么使用URDF从零构建一个机器人模型并在rviz中显示。

一、机器人结构组成

最终效果如下图:

机器人由如下部分组成:

  • 底盘 * 1
  • 主动轮 * 2
  • 从动轮(脚轮)* 2
  • 激光雷达 * 1
  • RGB相机 * 1

二、新建功能包

为面向零基础的同学,使教学清晰,新建一个功能包用于学习该章节,新建功能包方法见前文,功能包结构如下:

其中,

  • config :存放rviz配置文件
  • launch :存放launch文件
  • meshes :存放模型渲染文件
  • models :存放模型文件
  • src与include :存放源文件和头文件

关于ROS工作空间与功能包的创建,详见 ROS Hello World

三、编写launch文件

如前文所述,rviz显示urdf模型需要先把模型参数注册到参数服务器,然后打开rviz,在rviz中配置好后才能正常显示模型。这些步骤可以手动一步一步完成,也可以编写launch文件快速执行。

另外,对于包含多个link的模型,需要发布link间的jointtf关系,以使rviz可以确定link间的空间位置。ROS提供了 joint_state_publisherrobot_state_publisher 两个功能包来实现linkjointtf关系的发布,如没有安装这两个功能包,可以使用如下命令安装(以noetic为例):

bash 复制代码
sudo apt install ros-noetic-joint-state-publisher
sudo apt install ros-noetic-robot-state-publisher

launch文件内容如下:

xml 复制代码
<launch>
    <param name="robot_description" textfile="$(find simulation_learning)/models/urdf/mbot_base.urdf" />

    <!-- 设置GUI参数,显示关节控制插件 -->
    <param name="use_gui" value="true" />

    <!-- 运行joint_state_publisher节点,发布机器人的关节状态  -->
    <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" />

    <!-- 运行robot_state_publisher节点,发布tf  -->
    <node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher" />

    <!-- 运行rviz可视化界面,并加载配置 -->
    <node name="rviz" pkg="rviz" type="rviz" args="-d $(find simulation_learning)/config/mbot_urdf.rviz" required="true" />
</launch>

关于launch文件的具体描述,见[4.1 launch文件](#4.1 launch文件)

四、创建底盘

models/urdf中创建mbot_base.urdf文件,用于编写urdf模型。

底盘是一个圆柱体,半径为 0.2m,高为0.16m,urdf代码如下:

xml 复制代码
<?xml version="1.0"?>
<robot name="mbot">
    <link name="base_link">
        <visual>
            <origin xyz=" 0 0 0" rpy="0 0 0" />
            <geometry>
                <cylinder length="0.16" radius="0.20" />
            </geometry>
            <material name="yellow">
                <color rgba="1 0.4 0 1" />
            </material>
        </visual>
    </link>
</robot>

运行 launch 文件,结果如下:

这里注意base_link的原点位于圆柱体的几何中心,即有一半圆柱体是位于地面以下的,这一点到最后会解决,目前先把机器人模型搭建起来。

五、添加轮子

轮子是小一点的圆柱体,半径为 0.06m,高为0.025m

现在有了两个实体,底盘和一个轮子,需要使用joint说明他们之间的几何与运动关系,否则rviz解析urdf时会报错。

urdf代码如下:

xml 复制代码
<!-- 底盘实体描述 -->
<link name="base_link">
    <visual>
        <origin xyz=" 0 0 0" rpy="0 0 0" />
        <geometry>
            <cylinder length="0.16" radius="0.20" />
        </geometry>
        <material name="yellow">
            <color rgba="1 0.4 0 1" />
        </material>
    </visual>
</link>

<!-- 左轮与底盘的关节描述 -->
<joint name="left_wheel_joint" type="continuous">
    <origin xyz="0 0.19 -0.05" rpy="0 0 0" />
    <parent link="base_link" />
    <child link="left_wheel_link" />
    <axis xyz="0 1 0" />
</joint>

<!-- 左轮实体描述 -->
<link name="left_wheel_link">
    <visual>
        <origin xyz="0 0 0" rpy="1.5707 0 0" />
        <geometry>
            <cylinder radius="0.06" length="0.025" />
        </geometry>
        <material name="white">
            <color rgba="1 1 1 0.9" />
        </material>
    </visual>
</link>

其中,joint中的type描述了关节类型(机械中的运动副),该类型为旋转类型(机械中的旋转副),origin表示左轮原点相对于底盘原点的空间位姿偏移,parentchild分别表示该关节的父实体和子实体,由于该关节为旋转关节,使用axis定义旋转轴。关于joint的具体描述见 建模与仿真 - URDF 语法介绍

运行 launch 文件,结果如下:

六、添加其他部件

其他部件同理,添加实体与相应的关节描述。

完整的urdf代码如下:

xml 复制代码
<?xml version="1.0"?>
<robot name="mbot">

    <!-- 底盘实体描述 -->
    <link name="base_link">
        <visual>
            <origin xyz=" 0 0 0" rpy="0 0 0" />
            <geometry>
                <cylinder length="0.16" radius="0.20" />
            </geometry>
            <material name="yellow">
                <color rgba="1 0.4 0 1" />
            </material>
        </visual>
    </link>

    <!-- 左轮与底盘的关节描述 -->
    <joint name="left_wheel_joint" type="continuous">
        <origin xyz="0 0.19 -0.05" rpy="0 0 0" />
        <parent link="base_link" />
        <child link="left_wheel_link" />
        <axis xyz="0 1 0" />
    </joint>

    <!-- 左轮实体描述 -->
    <link name="left_wheel_link">
        <visual>
            <origin xyz="0 0 0" rpy="1.5707 0 0" />
            <geometry>
                <cylinder radius="0.06" length="0.025" />
            </geometry>
            <material name="white">
                <color rgba="1 1 1 0.9" />
            </material>
        </visual>
    </link>

    <!-- 右轮与底盘的关节描述 -->
    <joint name="right_wheel_joint" type="continuous">
        <origin xyz="0 -0.19 -0.05" rpy="0 0 0" />
        <parent link="base_link" />
        <child link="right_wheel_link" />
        <axis xyz="0 1 0" />
    </joint>

    <!-- 右轮实体描述 -->
    <link name="right_wheel_link">
        <visual>
            <origin xyz="0 0 0" rpy="1.5707 0 0" />
            <geometry>
                <cylinder radius="0.06" length="0.025" />
            </geometry>
            <material name="white">
                <color rgba="1 1 1 0.9" />
            </material>
        </visual>
    </link>

    <!-- 前脚轮实体描述 -->
    <joint name="front_caster_joint" type="continuous">
        <origin xyz="0.18 0 -0.095" rpy="0 0 0" />
        <parent link="base_link" />
        <child link="front_caster_link" />
        <axis xyz="0 1 0" />
    </joint>

    <!-- 前脚轮和底盘的关节描述 -->
    <link name="front_caster_link">
        <visual>
            <origin xyz="0 0 0" rpy="0 0 0" />
            <geometry>
                <sphere radius="0.015" />
            </geometry>
            <material name="black">
                <color rgba="0 0 0 0.95" />
            </material>
        </visual>
    </link>

    <!-- 后脚轮实体描述 -->
    <joint name="back_caster_joint" type="continuous">
        <origin xyz="-0.18 0 -0.095" rpy="0 0 0" />
        <parent link="base_link" />
        <child link="back_caster_link" />
        <axis xyz="0 1 0" />
    </joint>

    <!-- 后脚轮和底盘的关节描述 -->
    <link name="back_caster_link">
        <visual>
            <origin xyz="0 0 0" rpy="0 0 0" />
            <geometry>
                <sphere radius="0.015" />
            </geometry>
            <material name="black">
                <color rgba="0 0 0 0.95" />
            </material>
        </visual>
    </link>

    <!-- 激光雷达实体描述 -->
    <link name="laser_link">
        <visual>
            <origin xyz=" 0 0 0 " rpy="0 0 0" />
            <geometry>
                <cylinder length="0.05" radius="0.05" />
            </geometry>
            <material name="gray">
                <color rgba="0.25 0.25 0.25 0.95" />
            </material>
        </visual>
    </link>

    <!-- 激光雷达和底盘的关节描述 -->
    <joint name="laser_joint" type="fixed">
        <origin xyz="0 0 0.105" rpy="0 0 0" />
        <parent link="base_link" />
        <child link="laser_link" />
    </joint>

    <!-- 相机实体描述 -->
    <link name="camera_link">
        <visual>
            <origin xyz=" 0 0 0 " rpy="0 1.57 0" />
            <geometry>
                <cylinder radius="0.02" length = "0.05"/>
            </geometry>
            <material name="gray">
                <color rgba="0.25 0.25 0.25 0.95"/>
            </material>
        </visual>
    </link>

    <!-- 相机和底盘的关节描述 -->
    <joint name="camera_joint" type="fixed">
        <origin xyz="0.18 0 0.055" rpy="0 0 0"/>
        <parent link="base_link"/>
        <child link="camera_link"/>
    </joint>

</robot>

运行 launch 文件,结果如下:

七、解决部分实体位于地面以下的问题

首先明确我们的 Fixed Framebase_linkFixed Frame 可以理解为世界坐标系,对于我们的设置就是base_link即当前的世界,rviz默认一切都是从世界中心诞生。

实体位于地面以下,是由于我们设置的其他实体都是以世界中心为参考而又认为地面在轮子下方,但rviz认为地面原点在世界中心,所以看起来和我们的认知有些冲突。

解决办法有多种,但基本都是改变base_link或其他实体与世界中心的关系。

方法一:

比较常见的一种:新增floor_link作为 Fixed Frame 世界坐标系,base_link作为他的子级。这种方法对现有已经完成的模型修改最少。

在urdf文件中新增如下内容即可:

xml 复制代码
<!-- 地面实体描述 -->
<link name="floor_link">
    <visual>
        <origin xyz="0 0 0" rpy="0 0 0" />
        <geometry>
            <!-- 圆柱体高度尽量小,已达到可忽略的精度 -->
            <cylinder length="0.000001" radius="0.20" />
        </geometry>
        <material name="floor">
            <color rgba="1 0.4 0 0" />
        </material>
    </visual>
</link>

<!-- 底盘与地面的关节关系描述 -->
<joint name="base_joint" type="continuous">
    <origin xyz="0 0 0.11" rpy="0 0 0" />
    <parent link="floor_link" />
    <child link="base_link" />
    <axis xyz="0 1 0" />
</joint>

结果如下:

使用rviz查看TF关系如下:

新增的floor_link如图中框选,但这种方法在工程上很难测量base_link的实际位置,所以就有了方法二。

方法二:

floor_link直接命名为base_link,其他实体都以它为基准,但位置描述都要作相应的修改,这种方法对于现有已经完成的模型修改较多,对新工程比较友好,各实体的实际位置也很容易通过测量得到。

修改后的TF树如下,此时base_link位于世界原点,也是机器人底盘原点在地面的投影点。

机器人的外观显示和方法一一样:

相关推荐
边缘计算社区1 小时前
首个!艾灵参编的工业边缘计算国家标准正式发布
大数据·人工智能·边缘计算
游客5201 小时前
opencv中的各种滤波器简介
图像处理·人工智能·python·opencv·计算机视觉
一位小说男主1 小时前
编码器与解码器:从‘乱码’到‘通话’
人工智能·深度学习
深圳南柯电子1 小时前
深圳南柯电子|电子设备EMC测试整改:常见问题与解决方案
人工智能
Kai HVZ1 小时前
《OpenCV计算机视觉》--介绍及基础操作
人工智能·opencv·计算机视觉
biter00881 小时前
opencv(15) OpenCV背景减除器(Background Subtractors)学习
人工智能·opencv·学习
吃个糖糖1 小时前
35 Opencv 亚像素角点检测
人工智能·opencv·计算机视觉
IT古董2 小时前
【漫话机器学习系列】017.大O算法(Big-O Notation)
人工智能·机器学习
凯哥是个大帅比2 小时前
人工智能ACA(五)--深度学习基础
人工智能·深度学习
m0_748232923 小时前
DALL-M:基于大语言模型的上下文感知临床数据增强方法 ,补充
人工智能·语言模型·自然语言处理