Bootstrap

(十一)机器人系统的仿真——建造机器人模型

前言

对于ROS新手而言,可能会有疑问:学习机器人操作系统,实体机器人是必须的吗?答案是否定的,机器人一般价格不菲,为了降低机器人学习、调试成本,在ROS中提供了系统的机器人仿真实现,通过仿真,可以实现大部分需求,本章主要就是围绕“仿真”展开的,比如,本章会介绍:如何创建并显示机器人模型;如何搭建仿真环境;如何实现机器人模型与仿真环境的交互。

学习目标:

本章预期的学习目标如下:能够独立使用URDF创建机器人模型,并在Rviz和Gazebo中分别显示;能够使用Gazebo搭建仿真环境;能够使用机器人模型中的传感器(雷达、摄像头、编码器...)获取仿真环境数据。

机器人仿真应用的开发场景:

机器人操作系统学习、开发与测试过程中,会遇到诸多问题,比如:

场景1:机器人一般价格不菲,学习ROS要购买一台机器人吗?

场景2:机器人与之交互的外界环境具有多样性,如何实现复杂的环境设计?

场景3:测试时,直接将未经验证的程序部署到实体机器人运行,安全吗?

...

在诸如此类的场景中,ROS中的仿真就显得尤为重要了。

一、机器人的仿真系统

1.概念

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

2.作用

1.仿真优势:

仿真在机器人系统研发过程中占有举足轻重的地位,在研发与测试中较之于实体机器人实现,仿真有如下几点的显著优势:

1.低成本:当前机器人成本居高不下,动辄几十万,仿真可以大大降低成本,减小风险

2.高效:搭建的环境更为多样且灵活,可以提高测试效率以及测试覆盖率

3.高安全性:仿真环境下,无需考虑耗损问题

2.仿真缺陷:

机器人在仿真环境与实际环境下的表现差异较大,换言之,仿真并不能完全做到模拟真实的物理世界,存在一些"失真"的情况,原因:

1.仿真器所使用的物理引擎目前还不能够完全精确模拟真实世界的物理情况

2.仿真器构建的是关节驱动器(电机&齿轮箱)、传感器与信号通信的绝对理想情况,目前不支持模拟实际硬件缺陷或者一些临界状态等情况。

3.相关组件

1.URDF

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

2.rviz

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

3.gazebo

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

4.三者的关系

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

    URDF 用于创建机器人模型

    Gzebo 用于搭建仿真环境

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

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

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

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

接下来:

    先介绍 URDF 与 Rviz 集成使用,在 Rviz 中只是显示机器人模型,主要用于学习 URDF 语法

    再介绍 URDF 与 Gazebo 集成,主要学习 URDF 仿真相关语法以及仿真环境搭建

    最后集成 URDF 与 Gazebo 与 Rviz,实现综合应用。

二、URDF集成Rviz基本流程

前面介绍过,URDF 不能单独使用,需要结合 Rviz 或 Gazebo,URDF 只是一个文件,需要在 Rviz 或 Gazebo 中渲染成图形化的机器人模型,当前,首先演示URDF与Rviz的集成使用,因为URDF与Rviz的集成较之于URDF与Gazebo的集成更为简单,后期,基于Rviz的集成实现,我们再进一步介绍URDF语法。

1.需求描述

在Rviz中显示一个盒状机器人。

2.实现流程

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

2.核心:编写 urdf 文件

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

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

3.具体实现

1.创建工作空间demo_ws,完成编译环境、开发环境的配置

2.新建功能包 urdf_rviz ,导入依赖 urdf xacro

3.创建四个文件夹

(基础操作这里不过多叙述)

config存储配置信息;launch存储launch文件;meshes存储皮肤;urdf存储urdf文件

4.编写urdf文件

<robot name="mycar">
    <link name="base_link">
        <visual>
            <geometry>
                <box size="0.5 0.2 0.1" />
            </geometry>
        </visual>
    </link>
</robot>

5.创建launch文件

