在Jetson Nano上学习ROS的记录(版本Ubuntu18.04,课程来源赵虚左老师的《ROS理论与实践》)第十一章-第一节 机器人系统仿真(URDF相关)

系列文章目录

第一章 ROS空间创建、helloworld的实现、开启多个节点
第二章 话题通信
第三章 服务通信
第四章 参数服务器
第五章 常用指令
第六章 通信机制实操
第七章 ROS通信机制进阶(常用API、Python模块的导入)
第八章 元功能包、节点运行管理launch文件(teleop_twist安装方法)
第九章 重名问题、分布式通信
第十章-第一节 TF坐标变换(内含PyKDL 和PyInit__tf2功能缺失等解决)
第十章-第二节 TF坐标变换实操
第十章-第三节 rosbag、rqt工具箱
第十一章-第一节 机器人系统仿真(URDF相关)
第十一章-第二节 机器人系统仿真(Gazebo相关)
第十二章 机器人导航(仿真)

前言

现在大二,之前大一有幸参加了2021的国赛,很壮烈的拿了个江苏赛区的二等奖。但发现无人机这个题,真的是往堆钱上走了。不上ROS不行,现在来记录一下一个纯小白学习ROS的过程和遇到的问题。防止学弟、学妹们再走我走过的弯路。板子用的是学长给的Jetson Nano(4GB),版本是Ubuntu18.04(已配置好基础ROS所需配置)

一、概述

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

  • 如何创建并显示机器人模型; #URDF
  • 如何搭建仿真环境; #Gazebo
  • 如何实现机器人模型与仿真环境的交互。 #Rviz

本章预期的学习目标如下:

  • 能够独立使用URDF创建机器人模型,并在Rviz和Gazebo中分别显示;
  • 能够使用Gazebo搭建仿真环境;
  • 能够使用机器人模型中的传感器(雷达、摄像头、编码器…)获取仿真环境数据。

1.概念

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

2.相关组件

  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),从相机获取的图像值等
    以“ros- [ROS_DISTRO] -desktop-full”命令安装ROS时,RViz会默认被安装。
    运行使用命令rvizrosrun rviz rviz
    如果rviz没有安装,请调用如下命令自行安装:注意我的版本是ubuntu18.04,melodic

sudo apt install ros-melodic-rviz
  1. gazebo
    Gazebo是一款3D动态模拟器,用于显示机器人模型并创建仿真环境,能够在复杂的室内和室外环境中准确有效地模拟机器人。与游戏引擎提供高保真度的视觉模拟类似,Gazebo提供高保真度的物理模拟,其提供一整套传感器模型,以及对用户和程序非常友好的交互方式。
    以“ros- [ROS_DISTRO] -desktop-full”命令安装ROS时,gzebo会默认被安装。
    运行使用命令gazeborosrun gazebo_ros gazebo

二、URDF集成Rviz基本流程

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

需求描述:
在 Rviz 中显示一个盒状机器人

实现流程:

  • 准备:新建功能包,导入依赖
  • 核心:编写 urdf 文件
  • 核心:在 launch 文件集成 URDF 与 Rviz
  • 在 Rviz 中显示机器人模型

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

mkdir -p 自定义空间名称/src   #创建空间
cd 自定义空间名称             #进入空间
catkin_make        	     #初始化
cd src			     #进入src
catkin_create_pkg 自定义ROS包名 urdf xacro

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

  1. urdf: 存储 urdf 文件的目录
  2. meshes:机器人模型渲染文件(暂不使用)——皮肤
  3. config: 配置文件
  4. launch:存储 launch 启动文件

2.编写 URDF 文件

新建一个子级文件夹:urdf、xacro,文件夹中添加一个.urdf文件,复制如下内容:

<robot name="mycar">
    <link name="base_link">     <!--连杆,机器人的刚性部分的名字-->
        <visual>    <!--可视化,这个机器人看起来是啥样子-->
            <geometry>  <!--几何模型-->
                <box size="0.5 0.2 0.1" />  <!--长、宽、高,单位是m-->
            </geometry>
        </visual>
    </link>
</robot>

3.在 launch 文件中集成 URDF 与 Rviz

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

<launch>

    <!--1.在参数服务器载入 urdf 文件-->
    <!--name 固定为robot_description ; textfile指向urdf文件-->
    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo01_helloworld.urdf"/>
    
    <!--2.启动rviz-->
    <node pkg="rviz" type="rviz" name= "rviz"/>

</launch>

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

记得上一步中需要保存。和编译
快捷键 ctrl + shift + B 调用编译,选择:catkin_make:build(小齿轮)
可以点击配置设置为默认,修改.vscode/tasks.json 文件

{
    
    
// 有关 tasks.json 格式的文档,请参见
    // https://go.microsoft.com/fwlink/?LinkId=733558
    "version": "2.0.0",
    "tasks": [
        {
    
    
            "label": "catkin_make:debug", //代表提示的描述性信息
            "type": "shell",  //可以选择shell或者process,如果是shell代码是在shell里面运行一个命令,如果是process代表作为一个进程来运行
            "command": "catkin_make",//这个是我们需要运行的命令
            "args": [],//如果需要在命令后面加一些后缀,可以写在这里,比如-DCATKIN_WHITELIST_PACKAGES=“pac1;pac2”
            "group": {
    
    "kind":"build","isDefault":true},
            "presentation": {
    
    
                "reveal": "always"//可选always或者silence,代表是否输出信息
            },
            "problemMatcher": "$msCompile"
        }
    ]
}

打开一个命令行:

