目录
1、前言
本人是JUN自动化专业大三的学生,自学ROS(robot operation system--机器人操作系统)已经有半年的时间,在学习ros之前对于ros系统属于一问三不知的学习情况,只是对于python基本的语法(例如基本的数组结构和书写格式)但当涉略。也是一路摸爬滚打学习ros过来的,再次也非常感谢我的导师给我这个机会和平台学习ROS系统。
写下这篇文章的初衷是为了记录我的ROS学习路程,也是为了温习之前ROS知识操作体系,如果有错误的地方请读者多多包涵。
2、创建工作空间
2.1、mkdir -p test01_ws/src创建文件夹
# 注意是在根目录下创建工作空间
mkdir -p test01_ws/src
2.2、进入工作空间中,cd test01_ws/
# cd指令是linux常用指令,cd+刚创立的文件夹即可进入工作空间
cd test01_ws/
2.3、使用catkin_make,会多出build和devel文件夹
# 输入指令之前要注意当前的目录是否为刚刚创建的test01_ws/目录
catkin_make
2.4、在test01_ws目录下输入code .启动vs code
# 在test01_ws/目录下输入此指令会自动弹出vs code编译界面并且自动跳转到工作空间
code .
2.5、快捷键Crtl+Shift+B调用编译,选择catkin_make:build,点击后面的小齿轮配置文件,然后修改.vscode/tasks.json文件(注意保存修改后的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"
}
]
}
2.6、选择src右键,选择create catkin package添加依赖项,先输入src下工作空间的名字urdf01_multi并且按回车开始输入依赖项,这里的依赖项包括gazebo_plugins gazebo_ros gazebo_ros_control urdf xacro(每个依赖项之间使用空格隔开)。
2.7(选做)、如果担心输入依赖项错误,可以打开src下的子文件下的package.xml文件使用快捷键Crtl+Shift+B进行编译,不报错就是正确的。
2.8、在我们刚刚创建的src目录下创建几个文件夹,分别是config、launch、urdf、worlds,同时在urdf文件夹下再创建一个子文件夹gazebo。下面介绍一下创建这些文件夹的意义。
config文件夹:存放我们在rviz中的小车及地图配置文件。
launch文件夹:顾名思义launch是启动的意思,我们需要在此文件夹中存放启动小车模型的文件。
urdf文件夹:存放小车模型配置文件。
worlds文件夹:存放gazebo的地图模型文件。
gazebo文件:存放后期用于控制小车运动、摄像头和雷达传感器配置的文件。
3、创建单个小车的底盘
3.1、在刚刚创建的urdf文件夹下创建名为demo01_car_base.urdf.xacro和head.xacro的文件,下面介绍一下各个文件夹的功能。
demo01_car_base:包含了单个小车的配置代码(包括底盘,驱动轮和万向轮)
car.urdf.xacro:用于集成小车配置文件和控制运动的文件
head.xacro:存放惯性矩阵的代码
3.2、在head.xacro文件中复制粘贴标准的球体、圆柱与立方体的惯性矩阵公式代码(已经封装为了xacro文件)。
<robot name="base" xmlns:xacro="http://wiki.ros.org/xacro">
<!-- Macro for inertia matrix -->
<xacro:macro name="sphere_inertial_matrix" params="m r">
<inertial>
<mass value="${m}" />
<inertia ixx="${2*m*r*r/5}" ixy="0" ixz="0"
iyy="${2*m*r*r/5}" iyz="0"
izz="${2*m*r*r/5}" />
</inertial>
</xacro:macro>
<xacro:macro name="cylinder_inertial_matrix" params="m r h">
<inertial>
<mass value="${m}" />
<inertia ixx="${m*(3*r*r+h*h)/12}" ixy = "0" ixz = "0"
iyy="${m*(3*r*r+h*h)/12}" iyz = "0"
izz="${m*r*r/2}" />
</inertial>
</xacro:macro>
<xacro:macro name="Box_inertial_matrix" params="m l w h">
<inertial>
<mass value="${m}" />
<inertia ixx="${m*(h*h + l*l)/12}" ixy = "0" ixz = "0"
iyy="${m*(w*w + l*l)/12}" iyz= "0"
izz="${m*(w*w + h*h)/12}" />
</inertial>
</xacro:macro>
</robot>
3.3、在demo01_car_base.urdf.xacro文件中输入小车底盘中心点和小车底盘的代码,同时还要输入两者之间通过关节连接的配置代码。
<!-- 根标签,必须声明 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>
<material name="yellow">
<color rgba="0.5 0.3 0.0 0.5"/>
</material>
<!-- 底盘属性 -->
<!-- 小车底盘中心点半径 -->
<xacro:property name="base_footprint_radius" value="0.001"/>
<!--小车底盘半径 -->
<xacro:property name="base_link_radius" value="0.1"/>
<!-- 小车底盘高度 -->
<xacro:property name="base_link_length" value="0.08"/>
<!-- 小车底盘质量 -->
<xacro:property name="base_mass" value="2"/>
<!-- 小车底盘离地高度 -->
<xacro:property name="map_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"/>
</visual>
<collision>
<geometry>
<cylinder radius="${base_link_radius}" length="${base_link_length}"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
</collision>
<xacro:cylinder_inertial_matrix m="${base_mass}" r="${base_link_radius}" h="${base_link_length}"/>
</link>
<gazebo reference="base_link">
<material>Gazebo/Yellow</material>
</gazebo>
<!-- 小车底盘和底盘中心的关节连接 -->
<joint name="base_link_to_base_footprint" type="fixed">
<parent link="base_footprint"/>
<child link="base_link"/>
<!-- 关节的重心应该位于小车离地间距+一半的小车底盘高度 -->
<origin xyz="0 0 ${map_space + base_link_length / 2}"/>
</joint>
</robot>
3.4、在car.urdf.xacro文件中输入集成了demo01_car_base.urdf.xacro和head.xacro文件的代码。
<robot name="my_laser" xmlns:xacro="http://wiki.ros.org/xacro">
<!-- 包含惯性矩阵 -->
<xacro:include filename="head.xacro"/>
<!-- 包含小车配置的xacro文件 -->
<xacro:include filename="demo01_car_base.urdf.xacro"/>
</robot>
3.5、在demo01_car_base.launch文件中输入启动小车配置、Gazebo仿真环境和rviz仿真环境的启动代码。
<launch>
<!-- 将xacro小车文件加载到参数服务器 -->
<param name="robot_description" command ="$(find xacro)/xacro $(find urdf01_multi)/urdf/car.urdf.xacro"/>
<!-- 启动gazebo -->
<include file="$(find gazebo_ros)/launch/empty_world.launch"/>
<!-- 在gazebo中显示机器人模型 -->
<node pkg="gazebo_ros" type="spawn_model" name="car" args="-urdf -model mycar -param robot_description"/>
<!-- 发布机器人关节和状态节点 -->
<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"/>
<!-- 启动rviz -->
<node pkg="rviz" type="rviz" name="rviz"/>
</launch>
3.6、在vs code中使用快捷键Crtl+`启用终端,并且回到test01_ws/的路径下,输入source ./devel/setup.bash将当前目录作为工作空间,这样就可以使用roslaunch的指令
#在需求目录下输入指令,将该目录作为工作空间
source ./devel/setup.bash
在工作空间输入该指令后,就可以使用指令roslaunch urdf01_multi demo01_car_base.launch启动我们的小车了,使用roslaunch指令后,程序就会自动启动gazebo和rviz的仿真环境
在Gazebo仿真环境下,可以看到我们创建的小车底盘和一个基坐标系
我们第一次启动rviz环境时,会发现有报错的存在,而且rviz中也没有出现小车模型,这是因为我们打开的是一个空白的rviz仿真环境,需要我们自己加入小车模型并且保存rviz的配置,这样也可以方便下次使用rviz插件。
此时,我们需要将Fixed Frame(默认值为map)改成我们刚才命名的小车底盘中心(博主这里命名的是base_footprint),然后点击右下角的Add,添加RobotModel,这样就可以正确显示小车底盘了(当然也可以再添加一个TF,用于观察小车每个节点之间的坐标系变换)。
3.7、在rviz仿真界面使用快捷键Crtl+Shift+S保存我们的rviz配置,将该文件命名为XXX.rviz,并且将该配置文件保存到我们之前创建的config目录(在2.8节创建的config文件)下面
3.8、保存好了之后,回到vs code里的终端,在终端使用快捷键Crtl+C就可以停止程序的运行
3.9、随后我们再对launch文件中的启动rviz处代码进行修改,在原有代码的基础上加入新代码args="-d $(find urdf01_multi)/config/mycar.rviz",这新添加的代码目的是将我们刚刚在config下保存的rviz配置文件集成进启动rviz的环节,我们下次运行launch文件后,rviz中就会复现我们上次保存过的配置了(即再次打开修改后的launch文件,可以在rviz仿真环境下看到小车,无需手动添加小车模型)。
<!-- 启动rviz -->
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_multi)/config/mycar.rviz"/>
附加修改之后的launch文件代码如下:
<launch>
<!-- 将xacro小车文件加载到参数服务器 -->
<param name="robot_description" command ="$(find xacro)/xacro $(find urdf01_multi)/urdf/car.urdf.xacro"/>
<!-- 启动gazebo -->
<include file="$(find gazebo_ros)/launch/empty_world.launch"/>
<!-- 在gazebo中显示机器人模型 -->
<node pkg="gazebo_ros" type="spawn_model" name="car" args="-urdf -model mycar -param robot_description"/>
<!-- 发布机器人关节和状态节点 -->
<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"/>
<!-- 启动rviz -->
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_multi)/config/mycar.rviz"/>
</launch>
4、添加小车的驱动轮和万向轮
4.1、添加驱动轮(小车左右轮子)代码如下,需注意的是,这部分代码需要包含在<robot>...</robot>代码块之中。
<!-- 驱动轮属性封装 -->
<!-- 驱动轮半径 -->
<xacro:property name="wheel_radius" value="0.0325"/>
<!-- 驱动轮宽度 -->
<xacro:property name="wheel_length" value="0.015"/>
<!-- 驱动轮质量 -->
<xacro:property name="wheel_mass" value="0.05"/>
<!-- 驱动轮宏代码实现 -->
<xacro:macro name="driving_wheels" params="name flag">
<!-- 小车驱动轮连杆代码 -->
<link name="base_${name}">
<visual>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}"/>
</geometry>
<origin xyz="0 0 0" rpy="${PI/2} 0 0"/>
<material name="black"/>
</visual>
<collision>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}"/>
</geometry>
<origin xyz="0 0 0" rpy="${PI/2} 0 0"/>
</collision>
<xacro:cylinder_inertial_matrix m="${wheel_mass}" r="${wheel_radius}" h="${wheel_length}"/>
</link>
<gazebo reference="base_${name}">
<material>Gazebo/Red</material>
</gazebo>
<!-- 驱动轮和底盘代码连杆实现 -->
<joint name="${name}" type="continuous">
<parent link="base_link"/>
<child link="base_${name}"/>
<origin xyz="0 ${flag * base_link_radius} ${-(map_space + base_link_length / 2 - wheel_radius)}"/>
<axis xyz="0 1 0"/>
</joint>
</xacro:macro>
<xacro:driving_wheels name="left_wheel" flag="1"/>
<xacro:driving_wheels name="right_wheel" flag="-1"/>
重复步骤3.6,可以看到在rviz环境中拥有两个轮子的小车模型:
4.2、按照同样的方法,添加小车万向轮的代码如下:
<!-- 支撑轮宏属性封装 -->
<!-- 支撑轮半径 -->
<xacro:property name="support_wheel_radius" value="0.0075"/>
<!-- 支撑轮质量 -->
<xacro:property name="support_wheel_mass" value="0.01"/>
<!-- 支撑轮宏 -->
<xacro:macro name="support_wheel" params="name flag">
<link name="${name}">
<visual>
<geometry>
<sphere radius="${support_wheel_radius}"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
<material name="black"/>
</visual>
<collision>
<geometry>
<sphere radius="${support_wheel_radius}"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
</collision>
<xacro:sphere_inertial_matrix m="${support_wheel_mass}" r="${support_wheel_radius}"/>
</link>
<gazebo reference="${name}">
<material>Gazebo/Red</material>
</gazebo>
<!-- 支撑轮与底盘的关节连接实现 -->
<joint name="${name}_to_base_link" type="continuous">
<parent link="base_link"/>
<child link="${name}"/>
<origin xyz="${flag * (base_link_radius - support_wheel_radius)} 0 ${-(base_link_length / 2 + map_space / 2)}" />
<axis xyz="1 1 1"/>
</joint>
</xacro:macro>
<xacro:support_wheel name="front_wheel" flag="1"/>
<xacro:support_wheel name="back_wheel" flag="-1"/>
重复步骤3.6,在rviz中添加tf插件,可以观察到小车万向轮tf坐标如下:
4.3、到此为止,单个小车的xacro配置文件已经编写完毕,代码大体如下:
<!-- 根标签,必须声明 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>
<material name="yellow">
<color rgba="0.5 0.3 0.0 0.5"/>
</material>
<!-- 底盘属性 -->
<!-- 小车底盘中心点半径 -->
<xacro:property name="base_footprint_radius" value="0.001"/>
<!--小车底盘半径 -->
<xacro:property name="base_link_radius" value="0.1"/>
<!-- 小车底盘高度 -->
<xacro:property name="base_link_length" value="0.08"/>
<!-- 小车底盘质量 -->
<xacro:property name="base_mass" value="2"/>
<!-- 小车底盘离地高度 -->
<xacro:property name="map_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"/>
</visual>
<collision>
<geometry>
<cylinder radius="${base_link_radius}" length="${base_link_length}"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
</collision>
<xacro:cylinder_inertial_matrix m="${base_mass}" r="${base_link_radius}" h="${base_link_length}"/>
</link>
<gazebo reference="base_link">
<material>Gazebo/Yellow</material>
</gazebo>
<!-- 小车底盘和底盘中心的关节连接 -->
<joint name="base_link_to_base_footprint" type="fixed">
<parent link="base_footprint"/>
<child link="base_link"/>
<!-- 关节的重心应该位于小车离地间距+一半的小车底盘高度 -->
<origin xyz="0 0 ${map_space + base_link_length / 2}"/>
</joint>
<!-- 驱动轮属性封装 -->
<!-- 驱动轮半径 -->
<xacro:property name="wheel_radius" value="0.0325"/>
<!-- 驱动轮宽度 -->
<xacro:property name="wheel_length" value="0.015"/>
<!-- 驱动轮质量 -->
<xacro:property name="wheel_mass" value="0.05"/>
<!-- 驱动轮宏代码实现 -->
<xacro:macro name="driving_wheels" params="name flag">
<!-- 小车驱动轮连杆代码 -->
<link name="base_${name}">
<visual>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}"/>
</geometry>
<origin xyz="0 0 0" rpy="${PI/2} 0 0"/>
<material name="black"/>
</visual>
<collision>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}"/>
</geometry>
<origin xyz="0 0 0" rpy="${PI/2} 0 0"/>
</collision>
<xacro:cylinder_inertial_matrix m="${wheel_mass}" r="${wheel_radius}" h="${wheel_length}"/>
</link>
<gazebo reference="base_${name}">
<material>Gazebo/Red</material>
</gazebo>
<!-- 驱动轮和底盘代码连杆实现 -->
<joint name="${name}" type="continuous">
<parent link="base_link"/>
<child link="base_${name}"/>
<origin xyz="0 ${flag * base_link_radius} ${-(map_space + base_link_length / 2 - wheel_radius)}"/>
<axis xyz="0 1 0"/>
</joint>
</xacro:macro>
<xacro:driving_wheels name="left_wheel" flag="1"/>
<xacro:driving_wheels name="right_wheel" flag="-1"/>
<!-- 支撑轮宏属性封装 -->
<!-- 支撑轮半径 -->
<xacro:property name="support_wheel_radius" value="0.0075"/>
<!-- 支撑轮质量 -->
<xacro:property name="support_wheel_mass" value="0.01"/>
<!-- 支撑轮宏 -->
<xacro:macro name="support_wheel" params="name flag">
<link name="${name}">
<visual>
<geometry>
<sphere radius="${support_wheel_radius}"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
<material name="black"/>
</visual>
<collision>
<geometry>
<sphere radius="${support_wheel_radius}"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
</collision>
<xacro:sphere_inertial_matrix m="${support_wheel_mass}" r="${support_wheel_radius}"/>
</link>
<gazebo reference="${name}">
<material>Gazebo/Red</material>
</gazebo>
<!-- 支撑轮与底盘的关节连接实现 -->
<joint name="${name}_to_base_link" type="continuous">
<parent link="base_link"/>
<child link="${name}"/>
<origin xyz="${flag * (base_link_radius - support_wheel_radius)} 0 ${-(base_link_length / 2 + map_space / 2)}" />
<axis xyz="1 1 1"/>
</joint>
</xacro:macro>
<xacro:support_wheel name="front_wheel" flag="1"/>
<xacro:support_wheel name="back_wheel" flag="-1"/>
</robot>
5、添加小车控制运动的文件
5.1、在urdf文件夹下的子文件夹gazebo中创建一个名为move.xacro的文件
5.2、随后在该文件下输入控制小车运动的代码如下:
<robot name="my_car_move" xmlns:xacro="http://wiki.ros.org/xacro">
<!-- 传动实现:用于连接控制器与关节 -->
<xacro:macro name="joint_trans" params="joint_name">
<!-- Transmission is important to link the joints and the controller -->
<transmission name="${joint_name}_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="${joint_name}">
<hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
</joint>
<actuator name="${joint_name}_motor">
<hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
</xacro:macro>
<!-- 每一个驱动轮都需要配置传动装置 -->
<xacro:joint_trans joint_name="left_wheel" />
<xacro:joint_trans joint_name="right_wheel" />
<!-- 控制器 -->
<gazebo>
<plugin name="differential_drive_controller" filename="libgazebo_ros_diff_drive.so">
<rosDebugLevel>Debug</rosDebugLevel>
<publishWheelTF>false</publishWheelTF>
<robotNamespace>/</robotNamespace>
<publishTf>1</publishTf>
<publishWheelJointState>false</publishWheelJointState>
<alwaysOn>true</alwaysOn>
<updateRate>100</updateRate>
<legacyMode>true</legacyMode>
<leftJoint>left_wheel</leftJoint> <!-- 左轮 -->
<rightJoint>right_wheel</rightJoint> <!-- 右轮 -->
<wheelSeparation>${base_link_radius * 2}</wheelSeparation> <!-- 车轮间距 -->
<wheelDiameter>${wheel_radius * 2}</wheelDiameter> <!-- 车轮直径 -->
<broadcastTF>1</broadcastTF>
<wheelTorque>30</wheelTorque>
<wheelAcceleration>1.8</wheelAcceleration>
<commandTopic>cmd_vel</commandTopic> <!-- 运动控制话题 -->
<odometryFrame>odom</odometryFrame>
<odometryTopic>odom</odometryTopic> <!-- 里程计话题 -->
<robotBaseFrame>base_footprint</robotBaseFrame> <!-- 根坐标系 -->
</plugin>
</gazebo>
</robot>
5.3、回到集成了demo01_car_base.urdf.xacro和head.xacro代码的car.urdf.xacro文件中,再集成一个控制小车运动的move.xacro文件。
<!-- 包含控制小车的xacro文件 -->
<xacro:include filename="gazebo/move.xacro"/>
5.4、重复步骤3.6,打开rviz仿真环境,将rviz之前保存的配置进行修,这里为了让小车进行运动,需要将odom坐标作为基坐标系,即将Fixed Frame(默认值为map)改成odom。
使用快捷键Ctrl+Alt+T打开终端,输入rostopic list可以查看小车这时候发布的话题,如果能找到cmd_vel(博主这个话题在这里的第二行)这个话题说明我们可以控制小车的运动了,这时候介绍一个简单控制小车的办法,输入代码 rosrun teleop_twist_keyboard teleop_twist_keyboard.py后,我们就可以通过使用键盘控制小车运动。
#在终端显示小车当前发布的话题
rostopic list
#使用键盘控制小车
rosrun teleop_twist_keyboard teleop_twist_keyboard.py
5.5、
6、增加多个小车
6.1、类似步骤3.1的操作,我们需要在launch文件夹下面创建一个新的文件名为demo02_multi_car.launch,随后将之前的launch文件代码复制粘贴一份,我们需要在原来的代码上进行修改;同理,在urdf文件夹下创建一个新的文件夹名为demo02_multi_car.urdf.xacro,复制粘贴一份之前demo01_car_base.urdf.xacro的代码并进行修改;在gazebo文件夹下面创建multi_move.xacro文件,复制粘贴一份之前move.xacro的代码并进行修改。
6.2、创建多个小车最重要的部分在于给每个小车划分独立的命名空间,在这里我们使用<group ns>...</group>这种强命名(直接在小车的关节、连杆前面加上命名空间/robot1、/robot2)的方式。demo02_multi_car.launch修改后的代码如下:
<launch>
<!-- 启动gazebo -->
<include file="$(find gazebo_ros)/launch/empty_world.launch"/>
<group ns="car1">
<!-- 将xacro小车文件加载到参数服务器 -->
<param name="robot_description" command ="$(find xacro)/xacro $(find urdf01_multi)/urdf/car.urdf.xacro ns:=car1"/>
<!-- 在gazebo中显示机器人模型 -->
<node pkg="gazebo_ros" type="spawn_model" name="car1" args="-urdf -model car1 -param robot_description -x 0.5"/>
<!-- 发布机器人关节和状态节点 -->
<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"/>
<!-- 发布一个map节点,同时将map节点与小车的odom节点通过静态变换联系起来,这是小车能在rviz中显示的关键代码 -->
<node pkg="tf2_ros" type="static_transform_publisher" name="static_transform_publisher" args="0 0 0 0 0 0 /map /car1_odom"/>
</group>
<group ns="car2">
<!-- 将xacro小车文件加载到参数服务器 -->
<param name="robot_description" command ="$(find xacro)/xacro $(find urdf01_multi)/urdf/car.urdf.xacro ns:=car2"/>
<!-- 在gazebo中显示机器人模型 -->
<node pkg="gazebo_ros" type="spawn_model" name="car2" args="-urdf -model car2 -param robot_description -x 1.5"/>
<!-- 发布机器人关节和状态节点 -->
<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"/>
<!-- 发布一个map节点,同时将map节点与小车的odom节点通过静态变换联系起来,这是小车能在rviz中显示的关键代码 -->
<node pkg="tf2_ros" type="static_transform_publisher" name="static_transform_publisher" args="0 0 0 0 0 0 /map /car2_odom"/>
</group>
<!-- 启动rviz -->
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_multi)/config/mycar.rviz"/>
</launch>
6.3、将demo02_multi_car.urdf.xacro从单个小车的配置代码修改如下:
<!-- 根标签,必须声明 xmlns:xacro -->
<robot name="my_base" xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- 封装多个机器人属性 -->
<xacro:property name="ns" value="$(arg ns)"/>
<!-- 封装变量、常量 -->
<xacro:property name="PI" value="3.141"/>
<!-- 封装宏属性——黑色和黄色 -->
<material name="black">
<color rgba="0.0 0.0 0.0 1.0"/>
</material>
<material name="yellow">
<color rgba="0.5 0.3 0.0 0.5"/>
</material>
<!-- 底盘属性 -->
<!-- 小车底盘中心点半径 -->
<xacro:property name="base_footprint_radius" value="0.001"/>
<!--小车底盘半径 -->
<xacro:property name="base_link_radius" value="0.1"/>
<!-- 小车底盘高度 -->
<xacro:property name="base_link_length" value="0.08"/>
<!-- 小车底盘质量 -->
<xacro:property name="base_mass" value="2"/>
<!-- 小车底盘离地高度 -->
<xacro:property name="map_space" value="0.015"/>
<!-- 小车底盘中心点 -->
<link name="${ns}_base_footprint">
<visual>
<geometry>
<sphere radius="${base_footprint_radius}"/>
</geometry>
</visual>
</link>
<!-- 小车底盘连杆代码 -->
<link name="${ns}_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"/>
</visual>
<collision>
<geometry>
<cylinder radius="${base_link_radius}" length="${base_link_length}"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
</collision>
<xacro:cylinder_inertial_matrix m="${base_mass}" r="${base_link_radius}" h="${base_link_length}"/>
</link>
<gazebo reference="${ns}_base_link">
<material>Gazebo/Yellow</material>
</gazebo>
<!-- 小车底盘和底盘中心的关节连接 -->
<joint name="${ns}_base_link_to_base_footprint" type="fixed">
<parent link="${ns}_base_footprint"/>
<child link="${ns}_base_link"/>
<!-- 关节的重心应该位于小车离地间距+一半的小车底盘高度 -->
<origin xyz="0 0 ${map_space + base_link_length / 2}"/>
</joint>
<!-- 驱动轮属性封装 -->
<!-- 驱动轮半径 -->
<xacro:property name="wheel_radius" value="0.0325"/>
<!-- 驱动轮宽度 -->
<xacro:property name="wheel_length" value="0.015"/>
<!-- 驱动轮质量 -->
<xacro:property name="wheel_mass" value="0.05"/>
<!-- 驱动轮宏代码实现 -->
<xacro:macro name="driving_wheels" params="name flag">
<!-- 小车驱动轮连杆代码 -->
<link name="base_${name}">
<visual>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}"/>
</geometry>
<origin xyz="0 0 0" rpy="${PI/2} 0 0"/>
<material name="black"/>
</visual>
<collision>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}"/>
</geometry>
<origin xyz="0 0 0" rpy="${PI/2} 0 0"/>
</collision>
<xacro:cylinder_inertial_matrix m="${wheel_mass}" r="${wheel_radius}" h="${wheel_length}"/>
</link>
<gazebo reference="base_${name}">
<material>Gazebo/Red</material>
</gazebo>
<!-- 驱动轮和底盘代码连杆实现 -->
<joint name="${name}" type="continuous">
<parent link="${ns}_base_link"/>
<child link="base_${name}"/>
<origin xyz="0 ${flag * base_link_radius} ${-(map_space + base_link_length / 2 - wheel_radius)}"/>
<axis xyz="0 1 0"/>
</joint>
</xacro:macro>
<xacro:driving_wheels name="${ns}_left_wheel" flag="1"/>
<xacro:driving_wheels name="${ns}_right_wheel" flag="-1"/>
<!-- 支撑轮宏属性封装 -->
<!-- 支撑轮半径 -->
<xacro:property name="support_wheel_radius" value="0.0075"/>
<!-- 支撑轮质量 -->
<xacro:property name="support_wheel_mass" value="0.01"/>
<!-- 支撑轮宏 -->
<xacro:macro name="support_wheel" params="name flag">
<link name="${name}">
<visual>
<geometry>
<sphere radius="${support_wheel_radius}"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
<material name="black"/>
</visual>
<collision>
<geometry>
<sphere radius="${support_wheel_radius}"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
</collision>
<xacro:sphere_inertial_matrix m="${support_wheel_mass}" r="${support_wheel_radius}"/>
</link>
<gazebo reference="${name}">
<material>Gazebo/Red</material>
</gazebo>
<!-- 支撑轮与底盘的关节连接实现 -->
<joint name="${name}_to_base_link" type="continuous">
<parent link="${ns}_base_link"/>
<child link="${name}"/>
<origin xyz="${flag * (base_link_radius - support_wheel_radius)} 0 ${-(base_link_length / 2 + map_space / 2)}" />
<axis xyz="1 1 1"/>
</joint>
</xacro:macro>
<xacro:support_wheel name="${ns}_front_wheel" flag="1"/>
<xacro:support_wheel name="${ns}_back_wheel" flag="-1"/>
</robot>
6.4、因为我们小车的配置文件名变成了demo02_multi_car.urdf.xacro,那么对应的我们也需要修改multi_car.xacro中的文件名,具体修改如下:
<robot name="my_laser" xmlns:xacro="http://wiki.ros.org/xacro">
<!-- 包含惯性矩阵 -->
<xacro:include filename="head.xacro"/>
<!-- 包含多个小车配置的xacro文件 -->
<xacro:include filename="demo02_multi.urdf.xacro"/>
<!-- 包含控制多个小车的xacro文件 -->
<xacro:include filename="gazebo/move.xacro"/>
</robot>
6.5、这是我们在终端中启动demo02_multi_car.launch文件,可以发现在gazebo仿真环境中可以正常启动小车,但是rviz中无法显示小车,这是因为我们还没有修改multi_move.xacro文件,缺少了两个小车odom坐标的发布。
6.6、接下来对multi_car.xacro的文件进行修改,修改后的代码如下:
<robot name="my_car_move" xmlns:xacro="http://wiki.ros.org/xacro">
<!-- 封装多个机器人宏属性 -->
<xacro:property name="ns" value="$(arg ns)"/>
<!-- 传动实现:用于连接控制器与关节 -->
<xacro:macro name="joint_trans" params="joint_name">
<!-- Transmission is important to link the joints and the controller -->
<transmission name="${joint_name}_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="${joint_name}">
<hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
</joint>
<actuator name="${joint_name}_motor">
<hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
</xacro:macro>
<!-- 每一个驱动轮都需要配置传动装置 -->
<xacro:joint_trans joint_name="${ns}_left_wheel" />
<xacro:joint_trans joint_name="${ns}_right_wheel" />
<!-- 控制器 -->
<gazebo>
<plugin name="differential_drive_controller" filename="libgazebo_ros_diff_drive.so">
<rosDebugLevel>Debug</rosDebugLevel>
<publishWheelTF>false</publishWheelTF>
<robotNamespace>${ns}</robotNamespace>
<publishTf>1</publishTf>
<publishWheelJointState>false</publishWheelJointState>
<alwaysOn>true</alwaysOn>
<updateRate>100</updateRate>
<legacyMode>true</legacyMode>
<leftJoint>${ns}_left_wheel</leftJoint> <!-- 左轮 -->
<rightJoint>${ns}_right_wheel</rightJoint> <!-- 右轮 -->
<wheelSeparation>${base_link_radius * 2}</wheelSeparation> <!-- 车轮间距 -->
<wheelDiameter>${wheel_radius * 2}</wheelDiameter> <!-- 车轮直径 -->
<broadcastTF>1</broadcastTF>
<wheelTorque>30</wheelTorque>
<wheelAcceleration>1.8</wheelAcceleration>
<commandTopic>cmd_vel</commandTopic> <!-- 运动控制话题 -->
<odometryFrame>${ns}_odom</odometryFrame>
<odometryTopic>odom</odometryTopic> <!-- 里程计话题 -->
<robotBaseFrame>${ns}_base_footprint</robotBaseFrame> <!-- 根坐标系 -->
</plugin>
</gazebo>
</robot>
6.7、启动demo02_multi_car.launch文件,这时候的rviz界面如下:
那么想要正常显示两个小车,我们需要将Fixed Frame修改成map,Add两个RobotModel,并且将RobotModel的Robot Description选项改成我们的小车名字,我这儿是/car1/robot_description(如果不进行修改,默认是robot_description,无法识别到小车的模型,从而无法现实小车并报错)。最后使用快捷键Ctrl++Shift+S保存我们多个小车配置为另一个rviz文件。
那么保存了这个multi.rviz文件后,我们需要重新回到demo02_multi_car.launch文件,将rviz启动项的rviz配置文件改成我们刚刚保存的文件,这样我们每次启动时候就不需要添加小车了,rviz会自动显示小车。
<!-- 修改args参数这一项,将我们的rviz配置文件修改成multi_car.rviz -->
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_multi)/config/multi_car.rviz"/>
</launch>