<launch>
    <!-- 在参数服务器中载入 urdf 文件 -->
    <param  name = "robot_description" textfile = "$(find urdf_rviz)/urdf/demo_00.urdf"/>
    <!-- 启动rviz-->
    <node pkg = "rviz" type = "rviz" name = "rviz" />
</launch>

6.启动launch文件

roslaunch urdf_rviz demo_00.launch

添加一些配置:

7.优化

当我们重新启动launch文件时,还需要重新配置这时候就需要设置一些东西来优化一下。

<launch>
    <!-- 在参数服务器中载入 urdf 文件 -->
    <param  name = "robot_description" textfile = "$(find urdf_rviz)/urdf/demo_00.urdf"/>
    <!-- 启动rviz-->
    <node pkg = "rviz" type = "rviz" name = "rviz" args="-d $(find urdf_rviz)/config/show_mycar.rviz"/>
</launch>

三、URDF语法讲解

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

1.robot 根标签,类似于 launch文件中的launch标签

2.link 连杆标签

3.joint 关节标签

4.gazebo 集成gazebo需要使用的标签

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

1.URDF语法详解_robot

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

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

子级标签:其他标签都是子级标签。

2.URDF语法详解02_link

urdf 中的 link 标签用于描述机器人某个部件(也即刚体部分)的外观和物理属性,比如: 机器人底座、轮子、激光雷达、摄像头...每一个部件都对应一个 link, 在 link 标签内,可以设计该部件的形状、尺寸、颜色、惯性矩阵、碰撞参数等一系列属性。

属性:name ---> 为连杆命名

子级标签:

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 ---> 连杆的惯性矩阵

具体实例:

<!--设置不同形状的机器人部件-->
 
<robot name = "mycar">
    <link name = "base_link">
        <!--可视化标签-->
        <visual>
            <!--1.形状-->
            <geometry>
                <!--立方体 长宽高-->
                <!--box size = "0.3 0.2 0.1" /-->
                <!--圆柱-->
                <!--cylinder radius="0.1" length="2.0"/-->
                <!--球体-->
                <!--sphere radius="1.0"/-->
                <!--添加皮肤-->
                <mesh filename="package://urdf_rviz/meshes/autolabor_mini.stl" />
            </geometry>
            <!--2.偏移量以及倾斜弧度-->
            <!--xyz设置机器人模型在xyz轴的偏移量 rpy主要用于设置倾斜弧度-->
            <origin xyz="0.0 0.0 0.0" rpy="1.57 0.0 1.57"/>
            <!--3.设置颜色-->
            <material name = "car_color">
                <color rgba="0.0 0.0 1.0 1.0"/>
            </material>
        </visual>
    </link>
 
</robot>

3.URDF语法详解_joint

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

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

属性:

name ---> 为关节命名

type ---> 关节运动形式

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

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

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

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

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

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

子级标签:

parent(必需的)

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

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

child(必需的)

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

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

origin

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

axis

    属性: xyz用于设置围绕哪个关节轴运动。

具体实例:

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

小车的三围是 0.3 0.2 0.1 (长宽高),小车的中心为中心坐标点,因为摄像头要在最上面的平面上,因此距离要取0.05,y方向由于要在车体的正中间因此取0,x最多取0.15,这里我们取0.12.

旋转轴设置:旋转时旋转的坐标轴 x,y均不运动,摄像头仅仅围绕z轴运动

joint orginal z的计算:两个中心点相距离的位置:应该是两方各自除以2相加(0.05/2)+(0.1/2)= 0.075

<robot name="mycar">
    <link name="base_link">
        <visual>
            <geometry>
                <box size="0.3 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" />
        <origin xyz="0.12 0 0.075" rpy="0 0 0" />
        <axis xyz="0 0 1" />
    </joint>
 
</robot>

编写launch文件

如果沿用之前的launch文件的配置,则生成的模型会出错。

<launch>
    <!-- 在参数服务器中载入 urdf 文件 -->
    <param  name = "robot_description" textfile = "$(find urdf_rviz)/urdf/demo_01.urdf"/>
    <!-- 启动rviz-->
    <node pkg = "rviz" type = "rviz" name = "rviz" args="-d $(find urdf_rviz)/config/show_mycar.rviz"/>
