ROS机器人入门:机器人系统仿真【学习记录】——1

        最近闲来无事,重温了一下ROS方面的相关知识。先前的学习都是一带而过,发现差不多都忘了,学习的不够深入。因此,在重温的同时,于先前写下了2篇ROS架构与通信机制的博客:

对应的博客的链接为:

ROS架构的学习【Noetic】-CSDN博客

ROS实操:通信机制的实现-CSDN博客

        以上的这些博客可以算是一些基础入门心得与学习记录,下面我们开始新的历程:ROS机器人系统仿真

~~~路漫漫其修远兮,吾将上下而求索~~~

1. 概述:

机器人系统仿真:是通过计算机对实体机器人系统进行模拟的技术,在 ROS 中,仿真实现涉及的内容主要有三:对机器人建模(URDF)、创建仿真环境(Gazebo)以及感知环境(Rviz)等系统性实现。

URDF是 Unified Robot Description Format 的首字母缩写,直译为统一(标准化)机器人描述格式,可以以一种 XML 的方式描述机器人的部分结构,比如底盘、摄像头、激光雷达、机械臂以及不同关节的自由度, 该文件可以被 C++ 内置的解释器转换成可视化的机器人模型,是 ROS 中实现机器人仿真的重要组件。

RViz 是 ROS Visualization Tool 的首字母缩写,直译为ROS的三维可视化工具。它的主要目的是以三维方式显示ROS消息,可以将数据进行可视化表达。例如:可以显示机器人模型,可以无需编程就能表达激光测距仪(LRF)传感器中的传感 器到障碍物的距离,RealSense、Kinect或Xtion等三维距离传感器的点云数据(PCD, Point Cloud Data),从相机获取的图像值等。

运行使用命令rvizrosrun rviz rviz

Gazebo是一款3D动态模拟器,用于显示机器人模型并创建仿真环境,能够在复杂的室内和室外环境中准确有效地模拟机器人。与游戏引擎提供高保真度的视觉模拟类似,Gazebo提供高保真度的物理模拟,其提供一整套传感器模型,以及对用户和程序非常友好的交互方式。

运行使用命令gazeborosrun gazebo_ros gazebo

-----------------------------------------------------------------------

机器人的系统仿真是一种集成实现,主要包含三部分:

  • URDF 用于创建机器人模型

  • Gzebo 用于搭建仿真环境

  • Rviz 图形化的显示机器人各种传感器感知到的环境信息

三者应用中,只是创建 URDF 意义不大,一般需要结合 Gazebo 或 Rviz 使用,在 Gazebo 或 Rviz 中可以将 URDF 文件解析为图形化的机器人模型,一般的使用组合为:

  • 如果非仿真环境,那么使用 URDF 结合 Rviz 直接显示感知的真实环境信息

  • 如果是仿真环境,那么需要使用 URDF 结合 Gazebo 搭建仿真环境,并结合 Rviz 显示感知的虚拟环境信息

2. URDF集成Rviz基本流程

补充:这一部分我先前学习过【古月居】的相关课程,具体学习和心得的博客记录补充如下:

ROS建模:一起从零手写URDF模型:https://blog.csdn.net/qq_54900679/article/details/135726348?spm=1001.2014.3001.5501
ROS建模:从零手写机械臂的URDF模型:https://blog.csdn.net/qq_54900679/article/details/136872351?spm=1001.2014.3001.5501
一起来从Solidworks中导出URDF模型:https://blog.csdn.net/qq_54900679/article/details/137279115?spm=1001.2014.3001.5501
补充:一起来从Solidworks中导出机械臂的URDF模型:https://blog.csdn.net/qq_54900679/article/details/137342680?spm=1001.2014.3001.5501
关于在Ubuntu20.04(ROS1 noetic)中使用catkin_make编译时发生的与pyhton版本不兼容的问题解决办法:https://blog.csdn.net/qq_54900679/article/details/135780142?spm=1001.2014.3001.5501
Ubuntu20.04 [Ros Noetic]版本——在catkin_make编译时出现报错的解决方案:

https://blog.csdn.net/qq_54900679/article/details/138295152

实现流程:

  1. 准备:新建功能包,导入依赖

  2. 核心:编写 urdf 文件

  3. 核心:在 launch 文件集成 URDF 与 Rviz

  4. 在 Rviz 中显示机器人模型

1.创建功能包,导入依赖

创建一个新的功能包,名称自定义,导入依赖包:urdfxacro

在当前功能包下,再新建几个目录:

urdf: 存储 urdf 文件的目录

meshes:机器人模型渲染文件(暂不使用)

config: 配置文件

launch: 存储 launch 启动文件

下面进行实操演示,首先在自己的工作空间的src中新建一个功能包:

输入包名:urdf01_rviz ;导入依赖:urdf xacro 后回车,出现如下显示:

继续在urdf01_rviz文件夹当中创建一些文件夹:config、launch、urdf、meshes

在urdf文件夹当中再创建2个文件夹:urdf、xacro

其中urdf是用来存放urdf文件,xacro是存放urdf升级文件

 2.编写 URDF 文件

新建一个子级文件夹:urdf(可选),文件夹中添加一个.urdf文件(domo01_helloworld.urdf),复制如下内容:

<robot name="mycar">
    <link name="base_link">
        <visual>
            <geometry>
                <box size="0.5 0.2 0.1" />
            </geometry>
        </visual>
    </link>
</robot>
3.在 launch 文件中集成 URDF 与 Rviz

        在launch目录下,新建一个 launch 文件(demo01_helloworld.launch),该 launch 文件需要启动 Rviz,并导入 urdf 文件,Rviz 启动后可以自动载入解析urdf文件,并显示机器人模型,核心问题:如何导入 urdf 文件? 在 ROS 中,可以将 urdf 文件的路径设置到参数服务器,使用的参数名是:robot_description,示例代码如下:

<launch>

    <!-- 设置参数 -->
    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo01_helloworld.urdf" />

    <!-- 启动 rviz -->
    <node pkg="rviz" type="rviz" name="rviz" />

</launch>

具体的界面截图如下:

保存文件后,在VScode编辑器内可以source一下:

source ./devel/setup.bash

然后再执行:

roslaunch urdf01_rviz demo01_helloworld.launch

 可以发现rviz启动成功:

但是没有模型显示,需要做一下如下的配置:

可以发现在rviz中显示模型成功!

如果想将这个模型保存,可以保存到config配置文件夹中(点击File-->Save Config As)

保存到工作空间中的当前项目中去,命名自定义即可:

可以发现多出了一个文件(我这里命名为show_mycar)

这样下次再启动launch文件时,只需要将这个配置读进来即可

然后,launch文件中 Rviz 的启动配置添加参数:args,值设置为-d 配置文件路径

优化后的launch文件如下图所示,红色方框为后续添加内容:

再次启动launch文件时,就可以包含之前的组件配置了,使用更方便快捷。

3. URDF语法详解

        URDF 文件是一个标准的 XML 文件,在 ROS 中预定义了一系列的标签用于描述机器人模型,机器人模型可能较为复杂,但是 ROS 的 URDF 中机器人的组成却是较为简单,可以主要简化为两部分:连杆(link标签) 与 关节(joint标签),接下来我们就通过案例了解一下 URDF 中的不同标签:

  • robot 根标签,类似于 launch文件中的launch标签
  • link 连杆标签
  • joint 关节标签
  • gazebo 集成gazebo需要使用的标签

        关于gazebo标签,后期在使用 gazebo 仿真时,才需要使用到,用于配置仿真环境所需参数,比如: 机器人材料属性、gazebo插件等,但是该标签不是机器人模型必须的,只有在仿真时才需设置。

robot:urdf 中为了保证 xml 语法的完整性,使用了robot标签作为根标签,所有的 link 和 joint 以及其他标签都必须包含在 robot 标签内,在该标签内可以通过 name 属性设置机器人模型的名称。
1.属性

name: 指定机器人模型的名称

2.子标签

其他标签都是子级标签

1.属性
  • name ---> 为连杆命名