source ./devel/setup.bash 
roslaunch urdf01_rviz demo01_helloworld.launch

rviz 启动后,会发现并没有盒装的机器人模型,这是因为默认情况下没有添加机器人显示组件,需要手动添加,添加方式如下:
在这里插入图片描述

5.优化 rviz 启动

重复启动launch文件时,Rviz 之前的组件配置信息不会自动保存,需要重复执行步骤4的操作,为了方便使用,可以使用如下方式优化:

首先,将当前配置保存进config目录
然后,launch文件中 Rviz 的启动配置添加参数:args,值设置为-d 配置文件路径

<launch>

    <!--1.在参数服务器载入 urdf 文件-->
    <!--name 固定为robot_description ; textfile指向urdf文件-->
    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo01_helloworld.urdf"/>
    
    <!--2.启动rviz-->
    <node pkg="rviz" type="rviz" name= "rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz"/>

</launch>

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

三、URDF语法详解

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

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

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

1.URDF语法详解01_robot

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

  1. 属性
    name: 指定机器人模型的名称
  2. 子标签 其他标签都是子级标签

2.URDF语法详解02_link

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

  1. 属性
    name —> 为连杆命名
  2. 子标签
    在这里插入图片描述
  3. 案例
    需求:分别生成长方体、圆柱与球体的机器人部件
    urtf代码:
<!--需求:设置不同形状的机器人部件-->
<robot name="mycar">
    <link name="base_link">     <!--第一个刚体:底盘-->
        <visual>        <!--可视化标签-->
            <!--1.形状-->
            <geometry>
                <!--1.1立方体-->
                <!-- <box size="0.3 0.2 0.1"/> -->
                <!--1.2圆柱-->
                <!-- <cylinder radius="0.1" length="2" /> -->
                <!--1.3球体-->
                <!-- <sphere radius="1" /> -->
                <!--1.4皮肤-->
                <mesh filename="package://urdf01_rviz/meshes/autolabor_mini.stl"/>
            </geometry>

            <!--2.偏移量与倾斜弧度-->
            <!--

                x y z设置机器人模型在 x y z 上的偏移量
                r p y用于设置倾斜弧度 x(翻滚) y(俯仰) z(偏航)

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

            <!--3.颜色-->
            <!--

                rgba:
                    r=red
                    g=green
                    b=blue
                    a=透明度
                四者取值属于[0,1]

            -->
            <material name="car_color" >
                <color rgba="0 0 1 1" />
            </material>
        </visual>

    </link>

</robot>

launch代码:

<launch>

    <!--1.在参数服务器载入 urdf 文件-->
    <!--name 固定为robot_description ; textfile指向urdf文件-->
    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo02_link.urdf"/>
    
    <!--2.启动rviz-->
    <node pkg="rviz" type="rviz" name= "rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz"/>
</launch>

编译保存
打开一个命令行:

source ./devel/setup.bash
roslaunch urdf01_rviz demo02_link.launch 

3.URDF语法详解03_joint

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

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

  1. 属性
    在这里插入图片描述
  2. 子标签
    在这里插入图片描述
  3. 案例
    准备:
    打开一个命令行输入:rosrun joint_state_publisher_gui joint_state_publisher_gui
    如若没有这个包,我的版本为ubuntu18.02 melodic
    输入:sudo apt install ros-melodic-joint-state-publisher-gui
    即可安装

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

<!--需求:设置机器人底盘,并添加摄像头-->
<robot name="mycar">
    <!--1.底盘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="car_color" >
                <color rgba="0.8 0.5 0 0.5" />
            </material>
        </visual>
    </link>

    <!--2.摄像头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="camera_color" >
                <color rgba="0 0 1 0.5" />
            </material>
        </visual>
    </link>

    <!--3.关节-->
    <joint name="camera2base" type="continuous">
        <!--父级link-->
        <parent link="base_link" />

        <!--子级link-->
        <child link="camera"/>

        <!--设置偏移量 即设置相对于父级link中心点的偏移量
        其实这里主要是因为,都算的是坐标系中心点,因为上面的link的偏移量为000-->
        <origin xyz="0.12 0 0.075" rpy="0 0 0" />

        <!--设置关节围绕哪个轴旋转,即关节旋转参考的坐标轴 此处是绕z轴旋转,即偏航-->
        <axis xyz="0 0 1" />

    </joint>

</robot>

此处如果报错:

UnicodeEncodeError: 'ascii' codec can't encode characters in position 4-20: ordinal not in range(128)

解决:去除URDF中的中文注释
代码如下:

<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="car_color" >
                <color rgba="0.8 0.5 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="camera_color" >
                <color rgba="0 0 1 0.5" />
            </material>
        </visual>
    </link>

    <joint name="camera2base" 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>
    <!--1.在参数服务器载入 urdf 文件-->
    <!--name 固定为robot_description ; textfile指向urdf文件-->
    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo03_joint.urdf"/>
    
    <!--2.启动rviz-->
    <node pkg="rviz" type="rviz" name= "rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz"/>

    <!--

        rviz中显示urdf时,必须发布不同部件之间的 坐标系 关系
        解决策略:ROS中提供了关于机器人模型显示的坐标发布相关节点(两个)
        rosrun joint_state_publisher joint_state_publisher  #发布关节相关节点
        rosrun robot_state_publisher robot_state_publisher  #发布机器人相关节点
    -->

    <!-- 发布关节相关节点 -->
    <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>

打开一个命令行:

source ./devel/setup.bash 
roslaunch urdf01_rviz demo03_joint.launch
  1. base_footprint优化urdf
    前面实现的机器人模型是半沉到地下的,因为默认情况下:
    底盘的中心点位于地图原点上,所以会导致这种情况产生,可以使用的优化策略,将初始 link 设置为一个尺寸极小的 link(比如半径为0.001m 的球体,或边长为 0.001m 的立方体),然后再在初始 link 上添加底盘等刚体,这样实现,虽然仍然存在初始link半沉的现象,但是基本可以忽略了。这个初始 link一般称之base_footprint URDF文件示例如下:
<!--需求:设置机器人底盘,并添加摄像头-->
<robot name="mycar">

    <!--添加一个尺寸极小的link,再去关联初始的link和base_link
    关节的高度刚好和base_link下沉的高度一致(半个底盘高度)
    其实这里主要是因为,都算的是坐标系中心点-->
    <link name="base_footprint">   
        <visual>     
            <geometry>
                <box size="0.001 0.001 0.001"/>
            </geometry>
        </visual>
    </link>

    <!--1.底盘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="car_color" >
                <color rgba="0.8 0.5 0 0.5" />
            </material>
        </visual>
    </link>

    <!--2.摄像头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="camera_color" >
                <color rgba="0 0 1 0.5" />
            </material>
        </visual>
    </link>

    <!--关联base_footprint与base_link-->
    <joint name="link2footprint" type="fixed">
        <parent link="base_footprint" />

        <child link="base_link"/>

        <origin xyz=" 0 0 0.05" rpy="0 0 0" />
    </joint>

    <!--3.关节-->
    <joint name="camera2base" type="continuous">
        <!--父级link-->
        <parent link="base_link" />

        <!--子级link-->
        <child link="camera"/>

        <!--设置偏移量 即设置相对于父级link中心点的偏移量
        其实这里主要是因为,都算的是坐标系中心点,因为上面的link的偏移量为000-->
        <origin xyz="0.12 0 0.075" rpy="0 0 0" />

        <!--设置关节围绕哪个轴旋转,即关节旋转参考的坐标轴 此处是绕z轴旋转,即偏航-->
        <axis xyz="0 0 1" />

    </joint>

</robot>

此处如果报错:

UnicodeEncodeError: 'ascii' codec can't encode characters in position 4-20: ordinal not in range(128)

解决:去除URDF中的中文注释
代码如下:

<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="car_color" >
                <color rgba="0.8 0.5 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="camera_color" >
                <color rgba="0 0 1 0.5" />
            </material>
        </visual>
    </link>

    <joint name="link2footprint" type="fixed">
        <parent link="base_footprint" />

        <child link="base_link"/>

        <origin xyz=" 0 0 0.05" rpy="0 0 0" />
    </joint>

    <joint name="camera2base" 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>

    <!--1.在参数服务器载入 urdf 文件-->
    <!--name 固定为robot_description ; textfile指向urdf文件-->
    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo04_base_footprint.urdf"/>
    
    <!--2.启动rviz-->
    <node pkg="rviz" type="rviz" name= "rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz"/>

    <!--

        rviz中显示urdf时,必须发布不同部件之间的 坐标系 关系
        解决策略:ROS中提供了关于机器人模型显示的坐标发布相关节点(两个)
        rosrun joint_state_publisher joint_state_publisher  #发布关节相关节点
        rosrun robot_state_publisher robot_state_publisher  #发布机器人相关节点
    -->

    <!-- 发布关节相关节点 -->
    <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>

打开一个命令行:

source ./devel/setup.bash 
roslaunch urdf01_rviz demo04_base_footprint.launch

4.URDF练习

需求描述:

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

实现流程:

  • 创建机器人模型可以分步骤实现
  • 新建 urdf 文件,并与 launch 文件集成
  • 搭建底盘
  • 在底盘上添加两个驱动轮
  • 在底盘上添加两个万向轮

URDF代码如下:

<robot name="mycar">

    <!--1.添加 base_footprint-->
    <link name="base_footprint">
        <visual>
            <geometry>
                <sphere radius="0.001" />
            </geometry>
        </visual>
    </link>

    <!--2.添加底盘-->
    <!--底盘为圆柱状,半径 10cm,高 8cm.底盘离地间距为 1.5cm-->
    <!--2-1.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="baselink_color">
                <color rgba="1 0.5 0.2 0.5" />
            </material>
        </visual>
    </link>
    <!--2-2.joint-->
    <joint name="link2footprint" type="fixed" >
        <parent link="base_footprint" />
        <child link="base_link" />
        <!--车高0.08/2+离地间距0.015-->
        <origin xyz="0 0 0.055" rpy="0 0 0" />
    </joint>

    <!--3.添加驱动轮-->
    <!--两个驱动轮半径为 3.25cm,轮胎宽度1.5cm-->
    <!--3-1.link-->
    <link name="left_wheel">
        <visual>
            <geometry>
                <cylinder radius="0.0325" length="0.015" />
            </geometry>

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

            <material name="left_wheel">
                <color rgba="0 0 0 0.3" />
            </material>
        </visual>
    </link>
    <link name="right_wheel">
        <visual>
            <geometry>
                <cylinder radius="0.0325" length="0.015" />
            </geometry>

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

            <material name="right_wheel">
                <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 车轮半径0.0325-(车高0.08/2+离地间距0.015)
        -->
        <origin xyz="0 0.1 -0.0225" rpy="0 0 0" />
        <axis xyz="0 1 0" />
    </joint>
    <joint name="right2link" type="continuous" >
        <parent link="base_link" />
        <child link="right_wheel" />
        <!--
            x 无偏移
            y 车体半径
            z 车轮半径0.0325-(车高0.08/2+离地间距0.015)
        -->
        <origin xyz="0 -0.1 -0.0225" rpy="0 0 0" />
        <axis xyz="0 1 0" />
    </joint>

    <!--4.添加万向轮-->
    <!--
            形状: 球体
            半径: 0.75 cm
            颜色: 黑色
    -->
    <!--4-1.link-->
    <link name="front_wheel">
        <visual>
            <geometry>
                <sphere radius="0.0075" />
            </geometry>

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

            <material name="front_wheel">
                <color rgba="0 0 0 0.3" />
            </material>
        </visual>
    </link>
    <link name="back_wheel">
        <visual>
            <geometry>
                <sphere radius="0.0075" />
            </geometry>

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

            <material name="back_wheel">
                <color rgba="0 0 0 0.3" />
            </material>
        </visual>
    </link>
    <!--4-2.joint-->
    <joint name="front2link" type="continuous" >
        <parent link="base_link" />
        <child link="front_wheel" />
        <!--
            x 万向轮是在车子下面放置的,取值为:(0,半径)
            y 0
            z 车轮半径0.0075-(车高0.08/2+离地间距0.015)
        -->
        <origin xyz="0.08 0 -0.0475" rpy="0 0 0" />
        <axis xyz="0 1 0" />
    </joint>
    <joint name="back2link" type="continuous" >
        <parent link="base_link" />
        <child link="back_wheel" />
        <!--
            x 万向轮是在车子下面放置的,取值为:(0,半径)
            y 0
            z 车轮半径0.0075-(车高0.08/2+离地间距0.015)
        -->
        <origin xyz="-0.08 0 -0.0475" rpy="0 0 0" />
        <axis xyz="0 1 0" />
    </joint>