</launch>

可以看出此时的摄像头与小车的位置不太对。

这时候需要我们重新更改一下launch文件的配置。

<launch>
    <!-- 在参数服务器中载入 urdf 文件 -->
    <param  name = "robot_description" textfile = "$(find urdf_rviz)/urdf/demo_01.urdf"/>
    <!-- 启动rviz-->
    <node pkg = "rviz" type = "rviz" name = "rviz" />
    <!--
        若只有上面两条语句会抛出异常
        表现:摄像头显示位置与颜色异常
        提示:No transform from [camera] to [base_link]
        原因:rviz 中显示 URDF 时,必须发布不同部件之间的坐标系关系
        解决:ROS中提供了关于机器人模型显示的坐标发布相关结点(两个)
    -->
    <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"/>
</launch>

修改后可以看出此时的模型位置之间是正常的。

建完模之后怎么让摄像头转起来呢:添加节点控制关节运动节点

这一部分了解即可主要是用来测试urdf建出来的模型是否合理。

怎么做呢?

还需要在launch文件中加入关节控制的节点

<node pkg = "joint_state_publisher_gui" type = "joint_state_publisher_gui" name = "joint_state_publisher_gui"/>

base_footprint优化urdf 

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

2.代码逻辑:

①创建一个 0.001*0.001*0.001的立方体

②创建一个 0.3*0.2*0.1的底盘

③创建一个 0.02*0.05*0.05的摄像头

④创建约束 basefootprint&baselink 固定约束 z的运算为盒子的一半0.05

⑤创建约束 camera2baselink 旋转约束z

<robot name="mycar">
 
    <link name="base_footprint">
        <visual>
            <geometry>
                <box size="0.001 0.001 0.001" />
            </geometry>
        </visual>
    </link>
 
    <link name="base_link">
        <visual>
            <geometry>
                <box size="0.3 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="basefootprint2baselink" type="fixed">
        <parent link="base_footprint"/>
        <child link="base_link" />
        <origin xyz="0 0 0.05" rpy="0 0 0" />
    </joint>
 
    <joint name="camera2baselink" type="continuous">
        <parent link="base_link"/>
        <child link="camera" />
        <origin xyz="0.12 0 0.075" rpy="0 0 0" />
        <axis xyz="0 0 1" />
    </joint>
 
</robot>

编写launch文件

<launch>
 
    <param name="robot_description" textfile="$(find urdf_rviz)/urdf/demo_basefoot.urdf" />
    <node pkg="rviz" type="rviz" name="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" />
 
 
</launch>

 可以看到底座下边生成了一个很小的长方体

四、URDF练习:建立四轮车模型

需求:

创建一个四轮圆柱状机器人模型,机器人参数如下,底盘为圆柱状,半径 10cm,高 8cm,四轮由两个驱动轮和两个万向支撑轮组成,两个驱动轮半径为 3.25cm,轮胎宽度1.5cm,两个万向轮为球状,半径 0.75cm,底盘离地间距为 1.5cm(与万向轮直径一致)

实现流程:

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

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

2.搭建底盘

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

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

具体实现:

创建文件demo_03.urdf:

joint的xyz的设置:由于底盘的中心在其中心点,距离平面距离为 8cm/2 = 4cm,由于还要设置离地间隙,于是basefootprint相对于baselink的z坐标为 childz-parentz = 0.04 + 0.015 - 0.

z的偏移是:车轮的中心点到地面的距离刚好是车轮的半径0.0325m

chlid.z - base.z = (0.0325)- (0.04+0.015)

右驱动轮除了y是取相反值之外没有区别!

驱动轮的joint的axis :沿着哪个轴运动,左轮添加到车体左侧,y轴运动,y为1.

joint前轮的取值:
origin x:chlid_front.x - base_link.x = (底盘半径 -万向轮半径)-0 = 0.1 - 0.0075

origin y:0