2.子标签
  • visual ---> 描述外观(对应的数据是可视的)

    • geometry 设置连杆的形状

      • 标签1: box(盒状)

        • 属性:size=长(x) 宽(y) 高(z)
      • 标签2: cylinder(圆柱)

        • 属性:radius=半径 length=高度
      • 标签3: sphere(球体)

        • 属性:radius=半径
      • 标签4: mesh(为连杆添加皮肤)

        • 属性: filename=资源路径(格式:package://<packagename>/<path>/文件)
    • origin 设置偏移量与倾斜弧度

      • 属性1: xyz=x偏移 y便宜 z偏移

      • 属性2: rpy=x翻滚 y俯仰 z偏航 (单位是弧度)

    • metrial 设置材料属性(颜色)

      • 属性: name

      • 标签: color

        • 属性: rgba=红绿蓝权重值与透明度 (每个权重值以及透明度取值[0,1])
  • collision ---> 连杆的碰撞属性

  • Inertial ---> 连杆的惯性矩阵

在此,只演示visual使用。

3.案例

分别生成长方体、圆柱与球体的机器人部件。给出以下的urdf代码:

<!-- 需求:设置不同形状的机器人部件 -->
<robot name="mycar">

    <link name="base_link">
        <visual>
            <!-- 形状 -->
            <geometry>
                <!-- 长方体的长宽高 -->
                <!-- <box size="0.5 0.3 0.1" /> -->
                <!-- 圆柱,半径和长度 -->
                <cylinder radius="0.5" length="0.1" />
                <!-- 球体,半径-->
                <!-- <sphere radius="0.3" /> -->
            </geometry>
            <!-- xyz坐标 rpy翻滚俯仰与偏航角度(3.14=180度 1.57=90度) -->
            <origin xyz="0 0 0" rpy="0 0 0" />
            <!-- 颜色: r=red g=green b=blue a=alpha -->
            <material name="black">
                <color rgba="0.7 0.5 0 0.5" />
            </material>
        </visual>
    </link>

</robot>

下面进行案例演示,将上面的urdf代码复制到demo02_link.urdf文件当中,然后再新建一个launch文件(demo02_link.launch):

<launch>

    <!-- 设置参数 -->
    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo02_link.urdf" />

    <!-- 启动 rviz -->
    <node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />

</launch>

保存文件后,在VScode编辑器内可以source一下:

source ./devel/setup.bash

然后再执行:

roslaunch urdf01_rviz demo02_link.launch

 可以发现圆柱成功地显示了出来:

下面演示如何加载meshes,需要下载素材链接,找一个文件目录在终端执行:

git clone https://github.com/zx595306686/sim_demo.git

将其中的2个STL文件复制到meshes文件夹当中,并在urdf文件的代码中添加如下一行:

<mesh filename="package://urdf01_rviz/meshes/autolabor_mini.stl"/>

保存文件后,然后再执行launch指令:

roslaunch urdf01_rviz demo02_link.launch

可以发现,我们自己所添加的模型显示了出来:

但是,这个机器人模型的显示姿态好像不对,我们如何将它的显示摆正呢?下面我们给出解决方法:观察xyz坐标系,默认车头沿着x轴的正方向,只需绕x轴逆时针转90°即可:

修改urdf代码中的origin部分,先将 r 设置为1.57:

<origin xyz="0 0 0" rpy="1.57 0 0" />

保存文件后,然后再执行launch指令,可以发现,小车摆正了,但车头还没朝向x轴正方向。紧接着,我们进行第二步,绕z轴转动90°:

修改urdf代码中的origin部分,将rpy中的 y 设置为1.57:

<origin xyz="0 0 0" rpy="1.57 0 1.57" />

保存文件后,然后再执行launch指令,可以发现小车摆正了姿态:

于是问题得到了解决!

下面继续来介绍关节:

joint:urdf 中的 joint 标签用于描述机器人关节的运动学和动力学属性,还可以指定关节运动的安全极限,机器人的两个部件(分别称之为 parent link 与 child link)以"关节"的形式相连接,不同的关节有不同的运动形式: 旋转、滑动、固定、旋转速度、旋转角度限制....,比如:安装在底座上的轮子可以360度旋转,而摄像头则可能是完全固定在底座上。

注意:joint标签对应的数据在模型中是不可见的

1.属性
  • name ---> 为关节命名

  • type ---> 关节运动形式

    • continuous: 旋转关节,可以绕单轴无限旋转

    • revolute: 旋转关节,类似于 continues,但是有旋转角度限制

    • prismatic: 滑动关节,沿某一轴线移动的关节,有位置极限

    • planer: 平面关节,允许在平面正交方向上平移或旋转

    • floating: 浮动关节,允许进行平移、旋转运动

    • fixed: 固定关节,不允许运动的特殊关节

2.子标签
  • parent(必需的)

    parent link的名字是一个强制的属性:

    • link:父级连杆的名字,是这个link在机器人结构树中的名字。
  • child(必需的)

    child link的名字是一个强制的属性:

    • link:子级连杆的名字,是这个link在机器人结构树中的名字。
  • origin

    • 属性: xyz=各轴线上的偏移量 rpy=各轴线上的偏移弧度。
  • axis

    • 属性: xyz用于设置围绕哪个关节轴运动。
3.案例

        创建机器人模型,底盘为长方体,在长方体的前面添加一摄像头,摄像头可以沿着 Z 轴 360 度旋转。新建一个urdf文件:demo03_joint.urdf,并给出相关的urdf文件代码:

<!-- 
    需求: 创建机器人模型,底盘为长方体,
         在长方体的前面添加一摄像头,
         摄像头可以沿着 Z 轴 360 度旋转

 -->
<robot name="mycar">
    <!-- 底盘 -->
    <link name="base_link">
        <visual>
            <geometry>
                <box size="0.5 0.2 0.1" />
            </geometry>
            <origin xyz="0 0 0" rpy="0 0 0" />
            <material name="blue">
                <color rgba="0 0 1.0 0.5" />
            </material>
        </visual>
    </link>

    <!-- 摄像头 -->
    <link name="camera">
        <visual>
            <geometry>
                <box size="0.02 0.05 0.05" />
            </geometry>
            <origin xyz="0 0 0" rpy="0 0 0" />
            <material name="red">
                <color rgba="1 0 0 0.5" />
            </material>
        </visual>
    </link>

    <!-- 关节 -->
    <joint name="camera2baselink" type="continuous">
        <parent link="base_link"/>
        <child link="camera" />
        <!-- 需要计算两个 link 的物理中心之间的偏移量 -->
        <origin xyz="0.2 0 0.075" rpy="0 0 0" />
        <axis xyz="0 0 1" />
    </joint>

</robot>

接着给出相关的launch文件(demo03_joint.launch)代码:

<launch>

    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo03_joint.urdf" />
    <node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" /> 

    <!-- 添加关节状态发布节点 -->
    <node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />
    <!-- 添加机器人状态发布节点 -->
    <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />
    <!-- 可选:用于控制关节运动的节点 -->
    <node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />

</launch>

保存文件后,然后再执行launch指令:

roslaunch urdf01_rviz demo03_joint.launch

可以发现我们的模型显示成功,拖动关节滑动条可以观察红色小方块产生了旋转:

base_footprint优化urdf

        前面实现的机器人模型是半沉到地下的,因为默认情况下: 底盘的中心点位于地图原点上,所以会导致这种情况产生,可以使用的优化策略,将初始 link 设置为一个尺寸极小的 link(比如半径为 0.001m 的球体,或边长为 0.001m 的立方体),然后再在初始 link 上添加底盘等刚体,这样实现,虽然仍然存在初始link半沉的现象,但是基本可以忽略了。这个初始 link 一般称之为 base_footprint

下面给出优化后的urdf代码(demo04_base_footprint.urdf):

<!--

    使用 base_footprint 优化

-->
<robot name="mycar">
    <!-- 设置一个原点(机器人中心点的投影) -->
    <link name="base_footprint">
        <visual>
            <geometry>
                <sphere radius="0.001" />
            </geometry>
        </visual>
    </link>

    <!-- 添加底盘 -->
    <link name="base_link">
        <visual>
            <geometry>
                <box size="0.5 0.2 0.1" />
            </geometry>
            <origin xyz="0 0 0" rpy="0 0 0" />
            <material name="blue">
                <color rgba="0 0 1.0 0.5" />
            </material>
        </visual>
    </link>

    <!-- 底盘与原点连接的关节 -->
    <joint name="base_link2base_footprint" type="fixed">
        <parent link="base_footprint" />
        <child link="base_link" />
        <origin xyz="0 0 0.05" />
    </joint>

    <!-- 添加摄像头 -->
    <link name="camera">
        <visual>
            <geometry>
                <box size="0.02 0.05 0.05" />
            </geometry>
            <origin xyz="0 0 0" rpy="0 0 0" />
            <material name="red">
                <color rgba="1 0 0 0.5" />
            </material>
        </visual>
    </link>
    <!-- 关节 -->
    <joint name="camera2baselink" type="continuous">
        <parent link="base_link"/>
        <child link="camera" />
        <origin xyz="0.2 0 0.075" rpy="0 0 0" />
        <axis xyz="0 0 1" />
    </joint>

</robot>

接着给出相关的launch文件(demo04_base_footprint.launch)代码:

<launch>

    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo04_base_footprint.urdf" />
    <node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" /> 

    <!-- 添加关节状态发布节点 -->
    <node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />
    <!-- 添加机器人状态发布节点 -->
    <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />
    <!-- 可选:用于控制关节运动的节点 -->
    <node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />

</launch>

保存文件后,然后再执行launch指令:

roslaunch urdf01_rviz demo04_base_footprint.launch

再将 Fixed Frame 修改为 base_footprint 后,可以发现机器人模型的半沉问题得到了解决:

URDF练习

        在上述的URDF语法详解学完之后,我们可以来次练习:创建一个四轮圆柱状机器人模型,机器人参数如下,底盘为圆柱状,半径 10cm,高 8cm,四轮由两个驱动轮和两个万向支撑轮组成,两个驱动轮半径为 3.25cm, 轮胎宽度1.5cm,两个万向轮为球状,半径 0.75cm,底盘离地间距为 1.5cm(与万向轮直径一致)

实现流程:

创建机器人模型可以分步骤实现

  1. 新建 urdf 文件,并与 launch 文件集成

  2. 搭建底盘

  3. 在底盘上添加两个驱动轮

  4. 在底盘上添加两个万向轮

下面给出对应的urdf 文件如下(demo05_test.urdf):

<robot name="mycar">
    <!-- 设置 base_footprint  -->
    <link name="base_footprint">
        <visual>
            <geometry>
                <sphere radius="0.001" />
            </geometry>
        </visual>
    </link>

    <!-- 添加底盘 -->
    <!-- 
        参数
            形状:圆柱 
            半径:10     cm 
            高度:8      cm 
            离地:1.5    cm

    -->
    <link name="base_link">
        <visual>
            <geometry>
                <cylinder radius="0.1" length="0.08" />
            </geometry>
            <origin xyz="0 0 0" rpy="0 0 0" />
            <material name="yellow">
                <color rgba="0.8 0.3 0.1 0.5" />
            </material>
        </visual>
    </link>

    <joint name="base_link2base_footprint" type="fixed">
        <parent link="base_footprint" />
        <child link="base_link"/>
        <origin xyz="0 0 0.055" />
    </joint>

    <!-- 添加驱动轮 -->
    <!--
        驱动轮是侧翻的圆柱
        参数
            半径: 3.25 cm
            宽度: 1.5  cm
            颜色: 黑色
        关节设置:
            x = 0
            y = 底盘的半径 + 轮胎宽度 / 2
            z = 离地间距 + 底盘长度 / 2 - 轮胎半径 = 1.5 + 4 - 3.25 = 2.25(cm)
            axis = 0 1 0
    -->
    <link name="left_wheel">
        <visual>
            <geometry>
                <cylinder radius="0.0325" length="0.015" />
            </geometry>
            <origin xyz="0 0 0" rpy="1.5705 0 0" />
            <material name="black">
                <color rgba="0.0 0.0 0.0 1.0" />
            </material>
        </visual>

    </link>

    <joint name="left_wheel2base_link" type="continuous">
        <parent link="base_link" />
        <child link="left_wheel" />
        <origin xyz="0 0.1 -0.0225" />
        <axis xyz="0 1 0" />
    </joint>


    <link name="right_wheel">
        <visual>
            <geometry>
                <cylinder radius="0.0325" length="0.015" />
            </geometry>
            <origin xyz="0 0 0" rpy="1.5705 0 0" />
            <material name="black">
                <color rgba="0.0 0.0 0.0 1.0" />
            </material>
        </visual>

    </link>

    <joint name="right_wheel2base_link" type="continuous">
        <parent link="base_link" />
        <child link="right_wheel" />
        <origin xyz="0 -0.1 -0.0225" />
        <axis xyz="0 1 0" />
    </joint>

    <!-- 添加万向轮(支撑轮) -->
    <!--
        参数
            形状: 球体
            半径: 0.75 cm
            颜色: 黑色

        关节设置:
            x = 自定义(底盘半径 - 万向轮半径) = 0.1 - 0.0075 = 0.0925(cm)
            y = 0
            z = 底盘长度 / 2 + 离地间距 / 2 = 0.08 / 2 + 0.015 / 2 = 0.0475 
            axis= 1 1 1

    -->
    <link name="front_wheel">
        <visual>
            <geometry>
                <sphere radius="0.0075" />
            </geometry>
            <origin xyz="0 0 0" rpy="0 0 0" />
            <material name="black">
                <color rgba="0.0 0.0 0.0 1.0" />
            </material>
        </visual>
    </link>

    <joint name="front_wheel2base_link" type="continuous">
        <parent link="base_link" />
        <child link="front_wheel" />
        <origin xyz="0.0925 0 -0.0475" />
        <axis xyz="1 1 1" />
    </joint>

    <link name="back_wheel">
        <visual>
            <geometry>
                <sphere radius="0.0075" />
            </geometry>
            <origin xyz="0 0 0" rpy="0 0 0" />
            <material name="black">
                <color rgba="0.0 0.0 0.0 1.0" />
            </material>
        </visual>
    </link>

    <joint name="back_wheel2base_link" type="continuous">
        <parent link="base_link" />
        <child link="back_wheel" />
        <origin xyz="-0.0925 0 -0.0475" />
        <axis xyz="1 1 1" />
    </joint>

</robot>

给出对应的launch文件如下(demo05_test.launch):

<launch>
    <!-- 将 urdf 文件内容设置进参数服务器 -->
    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo05_test.urdf" />

    <!-- 启动 rivz -->
    <node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />

    <!-- 启动机器人状态和关节状态发布节点 -->
    <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />
    <node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />

    <!-- 启动图形化的控制关节运动节点 -->
    <node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />

</launch>

保存文件后,然后再执行launch指令:

roslaunch urdf01_rviz demo05_test.launch

可以发现四轮圆柱状机器人模型显示成功:

URDF工具

这一节的最后,我们来看一下URDF工具:

在 ROS 中,提供了一些工具来方便 URDF 文件的编写,比如:

  • check_urdf命令可以检查复杂的 urdf 文件是否存在语法问题

  • urdf_to_graphiz命令可以查看 urdf 模型结构,显示不同 link 的层级关系

当然,要使用工具之前,首先需要安装,安装命令:sudo apt install liburdfdom-tools

1.check_urdf 语法检查

进入urdf文件所属目录,调用:check_urdf urdf文件,如果不抛出异常,说明文件合法,否则非法

以demo05_test.urdf文件为例子,在urdf的文件夹中开启一个终端并执行:

check_urdf demo05_test.urdf

可以发现终端显示如下输出,说明文件合法:

2.urdf_to_graphiz 结构查看

进入urdf文件所属目录,调用:urdf_to_graphiz urdf文件,当前目录下会生成 pdf 文件

以demo05_test.urdf文件为例子,在urdf的文件夹中开启一个终端并执行:

urdf_to_graphiz demo05_test.urdf

可以发现在urdf的文件夹中新生成了2个文件:

下面来打开mycar.pdf文件进行查看:

这个被解析出的PDF文件描述了urdf模型的层级关系,以树形结构显示。

下面进行新内容的学习与记录:

4. URDF优化_xacro

       在前面的URDF文件学习后,仅仅对URDF模型进行可视化是单一的,需要对URDF文件进行升级(优化)。在 ROS 中,已经给出了类似编程的优化方案,称之为:Xacro

Xacro 是 XML Macros 的缩写,Xacro 是一种 XML 宏语言,是可编程的 XML

原理:Xacro 可以声明变量,可以通过数学运算求解,使用流程控制控制执行顺序,还可以通过类似函数的实现,封装固定的逻辑,将逻辑中需要的可变的数据以参数的方式暴露出去,从而提高代码复用率以及程序的安全性。

       所以,较之于先前纯粹的 URDF 实现,可以编写更安全、精简、易读性更强的机器人模型文件(Xacro),且可以提高编写效率。

下面开始进行 Xacro 的快速体验:

        首先需要编写 Xacro 文件,以变量的方式封装属性(常量半径、高度、车轮半径...),以函数的方式封装重复实现(车轮的添加)。

        使用xacro优化上一节案例中驱动轮实现,需要使用变量封装底盘的半径、高度,使用数学公式动态计算底盘的关节点坐标,使用 Xacro 宏封装轮子重复的代码并调用宏创建两个轮子(注意: 在此,演示 Xacro 的基本使用,不必要生成合法的 URDF )。

需要创建功能包,导入 urdf 与 xacro。这个之间沿用先前的功能即可,无需重新创建。

下面给出xacro文件(demo01_helloworld.xacro)的相关代码:

<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
    <!-- 属性封装 -->
    <xacro:property name="wheel_radius" value="0.0325" />
    <xacro:property name="wheel_length" value="0.0015" />
    <xacro:property name="PI" value="3.1415927" />
    <xacro:property name="base_link_length" value="0.08" />
    <xacro:property name="lidi_space" value="0.015" />

    <!-- 宏 -->
    <xacro:macro name="wheel_func" params="wheel_name flag" >
        <link name="${wheel_name}_wheel">
            <visual>
                <geometry>
                    <cylinder radius="${wheel_radius}" length="${wheel_length}" />
                </geometry>

                <origin xyz="0 0 0" rpy="${PI / 2} 0 0" />

                <material name="wheel_color">
                    <color rgba="0 0 0 0.3" />
                </material>
            </visual>
        </link>

        <!-- 3-2.joint -->
        <joint name="${wheel_name}2link" type="continuous">
            <parent link="base_link"  />
            <child link="${wheel_name}_wheel" />
            <!-- 
                x 无偏移
                y 车体半径
                z z= 车体高度 / 2 + 离地间距 - 车轮半径

            -->
            <origin xyz="0 ${0.1 * flag} ${(base_link_length / 2 + lidi_space - wheel_radius) * -1}" rpy="0 0 0" />
            <axis xyz="0 1 0" />
        </joint>

    </xacro:macro>
    <xacro:wheel_func wheel_name="left" flag="1" />
    <xacro:wheel_func wheel_name="right" flag="-1" />
</robot>

注意:在ROS中,Xacro文件是不能被直接识别的。需要先将Xacro文件转化为URDF文件,转换的指令如下(先启动 roscore 节点,再进入到Xacro文件夹内):

rosrun xacro xacro demo01_helloworld.xacro

终端打印结果如下,生成了一大段文本(其实就是生成了urdf文件的内容):

更常见的作法是将上面的解析结果保存到urdf文件当中,使用如下指令:

rosrun xacro xacro demo01_helloworld.xacro > demo01_helloworld.urdf

 可以发现,在Xacro文件夹内新生成了一个urdf文件:

这个生成的urdf结果与之前终端打印的结构是一样的,给出生成的urdf代码:

<?xml version="1.0" ?>
<!-- =================================================================================== -->
<!-- |    This document was autogenerated by xacro from demo01_helloworld.xacro        | -->
<!-- |    EDITING THIS FILE BY HAND IS NOT RECOMMENDED                                 | -->
<!-- =================================================================================== -->
<robot name="mycar">
  <link name="left_wheel">
    <visual>
      <geometry>
        <cylinder length="0.0015" radius="0.0325"/>
      </geometry>
      <origin rpy="1.57079635 0 0" xyz="0 0 0"/>
      <material name="wheel_color">
        <color rgba="0 0 0 0.3"/>
      </material>
    </visual>
  </link>
  <!-- 3-2.joint -->
  <joint name="left2link" type="continuous">
    <parent link="base_link"/>
    <child link="left_wheel"/>
    <!-- 
                x 无偏移
                y 车体半径
                z z= 车体高度 / 2 + 离地间距 - 车轮半径

            -->
    <origin rpy="0 0 0" xyz="0 0.1 -0.0225"/>
    <axis xyz="0 1 0"/>
  </joint>
  <link name="right_wheel">
    <visual>
      <geometry>
        <cylinder length="0.0015" radius="0.0325"/>
      </geometry>
      <origin rpy="1.57079635 0 0" xyz="0 0 0"/>
      <material name="wheel_color">
        <color rgba="0 0 0 0.3"/>
      </material>
    </visual>
  </link>
  <!-- 3-2.joint -->
  <joint name="right2link" type="continuous">
    <parent link="base_link"/>
    <child link="right_wheel"/>
    <!-- 
                x 无偏移
                y 车体半径
                z z= 车体高度 / 2 + 离地间距 - 车轮半径

            -->
    <origin rpy="0 0 0" xyz="0 -0.1 -0.0225"/>
    <axis xyz="0 1 0"/>
  </joint>
</robot>

注意: 该案例编写生成的是非法的 URDF 文件,目的在于演示 Xacro 的极简使用以及优点。

Xacro的语法详解

        xacro 提供了可编程接口,类似于计算机语言,包括变量声明调用、函数声明与调用等语法实现。

        在使用 xacro 生成 urdf 时,根标签robot中必须包含命名空间声明: xmlns:xacro="http://wiki.ros.org/xacro"

1.属性与算数运算

        用于封装 URDF 中的一些字段,比如: PAI 值,小车的尺寸,轮子半径 ....

属性定义:

<xacro:property name="xxxx" value="yyyy" /> 

属性调用:

${属性名称}

算数运算:

${数学表达式}
2.宏

        类似于函数实现,提高代码复用率,优化代码结构,提高安全性

宏定义

<xacro:macro name="宏名称" params="参数列表(多参数之间使用空格分隔)">

    .....

    参数调用格式: ${参数名}

</xacro:macro>

宏调用

<xacro:宏名称 参数1=xxx 参数2=xxx/>
3.文件包含

        机器人由多部件组成,不同部件可能封装为单独的 xacro 文件,最后再将不同的文件集成,组合为完整机器人,可以使用文件包含实现

文件包含

<robot name="xxx" xmlns:xacro="http://wiki.ros.org/xacro">
      <xacro:include filename="my_base.xacro" />
      <xacro:include filename="my_camera.xacro" />
      <xacro:include filename="my_laser.xacro" />
      ....
</robot>

Xacro的完整使用流程示例

        这一部分才是实操的精华所在,使用 Xacro 优化 URDF 版的小车底盘模型实现

1.编写 Xacro 文件

下面进入到xacro文件夹中新建一个文件(demo_car_base.xacro):

<!--
    使用 xacro 优化 URDF 版的小车底盘实现:

    实现思路:
    1.将一些常量、变量封装为 xacro:property
      比如:PI 值、小车底盘半径、离地间距、车轮半径、宽度 ....
    2.使用 宏 封装驱动轮以及支撑轮实现,调用相关宏生成驱动轮与支撑轮

-->
<!-- 根标签,必须声明 xmlns:xacro -->
<robot name="my_base" xmlns:xacro="http://www.ros.org/wiki/xacro">
    <!-- 封装变量、常量 -->
    <xacro:property name="PI" value="3.141"/>
    <!-- 宏:黑色设置 -->
    <material name="black">
        <color rgba="0.0 0.0 0.0 1.0" />
    </material>
    <!-- 底盘属性 -->
    <xacro:property name="base_footprint_radius" value="0.001" /> <!-- base_footprint 半径  -->
    <xacro:property name="base_link_radius" value="0.1" /> <!-- base_link 半径 -->
    <xacro:property name="base_link_length" value="0.08" /> <!-- base_link 长 -->
    <xacro:property name="earth_space" value="0.015" /> <!-- 离地间距 -->

    <!-- 底盘 -->
    <link name="base_footprint">
      <visual>
        <geometry>
          <sphere radius="${base_footprint_radius}" />
        </geometry>
      </visual>
    </link>

    <link name="base_link">
      <visual>
        <geometry>
          <cylinder radius="${base_link_radius}" length="${base_link_length}" />
        </geometry>
        <origin xyz="0 0 0" rpy="0 0 0" />
        <material name="yellow">
          <color rgba="0.5 0.3 0.0 0.5" />
        </material>
      </visual>
    </link>

    <joint name="base_link2base_footprint" type="fixed">
      <parent link="base_footprint" />
      <child link="base_link" />
      <origin xyz="0 0 ${earth_space + base_link_length / 2 }" />
    </joint>

    <!-- 驱动轮 -->
    <!-- 驱动轮属性 -->
    <xacro:property name="wheel_radius" value="0.0325" /><!-- 半径 -->
    <xacro:property name="wheel_length" value="0.015" /><!-- 宽度 -->
    <!-- 驱动轮宏实现 -->
    <xacro:macro name="add_wheels" params="name flag">
      <link name="${name}_wheel">
        <visual>
          <geometry>
            <cylinder radius="${wheel_radius}" length="${wheel_length}" />
          </geometry>
          <origin xyz="0.0 0.0 0.0" rpy="${PI / 2} 0.0 0.0" />
          <material name="black" />
        </visual>
      </link>

      <joint name="${name}_wheel2base_link" type="continuous">
        <parent link="base_link" />
        <child link="${name}_wheel" />
        <origin xyz="0 ${flag * base_link_radius} ${-(earth_space + base_link_length / 2 - wheel_radius) }" />
        <axis xyz="0 1 0" />
      </joint>
    </xacro:macro>
    <xacro:add_wheels name="left" flag="1" />
    <xacro:add_wheels name="right" flag="-1" />
    <!-- 支撑轮 -->
    <!-- 支撑轮属性 -->
    <xacro:property name="support_wheel_radius" value="0.0075" /> <!-- 支撑轮半径 -->

    <!-- 支撑轮宏 -->
    <xacro:macro name="add_support_wheel" params="name flag" >
      <link name="${name}_wheel">
        <visual>
            <geometry>
                <sphere radius="${support_wheel_radius}" />
            </geometry>
            <origin xyz="0 0 0" rpy="0 0 0" />
            <material name="black" />
        </visual>
      </link>

      <joint name="${name}_wheel2base_link" type="continuous">
          <parent link="base_link" />
          <child link="${name}_wheel" />
          <origin xyz="${flag * (base_link_radius - support_wheel_radius)} 0 ${-(base_link_length / 2 + earth_space / 2)}" />
          <axis xyz="1 1 1" />
      </joint>
    </xacro:macro>

    <xacro:add_support_wheel name="front" flag="1" />
    <xacro:add_support_wheel name="back" flag="-1" />

</robot>

注意:demo_car_base.xacro文件是参考demo05_test.urdf文件编写出来的

下面进入到xacro文件夹下开启一个终端,运行:

rosrun xacro xacro demo_car_base.xacro > demo_car_base.urdf

可以发现成功生成了demo_car_base.urdf文件:

为了验证这个urdf的生成是正确的,可以运行如下指令:

check_urdf demo_car_base.urdf

发现没有抛异常,说明这个urdf文件是没有问题的

2.集成launch文件

方式1:先将 xacro 文件转换出 urdf 文件,然后集成

先将 xacro 文件解析成 urdf 文件:rosrun xacro xacro xxx.xacro > xxx.urdf然后再按照之前的集成方式直接整合 launch 文件,内容示例(新建一个launch文件:demo06_car_base.launch):

<launch>
    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/xacro/demo_car_base.urdf" />

    <node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />
    <node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" output="screen" />
    <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" output="screen" />
    <node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" output="screen" />

</launch>

保存文件后,进入当前的工作空间source一下,然后再执行launch指令:

conda activate hjx

roslaunch urdf01_rviz demo06_car_base.launch

可以发现rviz中显示正常:

方式2: 无需中间文件urdf,在 launch 文件中直接加载 xacro(建议使用)

注意:加载robot_description时使用command属性,属性值就是调用 xacro 功能包的 xacro 程序直接解析 xacro 文件。

launch 内容示例(新建一个launch文件:demo07_car_base.launch):

<launch>
    <param name="robot_description" command="$(find xacro)/xacro $(find urdf01_rviz)/urdf/xacro/demo_car_base.xacro" />

    <node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />
    <node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" output="screen" />
    <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" output="screen" />
    <node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" output="screen" />

</launch>

保存文件后,进入当前的工作空间source一下,然后再执行launch指令:

conda activate hjx

roslaunch urdf01_rviz demo07_car_base.launch

同样可以发现rviz中显示正常:

Xacro的实操

        在前面小车底盘基础之上,添加摄像头和雷达传感器。机器人模型由多部件组成,可以将不同组件设置进单独文件,最终通过文件包含实现组件的拼装。

实现流程:

  1. 首先编写摄像头和雷达的 xacro 文件

  2. 然后再编写一个组合文件,组合底盘、摄像头与雷达

  3. 最后,通过 launch 文件启动 Rviz 并显示模型

1.摄像头和雷达 Xacro 文件实现

摄像头 xacro 文件(demo_car_camera.xacro):

<!-- 摄像头相关的 xacro 文件 -->
<robot name="my_camera" xmlns:xacro="http://wiki.ros.org/xacro">
    <!-- 摄像头属性 -->
    <xacro:property name="camera_length" value="0.01" /> <!-- 摄像头长度(x) -->
    <xacro:property name="camera_width" value="0.025" /> <!-- 摄像头宽度(y) -->
    <xacro:property name="camera_height" value="0.025" /> <!-- 摄像头高度(z) -->
    <xacro:property name="camera_x" value="0.08" /> <!-- 摄像头安装的x坐标 -->
    <xacro:property name="camera_y" value="0.0" /> <!-- 摄像头安装的y坐标 -->
    <xacro:property name="camera_z" value="${base_link_length / 2 + camera_height / 2}" /> <!-- 摄像头安装的z坐标:底盘高度 / 2 + 摄像头高度 / 2  -->

    <!-- 摄像头关节以及link -->
    <link name="camera">
        <visual>
            <geometry>
                <box size="${camera_length} ${camera_width} ${camera_height}" />
            </geometry>
            <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
            <material name="black" />
        </visual>
    </link>

    <joint name="camera2base_link" type="fixed">
        <parent link="base_link" />
        <child link="camera" />
        <origin xyz="${camera_x} ${camera_y} ${camera_z}" />
    </joint>
</robot>

雷达 xacro 文件(demo_car_laser.xacro):

<!--
    小车底盘添加雷达
-->
<robot name="my_laser" xmlns:xacro="http://wiki.ros.org/xacro">

    <!-- 雷达支架 -->
    <xacro:property name="support_length" value="0.15" /> <!-- 支架长度 -->
    <xacro:property name="support_radius" value="0.01" /> <!-- 支架半径 -->
    <xacro:property name="support_x" value="0.0" /> <!-- 支架安装的x坐标 -->
    <xacro:property name="support_y" value="0.0" /> <!-- 支架安装的y坐标 -->
    <xacro:property name="support_z" value="${base_link_length / 2 + support_length / 2}" /> <!-- 支架安装的z坐标:底盘高度 / 2 + 支架高度 / 2  -->

    <link name="support">
        <visual>
            <geometry>
                <cylinder radius="${support_radius}" length="${support_length}" />
            </geometry>
            <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
            <material name="red">
                <color rgba="0.8 0.2 0.0 0.8" />
            </material>
        </visual>
    </link>

    <joint name="support2base_link" type="fixed">
        <parent link="base_link" />
        <child link="support" />
        <origin xyz="${support_x} ${support_y} ${support_z}" />
    </joint>


    <!-- 雷达属性 -->
    <xacro:property name="laser_length" value="0.05" /> <!-- 雷达长度 -->
    <xacro:property name="laser_radius" value="0.03" /> <!-- 雷达半径 -->
    <xacro:property name="laser_x" value="0.0" /> <!-- 雷达安装的x坐标 -->
    <xacro:property name="laser_y" value="0.0" /> <!-- 雷达安装的y坐标 -->
    <xacro:property name="laser_z" value="${support_length / 2 + laser_length / 2}" /> <!-- 雷达安装的z坐标:支架高度 / 2 + 雷达高度 / 2  -->

    <!-- 雷达关节以及link -->
    <link name="laser">
        <visual>
            <geometry>
                <cylinder radius="${laser_radius}" length="${laser_length}" />
            </geometry>
            <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
            <material name="black" />
        </visual>
    </link>

    <joint name="laser2support" type="fixed">
        <parent link="support" />
        <child link="laser" />
        <origin xyz="${laser_x} ${laser_y} ${laser_z}" />
    </joint>
</robot>
2.组合底盘摄像头与雷达的 xacro 文件

需要再新建一个xacro的集成文件(car.xacro):

<!-- 组合小车底盘与摄像头与雷达 -->
<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
    <xacro:include filename="demo_car_base.xacro" />
    <xacro:include filename="demo_car_camera.xacro" />
    <xacro:include filename="demo_car_laser.xacro" />
</robot>
3.launch 文件

最后编写launch文件(car.launch):

<launch>
    <param name="robot_description" command="$(find xacro)/xacro $(find urdf01_rviz)/urdf/xacro/car.xacro" />

    <node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />
    <node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" output="screen" />
    <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" output="screen" />
    <node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" output="screen" />

</launch>

保存文件后,进入当前的工作空间source一下,然后再执行launch指令:

conda activate hjx

roslaunch urdf01_rviz car.launch

最后可以发现我们的car在rviz中显示正常:


        好啦 ~ 由于篇幅原因,目前有关URDF与Rviz的博客到此结束,但是,【ROS机器人入门:机器人系统仿真 】这个系列还没有结束。


后续的博客链接请见:

ROS机器人入门:机器人系统仿真【学习记录】——2-CSDN博客


~~~加油~~~


 这篇博客的学习网址为:227引言_机器人系统仿真_哔哩哔哩_bilibili

所对应的图文教程为:6.1 概述 · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程


参考资料:

第 6 章 机器人系统仿真 · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ROS机器人操作系统)是一种开源的机器人操作系统,提供了一个框架和工具来帮助开发者创建、编译和运行机器人应用程序。ROS的特点是模块化、可重用和分布式,它使得机器人开发变得更加灵活和高效。 在Ubuntu操作系统上进行ROS机器人仿真开发具有许多优势。首先,Ubuntu是一种基于Linux的操作系统,与ROS非常兼容。ROS的开发环境和工具包都可以在Ubuntu上进行安装和配置,使得程序员能够方便地开展ROS的开发和调试工作。 其次,Ubuntu作为一个开源的操作系统,兼容性强,并且有庞大的开发者社区。在Ubuntu上开发ROS机器人应用程序,可以方便地获取开源的ROS软件包,并从社区中获取支持和解决方案。这大大提高了开发效率和质量。 此外,Ubuntu提供了强大的图形化用户界面(GUI),如GNOME、Unity等,使得开发者可以使用图形化工具和界面来构建、调试和监控ROS机器人应用程序,而不仅仅是通过命令行界面(CLI)进行开发。 最后,Ubuntu是一种稳定、可靠和安全的操作系统。在ROS机器人仿真开发中,稳定性和安全性是非常重要的因素,以确保机器人应用程序的正确性和性能。Ubuntu不仅提供了一个稳定的平台,还有强大的安全性和管理工具,以确保ROS应用程序的稳定性和安全性。 综上所述,使用Ubuntu进行ROS机器人仿真开发是一个明智的选择。Ubuntu的兼容性、强大的开发者社区、图形化界面以及稳定性和安全性等优势,可以大大提高ROS机器人应用程序的开发效率和质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

随机惯性粒子群

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

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

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

打赏作者

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

抵扣说明:

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

余额充值