</robot>

此处如果报错:

UnicodeEncodeError: 'ascii' codec can't encode characters in position 4-20: ordinal not in range(128)

解决:去除URDF中的中文注释
代码如下:

<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="baselink_color">
                <color rgba="1 0.5 0.2 0.5" />
            </material>
        </visual>
    </link>
    <!--2-2.joint-->
    <joint name="link2footprint" type="fixed" >
        <parent link="base_footprint" />
        <child link="base_link" />
        <origin xyz="0 0 0.055" rpy="0 0 0" />
    </joint>

    <link name="left_wheel">
        <visual>
            <geometry>
                <cylinder radius="0.0325" length="0.015" />
            </geometry>

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

            <material name="left_wheel">
                <color rgba="0 0 0 0.3" />
            </material>
        </visual>
    </link>
    <link name="right_wheel">
        <visual>
            <geometry>
                <cylinder radius="0.0325" length="0.015" />
            </geometry>

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

            <material name="right_wheel">
                <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" />
        <origin xyz="0 0.1 -0.0225" rpy="0 0 0" />
        <axis xyz="0 1 0" />
    </joint>
    <joint name="right2link" type="continuous" >
        <parent link="base_link" />
        <child link="right_wheel" />
        <origin xyz="0 -0.1 -0.0225" rpy="0 0 0" />
        <axis xyz="0 1 0" />
    </joint>

    <!--4-1.link-->
    <link name="front_wheel">
        <visual>
            <geometry>
                <sphere radius="0.0075" />
            </geometry>

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

            <material name="front_wheel">
                <color rgba="0 0 0 0.3" />
            </material>
        </visual>
    </link>
    <link name="back_wheel">
        <visual>
            <geometry>
                <sphere radius="0.0075" />
            </geometry>

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

            <material name="back_wheel">
                <color rgba="0 0 0 0.3" />
            </material>
        </visual>
    </link>
    <!--4-2.joint-->
    <joint name="front2link" type="continuous" >
        <parent link="base_link" />
        <child link="front_wheel" />
        <origin xyz="0.08 0 -0.0475" rpy="0 0 0" />
        <axis xyz="0 1 0" />
    </joint>
    <joint name="back2link" type="continuous" >
        <parent link="base_link" />
        <child link="back_wheel" />
        <origin xyz="-0.08 0 -0.0475" rpy="0 0 0" />
        <axis xyz="0 1 0" />
    </joint>
</robot>

launch文件示例如下:

<launch>
    <!--1.在参数服务器中载入URDF-->
    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo05_test.urdf" />

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

    <!--3.添加关节状态发布节点-->
    <node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />

    <!--4.添加机器人状态发布节点-->
    <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />

    <!--5.关节运动控制节点-->
    <node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />

</launch>

打开一个命令行:

source ./devel/setup.bash 
roslaunch urdf01_rviz demo05_test.launch

在这里插入图片描述

5.URDF工具

在 ROS 中,提供了一些工具来方便 URDF 文件的编写,比如:
check_urdf命令可以检查复杂的 urdf 文件是否存在语法问题
urdf_to_graphiz命令可以查看 urdf 模型结构,显示不同 link 的层级关系

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

cd workspace/src/urdf01_rviz/urdf/urdf
check_urdf demo05_test.urdf

输出:

robot name is: mycar
---------- Successfully Parsed XML ---------------
root Link: base_footprint has 1 child(ren)
    child(1):  base_link
        child(1):  back_wheel
        child(2):  front_wheel
        child(3):  left_wheel
        child(4):  right_wheel

打开一个命令行::

urdf_to_graphiz demo05_test.urdf 
evince mycar.pdf 

在这里插入图片描述

四、URDF优化_xacro

前面 URDF 文件构建机器人模型的过程中,存在若干问题。
问题1:计算难度
问题2:代码复用性差