origin z:chlid_front.z - base_link.z = 万向轮半径 - (底盘高/2 +0.015) = 0.0075 - (底盘高/2+0.015) = 0.0075-0.055=0.0475

<robot name="mycar">
    <link name="base_footprint">
        <visual>
            <geometry>
                <sphere radius="0.001" />
            </geometry>
        </visual>
    </link>
 
 
    <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>
 
    <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>
 
 
    <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文件demo_02.launch

<launch>
 
    <param name="robot_description" textfile="$(find urdf_rviz)/urdf/demo_03.urdf" />
    <node pkg="rviz" type="rviz" name="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" />
 
</launch>

若运行时出现下列问题就是你的urdf文件的格式有错误

[robot_state_publisher-3] process has died [pid 15180, exit code 1, cmd /opt/ros/noetic/lib/robot_state_publisher/robot_state_publisher __name:=robot_state_publisher __log:=/home/lvl/.ros/log/169e0358-d588-11ef-92be-153185e84cef/robot_state_publisher-3.log].
log file: /home/lvl/.ros/log/169e0358-d588-11ef-92be-153185e84cef/robot_state_publisher-3*.log
[joint_state_publisher-2] process has died [pid 15179, exit code 1, cmd /opt/ros/noetic/lib/joint_state_publisher/joint_state_publisher __name:=joint_state_publisher __log:=/home/lvl/.ros/log/169e0358-d588-11ef-92be-153185e84cef/joint_state_publisher-2.log].
log file: /home/lvl/.ros/log/169e0358-d588-11ef-92be-153185e84cef/joint_state_publisher-2*.log

建模展示:

URDF工具:

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

2.urdf_to_graphiz 可以查看urdf模型结构

五、URDF优化_xacro

1.为什么需要xacro?

问题1:在设计关节位置时,需要按照一定的公式计算,公式是固定的,但是在URDF中依赖于人工计算,存在不便,容易计算失误,且当某些参数发生改变时,还需要重新计算。

问题2:URDF中部分内容是高度重复的,驱动轮与支撑轮的设计实现,不同轮子只是参数不同,形状颜色翻转量都是一样的,考虑封装。

2.概念

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

3.原理

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

Xacro语法讲解

需求描述 

使用xacro优化上节的驱动轮实现,需要使用变量封装底盘的半径、高度、使用动态数学公式动态计算底盘的关节点坐标,使用xacro宏封装轮子重复的代码并调用宏创建两个轮子。

xacro提供了可编程端口,类似于计算机语言,包括变量声明调用、函数声明与调用等语法实现。在使用xacro生成urdf时,根标签robot中必须包含命名空间声明:

xmlns:xacro="http://wiki.ros.org/xacro"

1.属性定义

基本结构:

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

属性调用:

通过下列的格式进行调用。

${属性名称}

算术运算:

同样通过下列格式调用:

${数学表达式}

具体实例:

demo00_urdf.xacro

<robot name = "mycar" xmlns:xacro="http://www.ros.org/wiki/xacro">
    <!--  1. 属性定义  -->
    <xacro:property name = "PI"  value = "3.1415927"/>
    <xacro:property name = "radius" value = "0.03" />
    <!--  2.属性调用  -->
    <myuserpropertyxxx name = "${PI}"/>
    <myuserpropertyxxx name = "${radius}"/>
    <!--  3.算术运算  -->
    <myuserpropertyyy result = "${PI/2}"/>
    <myuserpropertyyy result = "${radius*2}"/>
</robot>

运行

在之前创建好的xacro文件夹下输入如下命令。

rosrun xacro xacro demo00_urdf.xacro

输出结果:

lvl@lvl-Legion-Y7000P-IRX9:~/demo_ws/src/urdf_rviz/xacro$ rosrun xacro xacro demo00_urdf.xacro 
<?xml version="1.0" ?>
<!-- =================================================================================== -->
<!-- |    This document was autogenerated by xacro from demo00_urdf.xacro              | -->
<!-- |    EDITING THIS FILE BY HAND IS NOT RECOMMENDED                                 | -->
<!-- =================================================================================== -->
<robot name="mycar">
  <!--  2.属性调用  -->
  <myuserpropertyxxx name="3.1415927"/>
  <myuserpropertyxxx name="0.03"/>
  <!--  3.算术运算  -->
  <myuserpropertyyy result="1.57079635"/>
  <myuserpropertyyy result="0.06"/>