如果在编程语言中,可以通过变量结合函数直接解决上述问题,在 ROS 中,已经给出了类似编程的优化方案,称之为:Xacro

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

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

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

1.Xacro_快速体验

目的:简单了解 xacro 的基本语法。

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

准备:
创建功能包,导入 urdf 与 xacro。

1.Xacro文件编写
编写 Xacro 文件,以变量的方式封装属性(常量半径、高度、车轮半径…),以函数的方式封装重复实现(车轮的添加)。
代码如下:

<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
    <!--
        上节案例问题:
                                    1.代码复用——Xacro宏(函数)
                                    2.参数设计——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>

打开一个命令行:

cd workspace/src/urdf01_rviz/urdf/xacro
rosrun xacro xacro demo01_helloworld.urdf.xacro > demo01_helloworld.urdf

生成的文件代码如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- =================================================================================== -->
<!-- |    This document was autogenerated by xacro from demo01_helloworld.urdf.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>

2.Xacro_语法详解

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

  1. 属性与算数运算
    用于封装 URDF 中的一些字段,比如: PAI 值,小车的尺寸,轮子半径 …
    属性定义<xacro:property name="xxxx" value="yyyy" />
    属性调用${属性名称}
    算数运算${数学表达式}
    代码如下:
<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
    <!-- 1.属性定义 -->
    <xacro:property name="PI" value="3.1415927" />
    <xacro:property name="radius" value="0.03" />

    <!-- 2.属性调用 -->
    <myuse name="${PI}" />
    <myuse name="${radius}" />

    <!-- 3.算术运算 -->
    <myuse result="${PI/2}" />
    <myuse result="${radius*2}" />
</robot>

打开一个命令行:

cd workspace/src/urdf01_rviz/urdf/xacro
rosrun xacro xacro demo02_field.urdf.xacro

输出:

<?xml version="1.0" encoding="utf-8"?>
<!-- =================================================================================== -->
<!-- |    This document was autogenerated by xacro from demo02_field.urdf.xacro        | -->
<!-- |    EDITING THIS FILE BY HAND IS NOT RECOMMENDED                                 | -->
<!-- =================================================================================== -->
<robot name="mycar">
  <!-- 2.属性调用 -->
  <myuse name="3.1415927"/>
  <myuse name="0.03"/>
  <!-- 3.算术运算 -->
  <myuse result="1.57079635"/>
  <myuse result="0.06"/>
</robot>

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

宏定义

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

    .....

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

</xacro:macro>

宏调用

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

代码如下:

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

    <!-- 2.宏调用 -->
    <xacro:getSun num1="1" num2="5" />
</robot>

打开一个命令行:

cd workspace/src/urdf01_rviz/urdf/xacro
rosrun xacro xacro demo03_macro.urdf.xacro

输出:

<?xml version="1.0" encoding="utf-8"?>
<!-- =================================================================================== -->
<!-- |    This document was autogenerated by xacro from demo03_macro.urdf.xacro        | -->
<!-- |    EDITING THIS FILE BY HAND IS NOT RECOMMENDED                                 | -->
<!-- =================================================================================== -->
<robot name="mycar">
  <result value="6"/>
</robot>
  1. 文件包含
    机器人由多部件组成,不同部件可能封装为单独的 xacro 文件,最后再将不同的文件集成,组合为完整机器人,可以使用文件包含实现<xacro:include filename=".xacro文件" />
    代码如下:
<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
    <!-- 演示文件包含 -->
    <xacro:include filename="demo02_field.urdf.xacro" />
    <xacro:include filename="demo03_macro.urdf.xacro" />
</robot>

输出:

<?xml version="1.0" encoding="utf-8"?>
<!-- =================================================================================== -->
<!-- |    This document was autogenerated by xacro from demo04_sum.urdf.xacro          | -->
<!-- |    EDITING THIS FILE BY HAND IS NOT RECOMMENDED                                 | -->
<!-- =================================================================================== -->
<robot name="mycar">
  <!-- 2.属性调用 -->
  <myuse name="3.1415927"/>
  <myuse name="0.03"/>
  <!-- 3.算术运算 -->
  <myuse result="1.57079635"/>
  <myuse result="0.06"/>
  <result value="6"/>
</robot>

3.Xacro_完整使用流程示例

需求描述:
使用 Xacro 优化 URDF 版的小车底盘模型实现
1.编写 Xacro 文件