</robot>

2.宏

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

1.宏定义

<xacro:macro name = "宏名称" params = "参数1 参数2...">
    参数调用格式:${参数名}
</xacro:macro>

2.宏调用

<xacro:宏名称 参数1=xx 参数2=xxx />

3.具体实例

demo01_urdf.xacro

<robot name = "mycar" xmlns:xacro="http://www.ros.org/wiki/xacro">
    <!--  1. 宏定义  -->
    <xacro:macro name = "getSum"  params = "num1 num2">
        <result value = "${num1 + num2}" />
    </xacro:macro>
    <!--  2. 宏调用  -->
    <xacro:getSum num1 = "1" num2 = "5"/>
</robot>

4.运行:

lvl@lvl-Legion-Y7000P-IRX9:~/demo_ws/src/urdf_rviz/xacro$ rosrun xacro xacro demo01_urdf.xacro 
<?xml version="1.0" ?>
<!-- =================================================================================== -->
<!-- |    This document was autogenerated by xacro from demo01_urdf.xacro              | -->
<!-- |    EDITING THIS FILE BY HAND IS NOT RECOMMENDED                                 | -->
<!-- =================================================================================== -->
<robot name="mycar">
  <result value="6"/>
</robot>

3.文件包含

1.应用场景:机器人由多组件组成、不同部件可能封装为不同的xacro文件,最后再将不同的文件集成,组合为完整机器人,可以用文件包含实现。

2.语法

<robot name = "mycar" xmlns:xacro="http://www.ros.org/wiki/xacro">
    <!--  1. 演示文件包含  -->
    <xacro:include filename = "...."/>
    <xacro:include filename = "...."/>
</robot>

3.具体实例

demo02_urdf.xacro

<robot name = "mycar" xmlns:xacro="http://www.ros.org/wiki/xacro">
    <!--  1. 演示文件包含  -->
    <xacro:include filename = "demo00_urdf.xacro"/>
    <xacro:include filename = "demo01_urdf.xacro"/>
</robot>

4.运行

lvl@lvl-Legion-Y7000P-IRX9:~/demo_ws/src/urdf_rviz/xacro$ rosrun xacro xacro demo02_urdf.xacro 
<?xml version="1.0" ?>
<!-- =================================================================================== -->
<!-- |    This document was autogenerated by xacro from demo02_urdf.xacro              | -->
<!-- |    EDITING THIS FILE BY HAND IS NOT RECOMMENDED                                 | -->
<!-- =================================================================================== -->
<robot name="mycar">
  <!--  2.属性调用  -->
  <myuserpropertyxxx name="3.1415927"/>
  <myuserpropertyxxx name="0.03"/>
  <!--  3.算术运算  -->
  <myuserpropertyyy result="1.57079635"/>
  <myuserpropertyyy result="0.06"/>
  <result value="6"/>
</robot>

Xacro完整版使用流程

1.需求

使用xacro优化urdf版的小车底盘模型实现。

新建文件 demo03_urdf.xacro 编写文件

<robot name = "mycar" xmlns:xacro="http://www.ros.org/wiki/xacro">
 
    <xacro:property name = "footfrintradius" value = "0.001"/>
    <xacro:property name = "base_radius" value = "0.1" />
    <xacro:property name = "base_length" value = "0.08" />
    <xacro:property name = "lidi" value = "0.015" />
    <xacro:property name = "base_joint_z" value = "${base_length/2+ lidi }" />
 
    <link name="base_footprint">
        <visual>
            <geometry>
                <sphere radius="${footfrintradius}" />
            </geometry>
        </visual>
    </link>
 
    <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>
 
</robot>

输入命令将xacro转换成urdf(demo04.urdf)

lvl@lvl-Legion-Y7000P-IRX9:~/demo_ws/src/urdf_rviz/xacro$ rosrun xacro xacro demo03_urdf.xacro > demo04.urdf

生成的具体内容:

<?xml version="1.0" ?>
<!-- =================================================================================== -->
<!-- |    This document was autogenerated by xacro from demo03_urdf.xacro              | -->
<!-- |    EDITING THIS FILE BY HAND IS NOT RECOMMENDED                                 | -->
<!-- =================================================================================== -->
<robot name="mycar">
  <link name="base_footprint">
    <visual>
      <geometry>
        <sphere radius="0.001"/>
      </geometry>
    </visual>
  </link>
  <link name="base_link">
    <visual>
      <geometry>
        <cylinder length="0.08" radius="0.1"/>
      </geometry>
      <origin rpy="0 0 0" xyz="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>
</robot>

实现xacro与rviz的集成

先转换出urdf文件,然后集成。

demo_03.launch

<launch>
 
    <param name="robot_description" textfile="$(find urdf_rviz)/xacro/demo03_.urdf" />
    <node pkg="rviz" type="rviz" name="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" />
 
</launch>

直接在launch文件中直接加载xacro 

<launch>
 
    <param name="robot_description" command="$(find xacro)/xacro $(find urdf_rviz)/xacro/demo03_urdf.xacro" />
    <node pkg="rviz" type="rviz" name="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" />
 
</launch>

执行结果相同 

不显示的原因在前边已经阐述需要进行配置这里不过多赘述。

继续完成驱动轮万向轮的封装

比较两个驱动轮,link比较name的差异,joint比较也有name的差异,还有y方向上偏移量的差异,还有各种参数的设计,计算的逻辑

因此把右轮的数据删掉,尝试用左轮进行复用wheel_name 可取 left 或 rightflag 可取 1 或 -1

重新新建一个demo04_urdf.xacro 文件

<robot name = "mycar" xmlns:xacro="http://www.ros.org/wiki/xacro">
 
    <xacro:property name = "footfrintradius" value = "0.001"/>
    <xacro:property name = "base_radius" value = "0.1" />
    <xacro:property name = "base_length" value = "0.08" />
    <xacro:property name = "lidi" value = "0.015" />
    <xacro:property name = "base_joint_z" value = "${base_length/2+ lidi }" />
 
    <xacro:property name = "wheel_radius" value = "0.0325" />
    <xacro:property name = "wheel_length" value = "0.015" />
    <xacro:property name = "PI" value = "3.1415927" />
    <xacro:property name = "wheel_joint_z" value = "${-(base_length/2-lidi+wheel_radius)}" />
 
    <xacro:property name = "small_wheel_radius" value = "0.0075" />
    <xacro:property name = "small_joint_z" value = "${-(base_length/2+lidi-small_wheel_radius)}" />
 
 
 
    <link name="base_footprint">
        <visual>
            <geometry>
                <sphere radius="${footfrintradius}" />
            </geometry>
        </visual>
    </link>
 
    <link name="base_link">
        <visual>
            <geometry>
                <cylinder radius="${base_radius}" length="${base_length}" />
            </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 ${base_joint_z}" />
    </joint>
 
   
    <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="black">
                <color rgba="0.0 0.0 0.0 1.0" />
            </material>
        </visual>
        </link>
        <joint name="${wheel_name}2link" type="continuous">
            <parent link="base_link" />
            <child link="${wheel_name}_wheel" />
            <origin xyz="0 ${0.1 * flag} ${wheel_joint_z}" />
            <axis xyz="0 1 0" />
        </joint>
    </xacro:macro>
 
    <xacro:wheel_func wheel_name = "left" flag = "1"/>
    <xacro:wheel_func wheel_name = "right" flag = "-1"/>
 
 
    <xacro:macro name = "small_wheel_func" params = "small_wheel_name flag">
        <link name="${small_wheel_name}_wheel">
            <visual>
                <geometry>
                    <sphere radius="${small_wheel_radius}" />
                </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="${small_wheel_name}2link" type="continuous">
            <parent link="base_link" />
            <child link="${small_wheel_name}_wheel" />
            <origin xyz="${0.08*flag} 0 ${small_joint_z}" />
            <axis xyz="1 1 1" />
        </joint>
    </xacro:macro>
    <xacro:small_wheel_func small_wheel_name = "front" flag = "1"/>
    <xacro:small_wheel_func small_wheel_name = "back" flag = "-1"/>
 