<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
    <xacro:property name="footprint_radius" value="0.001" />
    <!--1.添加 base_footprint-->
    <link name="base_footprint">
        <visual>
            <geometry>
                <sphere radius="${footprint_radius}" />
            </geometry>
        </visual>
    </link>

    <xacro:property name="base_radius" value="0.1" />  
    <xacro:property name="base_length" value="0.08" />
    <xacro:property name="base_lidijianju" value="0.015" />
    <xacro:property name="base_joint_z" value="${base_length/2+base_lidijianju}" />
    <!--2.添加底盘-->
    <!--底盘为圆柱状,半径 10cm,高 8cm.底盘离地间距为 1.5cm-->
    <!--2-1.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="baselink_color">
                <color rgba="1 0.5 0.2 0.5" />
            </material>
        </visual>
    </link>
    <!--2-2.joint-->
    <joint name="link2footprint" type="fixed" >
        <parent link="base_footprint" />
        <child link="base_link" />
        <!--车高0.08/2+离地间距0.015-->
        <origin xyz="0 0 ${base_joint_z}" rpy="0 0 0" />
    </joint>

    <!-- 本质:相同的参数属性封装,不同的参数进行传参 -->
    <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_joint_z}" />

    <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_name}_wheel">
                    <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 车轮半径0.0325-(车高0.08/2+离地间距0.015)
            -->
            <origin xyz="0 ${flag*0.1} ${wheel_joint_z}" 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" />

    <!--4.添加万向轮-->
    <!--
            形状: 球体
            半径: 0.75 cm
            颜色: 黑色
    -->
    <xacro:property name="small_wheel_radius" value="0.0075" />
    <xacro:property name="small_wheel_z" value="${small_wheel_radius-base_joint_z}" />  
    
    <xacro:macro name="small_wheel_func" params="small_wheel_name flag" >
        <!--4-1.link-->
        <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="${small_wheel_name}_wheel">
                    <color rgba="0 0 0 0.3" />
                </material>
            </visual>
        </link>

        <!--4-2.joint-->
        <joint name="${small_wheel_name}2link" type="continuous" >
            <parent link="base_link" />
            <child link="${small_wheel_name}_wheel" />
            <!--
                x 万向轮是在车子下面放置的,取值为:(0,半径)
                y 0
                z 车轮半径0.0075-(车高0.08/2+离地间距0.015)
            -->
            <origin xyz="${flag*0.08} 0 ${small_wheel_z}" rpy="0 0 0" />
            <axis xyz="0 1 0" />
        </joint>
    </xacro:macro>

    <xacro:small_wheel_func small_wheel_name="front" flag="1" />
    <xacro:small_wheel_func small_wheel_name="back" flag="-1" />

</robot>

编译运行
打开一个命令行:

cd workspace/src/urdf01_rviz/urdf/xacro/
rosrun xacro xacro demo05_car_base.urdf.xacro > demo05_car_base.urdf

打开demo05_car_base.urdf,消除中间的中文注释:

<?xml version="1.0" encoding="utf-8"?>
<!-- =================================================================================== -->
<!-- |    This document was autogenerated by xacro from demo05_car_base.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="baselink_color">
        <color rgba="1 0.5 0.2 0.5"/>
      </material>
    </visual>
  </link>
  <!--2-2.joint-->
  <joint name="link2footprint" type="fixed">
    <parent link="base_footprint"/>
    <child link="base_link"/>
    <origin rpy="0 0 0" xyz="0 0 0.055"/>
  </joint>
  <link name="left_wheel">
    <visual>
      <geometry>
        <cylinder length="0.015" radius="0.0325"/>
      </geometry>
      <origin rpy="1.57079635 0 0" xyz="0 0 0"/>
      <material name="left_wheel">
        <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"/>
    <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.015" radius="0.0325"/>
      </geometry>
      <origin rpy="1.57079635 0 0" xyz="0 0 0"/>
      <material name="right_wheel">
        <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"/>
    <origin rpy="0 0 0" xyz="0 -0.1 -0.0225"/>
    <axis xyz="0 1 0"/>
  </joint>
  <!--4-1.link-->
  <link name="front_wheel">
    <visual>
      <geometry>
        <sphere radius="0.0075"/>
      </geometry>
      <origin rpy="0 0 0" xyz="0 0 0"/>
      <material name="front_wheel">
        <color rgba="0 0 0 0.3"/>
      </material>
    </visual>
  </link>
  <!--4-2.joint-->
  <joint name="front2link" type="continuous">
    <parent link="base_link"/>
    <child link="front_wheel"/>
    <origin rpy="0 0 0" xyz="0.08 0 -0.0475"/>
    <axis xyz="0 1 0"/>
  </joint>
  <!--4-1.link-->
  <link name="back_wheel">
    <visual>
      <geometry>
        <sphere radius="0.0075"/>
      </geometry>
      <origin rpy="0 0 0" xyz="0 0 0"/>
      <material name="back_wheel">
        <color rgba="0 0 0 0.3"/>
      </material>
    </visual>
  </link>
  <!--4-2.joint-->
  <joint name="back2link" type="continuous">
    <parent link="base_link"/>
    <child link="back_wheel"/>
    <origin rpy="0 0 0" xyz="-0.08 0 -0.0475"/>
    <axis xyz="0 1 0"/>
  </joint>
</robot>

launch文件代码如下:

<launch>
    <!--1.在参数服务器中载入URDF-->
    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/xacro/demo05_car_base.urdf" />
    <!-- <param name="robot_description" command="$(find xacro)/xacro $(find urdf01_rviz)/urdf/xacro/demo05_car_base.urdf.xacro" /> -->

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

    <!--3.添加关节状态发布节点-->
    <node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />

    <!--4.添加机器人状态发布节点-->
    <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />

    <!--5.关节运动控制节点-->
    <node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />

</launch>

打开一个命令行:

cd workspace
roslaunch urdf01_rviz demo06_car_base.launch

4.Xacro_实操

需求描述:
在前面小车底盘基础之上,添加摄像头和雷达传感器。

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

实现流程:

  • 首先编写摄像头和雷达的 xacro 文件
  • 然后再编写一个组合文件,组合底盘、摄像头与雷达
  • 最后,通过 launch 文件启动 Rviz
    并显示模型
  1. 摄像头和雷达 Xacro 文件实现
    摄像头 xacro 文件:
<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
    <!-- 摄像头部件 -->
    <!-- 1.参数 -->
    <!-- 
            参数:
                    连杆属性:厚度、宽度、高度
                    关节属性:x y z
     -->
    <xacro:property name="camera_length" value="0.02" /> <!--houdu  x-->
    <xacro:property name="camera_width" value="0.05" /> <!--kuandu  y-->
    <xacro:property name="camera_height" value="0.05" /> <!--height  z-->
    <xacro:property name="joint_camera_x" value="0.08" /> 
    <xacro:property name="joint_camera_y" value="0" /> 
    <xacro:property name="joint_camera_z" value="${(camera_height+base_length)/2}" />

    <!-- 2.设计连杆和关节 -->
    <link name="camera">
        <visual>
            <geometry>
                <box size="${camera_length} ${camera_width} ${camera_height}" />
            </geometry>

            <material name="black">
                <color rgba="0 0 0 0.8" />
            </material>
        </visual>
    </link>
    <joint name="camera2base" type="fixed">
        <parent link="base_link" />
        <child link="camera" />
        <origin xyz="${joint_camera_x} ${joint_camera_y} ${joint_camera_z}" rpy="0 0 0" />
    </joint>
</robot>

雷达 xacro 文件:

<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
    <!-- 雷达部件 -->
    <!-- 
        参数:
                1.支架
                    支架尺寸:半径0.01 高度0.15
                    关节偏移量:x y z
                2.雷达
                    尺寸:半径0.03 高度0.05
                    关节偏移量:x y z
     -->
    <xacro:property name="support_radius" value="0.01" /> 
    <xacro:property name="support_length" value="0.15" />

    <xacro:property name="laser_length" value="0.05" /> 
    <xacro:property name="laser_radius" value="0.03" />

    <xacro:property name="joint_support_x" value="0.0" /> 
    <xacro:property name="joint_support_y" value="0.0" /> 
    <xacro:property name="joint_support_z" value="${base_length / 2 + support_length / 2}" />

    <xacro:property name="joint_laser_x" value="0.0" /> 
    <xacro:property name="joint_laser_y" value="0.0" /> 
    <xacro:property name="joint_laser_z" value="${support_length / 2 + laser_length / 2}" />
    <!-- 1.支架 -->
    <link name="support">
        <visual>
            <geometry>
                <cylinder radius="${support_radius}" length="${support_length}" />
            </geometry>
            <material name="yellow">
                <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>
    <!-- 2.雷达 -->
    <link name="laser">
        <visual>
            <geometry>
                <cylinder radius="${laser_radius}" length="${laser_length}" />
            </geometry>
            <material name="black">
                <color rgba="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>
  1. 组合底盘摄像头与雷达的 xacro 文件
<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
    <!--包含底盘、摄像头、雷达的xacro文件-->
    <xacro:include filename="demo05_car_base.urdf.xacro" />
    <xacro:include filename="demo06_car_camera.urdf.xacro" />
    <xacro:include filename="demo07_car_laser.urdf.xacro" />
</robot>

打开一个命令行:

cd src/urdf01_rviz/urdf/xacro/
rosrun xacro xacro car.urdf.xacro > car.urdf

打开car.urdf,消除中间的中文注释:

<?xml version="1.0" encoding="utf-8"?>
<!-- =================================================================================== -->
<!-- |    This document was autogenerated by xacro from car.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>
  <!--2-1.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="baselink_color">
        <color rgba="1 0.5 0.2 0.5"/>
      </material>
    </visual>
  </link>
  <!--2-2.joint-->
  <joint name="link2footprint" type="fixed">
    <parent link="base_footprint"/>
    <child link="base_link"/>
    <origin rpy="0 0 0" xyz="0 0 0.055"/>
  </joint>
  <link name="left_wheel">
    <visual>
      <geometry>
        <cylinder length="0.015" radius="0.0325"/>
      </geometry>
      <origin rpy="1.57079635 0 0" xyz="0 0 0"/>
      <material name="left_wheel">
        <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"/>
    <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.015" radius="0.0325"/>
      </geometry>
      <origin rpy="1.57079635 0 0" xyz="0 0 0"/>
      <material name="right_wheel">
        <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"/>
    <origin rpy="0 0 0" xyz="0 -0.1 -0.0225"/>
    <axis xyz="0 1 0"/>
  </joint>
  <!--4-1.link-->
  <link name="front_wheel">
    <visual>
      <geometry>
        <sphere radius="0.0075"/>
      </geometry>
      <origin rpy="0 0 0" xyz="0 0 0"/>
      <material name="front_wheel">
        <color rgba="0 0 0 0.3"/>
      </material>
    </visual>
  </link>
  <!--4-2.joint-->
  <joint name="front2link" type="continuous">
    <parent link="base_link"/>
    <child link="front_wheel"/>
    <origin rpy="0 0 0" xyz="0.08 0 -0.0475"/>
    <axis xyz="0 1 0"/>
  </joint>
  <!--4-1.link-->
  <link name="back_wheel">
    <visual>
      <geometry>
        <sphere radius="0.0075"/>
      </geometry>
      <origin rpy="0 0 0" xyz="0 0 0"/>
      <material name="back_wheel">
        <color rgba="0 0 0 0.3"/>
      </material>
    </visual>
  </link>
  <!--4-2.joint-->
  <joint name="back2link" type="continuous">
    <parent link="base_link"/>
    <child link="back_wheel"/>
    <origin rpy="0 0 0" xyz="-0.08 0 -0.0475"/>
    <axis xyz="0 1 0"/>
  </joint>
  <link name="camera">
    <visual>
      <geometry>
        <box size="0.02 0.05 0.05"/>
      </geometry>
      <material name="black">
        <color rgba="0 0 0 0.8"/>
      </material>
    </visual>
  </link>
  <joint name="camera2base" type="fixed">
    <parent link="base_link"/>
    <child link="camera"/>
    <origin rpy="0 0 0" xyz="0.08 0 0.065"/>
  </joint>
  <link name="support">
    <visual>
      <geometry>
        <cylinder length="0.15" radius="0.01"/>
      </geometry>
      <material name="yellow">
        <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 rpy="0 0 0" xyz="0.0 0.0 0.115"/>
  </joint>
  <link name="laser">
    <visual>
      <geometry>
        <cylinder length="0.05" radius="0.03"/>
      </geometry>
      <material name="black">
        <color rgba="0 0 0 0.5"/>
      </material>
    </visual>
  </link>
  <joint name="laser2support" type="fixed">
    <parent link="support"/>
    <child link="laser"/>
    <origin rpy="0 0 0" xyz="0.0 0.0 0.1"/>
  </joint>