</robot>

利用launch文件直接加载xacro文件。

新建一个demo_04.launch文件

<launch>
 
    <param name="robot_description" command="$(find xacro)/xacro $(find urdf_rviz)/xacro/demo04_urdf.xacro" />
    <node pkg="rviz" type="rviz" name="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" />
 
</launch>

别忘了进行配置才能显示模型

六、案例:运用rviz、xacro及本章所学知识做一个多功能小车

需求:

在前面小车底盘基础上,填上摄像头和雷达传感器。

分析:

机器人模型由多组件完成,可以将不同组件设置进单独文件,最终通过文件包含实现组件的安装。

大体流程:

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

2..编写一个组合文件,组合底盘,摄像头和雷达

3.通过launch文件启动rviz并显示模型

具体实现:

首先创建四个xacro文件和launch文件:

1.car_camera_lidar.xacro xacro的头文件。

2.car.xacro 小车的模型

3.camera.xacro 摄像机的模型

4.lidar.xacro 激光雷达的模型

5.car_camera_lidar.launch

1.编辑car_camera_lidar.xacro

<robot name = "mycar" xmlns:xacro="http://www.ros.org/wiki/xacro">
    <xacro:include filename = "car.xacro"/>
    <xacro:include filename = "camera.xacro"/>
    <xacro:include filename = "lidar.xacro"/>
</robot>

2.编辑launch文件car_camera_lidar.launch

<launch>
    <param name="robot_description" command="$(find xacro)/xacro $(find urdf_rviz)/xacro/car_camera_lidar.xacro" />
    <node pkg="rviz" type="rviz" name="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" />
</launch>

3.小车底盘实现 car.xacro

<robot name = "mycar" xmlns:xacro="http://www.ros.org/wiki/xacro">
 
    <xacro:property name = "footfrintradius" value = "0.001"/>
    <xacro:property name = "base_radius" value = "0.1" />
    <xacro:property name = "base_length" value = "0.08" />
    <xacro:property name = "lidi" value = "0.015" />
    <xacro:property name = "base_joint_z" value = "${base_length/2+ lidi }" />
 
    <xacro:property name = "wheel_radius" value = "0.0325" />
    <xacro:property name = "wheel_length" value = "0.015" />
    <xacro:property name = "PI" value = "3.1415927" />
    <xacro:property name = "wheel_joint_z" value = "${wheel_radius - (base_length/2 +lidi)}" />
 
    <xacro:property name = "small_wheel_radius" value = "0.0075" />
    <xacro:property name = "small_joint_z" value = "${-(base_length/2+lidi-small_wheel_radius)}" />
 
 
 
    <link name="base_footprint">
        <visual>
            <geometry>
                <sphere radius="${footfrintradius}" />
            </geometry>
        </visual>
    </link>
 
    <link name="base_link">
        <visual>
            <geometry>
                <cylinder radius="${base_radius}" length="${base_length}" />
            </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 ${base_joint_z}" />
    </joint>
 
   
    <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="black">
                <color rgba="0.0 0.0 0.0 1.0" />
            </material>
        </visual>
        </link>
        <joint name="${wheel_name}2link" type="continuous">
            <parent link="base_link" />
            <child link="${wheel_name}_wheel" />
            <origin xyz="0 ${0.1 * flag} ${wheel_joint_z}" />
            <axis xyz="0 1 0" />
        </joint>
    </xacro:macro>
 
    <xacro:wheel_func wheel_name = "left" flag = "1"/>
    <xacro:wheel_func wheel_name = "right" flag = "-1"/>
 
 
    <xacro:macro name = "small_wheel_func" params = "small_wheel_name flag">
        <link name="${small_wheel_name}_wheel">
            <visual>
                <geometry>
                    <sphere radius="${small_wheel_radius}" />
                </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="${small_wheel_name}2link" type="continuous">
            <parent link="base_link" />
            <child link="${small_wheel_name}_wheel" />
            <origin xyz="${0.08*flag} 0 ${small_joint_z}" />
            <axis xyz="1 1 1" />
        </joint>
    </xacro:macro>
    <xacro:small_wheel_func small_wheel_name = "front" flag = "1"/>
    <xacro:small_wheel_func small_wheel_name = "back" flag = "-1"/>
 