</robot>

launch文件代码如下:

<launch>
    <!--1.在参数服务器中载入URDF-->
    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/xacro/car.urdf" />
    <!-- <param name="robot_description" command="$(find xacro)/xacro $(find urdf01_rviz)/urdf/xacro/demo05_car_base.urdf.xacro" /> -->

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

    <!--3.添加关节状态发布节点-->
    <node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />

    <!--4.添加机器人状态发布节点-->
    <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />

    <!--5.关节运动控制节点-->
    <node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />

</launch>

打开一个命令行:

cd workspace
roslaunch urdf01_rviz car.launch

在这里插入图片描述

五、Rviz中控制机器人模型运动

通过 URDF 结合 rviz 可以创建并显示机器人模型,不过,当前实现的只是静态模型,如何控制模型的运动呢?在此,可以调用 Arbotix 实现此功能。

Arbotix:Arbotix 是一款控制电机、舵机的控制板,并提供相应的 ros 功能包,这个功能包的功能不仅可以驱动真实的 Arbotix 控制板,它还提供一个差速控制器,通过接受速度控制指令更新机器人的 joint 状态,从而帮助我们实现机器人在 rviz 中的运动。

这个差速控制器在 arbotix_python 程序包中,完整的 arbotix 程序包还包括多种控制器,分别对应 dynamixel 电机、多关节机械臂以及不同形状的夹持器。

1.Arbotix使用流程

接下来,通过一个案例演示 arbotix 的使用。

需求描述:
控制机器人模型在 rviz 中做圆周运动

实现流程:

  • 安装 Arbotix
  • 创建新功能包,准备机器人 urdf、xacro 文件
  • 添加 Arbotix 配置文件
  • 编写 launch 文件配置Arbotix
  • 启动 launch 文件并控制机器人模型运动
  1. 安装 Arbotix,我的版本是ubuntu18.04,melodic
sudo apt-get install ros-melodic-arbotix
  1. 添加 arbotix 所需的配置文件 ymal
    添加 arbotix 所需配置文件
    代码如下:
# 该文件是控制器配置,一个机器人模型可能有多个控制器,比如: 底盘、机械臂、夹持器(机械手)....
# 因此,根 name 是 controller
controllers: {
    
    
   # 单控制器设置
   base_controller: {
    
    
          #类型: 差速控制器
       type: diff_controller,
       #参考坐标
       base_frame_id: base_footprint, 
       #两个轮子之间的间距
       base_width: 0.2,
       #控制频率
       ticks_meter: 2000, 
       #PID控制参数,使机器人车轮快速达到预期速度
       Kp: 12, 
       Kd: 12, 
       Ki: 0, 
       Ko: 50, 
       #加速限制
       accel_limit: 1.0 
    }
}
  1. launch 文件中配置 arbotix 节点
    launch 示例代码
<launch>
    <!--1.在参数服务器中载入URDF-->
    <param name="robot_description" textfile="$(find urdf01_rviz)/urdf/xacro/car.urdf" />
    <!-- <param name="robot_description" command="$(find xacro)/xacro $(find urdf01_rviz)/urdf/xacro/demo05_car_base.urdf.xacro" /> -->

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

    <!--3.添加关节状态发布节点-->
    <node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />

    <!--4.添加机器人状态发布节点-->
    <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />

    <!-- 集成arbotix运动控制节点,并且加载参数 -->
    <node pkg="arbotix_python" type="arbotix_driver" name="driver" output="screen" >
        <!-- 载入control.yaml中的参数 -->
        <rosparam command="load" file="$(find urdf01_rviz)/config/control.yaml" />
        <!-- 传入仿真相关参数 -->
        <param name="sim" value="true" />
    </node>
</launch>

打开一个命令行:

cd workspace
source ./devel/setup.bash
roslaunch urdf01_rviz demo07_control.launch

打开一个命令行:

cd workspace
source ./devel/setup.bash
rostopic list

输出:

/clicked_point
/cmd_vel
/diagnostics
/initialpose
/joint_states
/move_base_simple/goal
/odom
/rosout
/rosout_agg
/tf
/tf_static

也就说我们可以发布 cmd_vel 话题消息控制小车运动了,该实现策略有多种,可以另行编写节点,或者更简单些可以直接通过如下命令发布消息:(tab补齐)

rostopic pub -r 10 /cmd_vel geometry_msgs/Twist "linear:
  x: 1.0
  y: 0.0
  z: 0.0
angular:
  x: 0.0
  y: 0.0
  z: 1.0" 

在这里插入图片描述

总结

以上就是今天要讲的内容,本文仅仅简单记录了ROS的机器人系统仿真(URDF相关),如果有问题请在博客下留言或者咨询邮箱:[email protected]

猜你喜欢

转载自blog.csdn.net/layra_liu/article/details/125612364