</robot>

4. 小车摄像头实现 camera.xacro

<robot name = "my_camera" xmlns:xacro="http://www.ros.org/wiki/xacro">
    <xacro:property name = "camera_x" value = "0.02" />
    <xacro:property name = "camera_y" value = "0.05" />
    <xacro:property name = "camera_z" value = "0.05" />
    <xacro:property name = "joint_camera_x" value = "0.08" />
    <xacro:property name = "joint_camera_y" value = "0" />
    <xacro:property name = "base_length" value = "0.08" />
    <xacro:property name = "joint_camera_z" value = "${camera_z /2 +base_length/2 }" />
    
    <link name="camera">
        <visual>
            <geometry>
                <box size="${camera_x} ${camera_y} ${camera_z}"/>
            </geometry>
            <material name = "no" >
                <color rgba = "3 3 3 0.3"/>
            </material>
        </visual>
    </link>
 
 
    <joint name="camera2baselink" type="fixed">
        <origin xyz="${joint_camera_x} ${joint_camera_y} ${joint_camera_z}" rpy="0.0 0.0 0.0"/>
        <parent link="base_link"/>
        <child link="camera"/>
        <axis xyz="0.0 0.0 0.0"/>
    </joint>
</robot>

5.小车雷达实现 lidar.xacro

<robot name = "mylaser" xmlns:xacro="http://www.ros.org/wiki/xacro">
    <xacro:property name = "support_radius" value = "0.01" />
    <xacro:property name = "support_length" value = "0.15" />
    <xacro:property name = "laser_radius" value = "0.03" />
    <xacro:property name = "laser_length" value = "0.05" />
 
    <xacro:property name = "joint_support_x" value = "0.00" />
    <xacro:property name = "joint_support_y" value = "0.00" />
    <xacro:property name = "joint_support_z" value = "${base_length / 2 +support_length / 2}" />
    <xacro:property name = "joint_laser_x" value = "0.00" />
    <xacro:property name = "joint_laser_y" value = "0.00" />
    <xacro:property name = "joint_laser_z" value = "${support_length/2 + laser_length/2}" />
 
    <link name="support">
        <visual>
            <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
            <geometry>
                <cylinder radius = "${support_radius}" length = "${support_length}"/>
            </geometry>
            <material name="sha">
                <color rgba="0.8 0.5 0.0 0.5"/>
            </material>
        </visual>
    </link>
 
    <joint name="support2base" type="fixed">
        <parent link = "base_link"/>
        <child link = "support" />
        <origin xyz = "${joint_support_x} ${joint_support_y} ${joint_support_z}" rpy = "0 0 0" />
    </joint>
 
    <link name="laser">
        <visual>
            <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
            <geometry>
                <cylinder radius = "${laser_radius}" length = "${laser_length}"/>
            </geometry>
            <material name="shav">
                <color rgba="0.0 0.0 0.0 0.5"/>
            </material>
        </visual>
    </link>
 
    <joint name="laser2support" type="fixed">
        <parent link = "support"/>
        <child link = "laser" />
        <origin xyz = "${joint_laser_x} ${joint_laser_y} ${joint_laser_z}" rpy = "0 0 0" />
    </joint>
</robot>

运行:

记得要配置机器人模型才能显示。

结束语

以上就是我学习到的内容,如果对您有帮助请多多支持我,如果哪里有问题欢迎大家在评论区积极讨论,我看到会及时回复。

 

 

;