ROS多机器人仿真
ROS多机器人仿真实验摘要 本实验基于ROS框架实现多机器人协同仿真。通过在URDF文件中添加命名空间参数(ns),使用group标签在launch文件中创建三个独立命名空间的机器人实例(robot1/2/3)。每个机器人具有唯一的部件命名、关节命名和传感器配置,避免ID冲突。通过修改car.xacro文件,确保所有引用关系正确,包括link、joint、Gazebo引用和控制器话题。最终实现了三
ROS多机器人仿真
前言

该实验是基于赵旭左老师课程中搭建的机器人模型,通过group建立三个机器人模型,在Gazebo和Rviz中通过话题通信实现简单的圆周运动。
1.需要的机器人模型文件
如图,在urdf文件夹下存放着机器人模型的文件,gazebo子文件夹下是运动控制和传感器信息仿真的文件。car.xacro文件将小车组件组合在一起,launch文件中使用该文件加载机器人模型。(没有导航可以不需要传感器信息仿真文件)

2.launch文件(重点)
<launch>
<!-- 启动 gazebo -->
<include file="$(find gazebo_ros)/launch/empty_world.launch" />
<!-- 第一个机器人 -->
<group ns="robot1">
<!-- 将 Urdf 文件的机器人模型加载到参数服务器 -->
<param name="robot_description" command="$(find xacro)/xacro $(find multil_car)/urdf/car.xacro ns:=robot1" />
<!-- 在 gazebo 中显示机器人模型 -->
<node pkg="gazebo_ros" type="spawn_model" name="robot1" args="-urdf -model robot1 -param robot_description -x 0.5" />
<!-- 关节以及机器人状态发布节点 -->
<node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" output="screen" />
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" output="screen" />
<node pkg="tf2_ros" type="static_transform_publisher" name="static_transform_publisher" args="0 0 0 0 0 0 /map /robot1_odom" />
</group>
<!-- 第2个机器人 -->
<group ns="robot2">
<!-- 将 Urdf 文件的机器人模型加载到参数服务器 -->
<param name="robot_description" command="$(find xacro)/xacro $(find multil_car)/urdf/car.xacro ns:=robot2" />
<!-- 在 gazebo 中显示机器人模型 -->
<node pkg="gazebo_ros" type="spawn_model" name="robot2" args="-urdf -model robot2 -param robot_description -x 2.5" />
<!-- 关节以及机器人状态发布节点 -->
<node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" output="screen" />
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" output="screen" />
<node pkg="tf2_ros" type="static_transform_publisher" name="static_transform_publisher" args="0 0 0 0 0 0 /map /robot2_odom" />
</group>
<!-- 第3个机器人 -->
<group ns="robot3">
<!-- 将 Urdf 文件的机器人模型加载到参数服务器 -->
<param name="robot_description" command="$(find xacro)/xacro $(find multil_car)/urdf/car.xacro ns:=robot3" />
<!-- 在 gazebo 中显示机器人模型 -->
<node pkg="gazebo_ros" type="spawn_model" name="robot3" args="-urdf -model robot3 -param robot_description -x 4.5" />
<!-- 关节以及机器人状态发布节点 -->
<node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" output="screen" />
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" output="screen" />
<node pkg="tf2_ros" type="static_transform_publisher" name="static_transform_publisher" args="0 0 0 0 0 0 /map /robot3_odom" />
</group>
<!-- 启动rvizz -->
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find multil_car)/config/rviz.rviz" />
<!-- 启动编写的C++源文件,控制三个小车做圆周运动 -->
<node pkg="move_control" type="pub_control" name="pubControl" output="screen"/>
</launch>
3.修改机器人模型文件
由于课程里的机器人模型很多的组件命名都是确定的,导致遇到报错,错误核心在于 xacro文件中命名空间(ns)参数的处理逻辑不连贯。所以解决方法是在每个机器人组件和信息仿真的文件里加入
<!-- 封装多个机器人属性 -->
<xacro:property name="ns" value="$(arg ns)"/>
必须添加 ${ns} 的地方(“起名字”和关键配置)
| 类别 | 具体位置 | 示例(修改后) | 作用 |
|---|---|---|---|
| 机器人模型部件 | Link(连杆)的 name |
<link name="${ns}_base_link"> |
确保每个机器人的部件在URDF中有唯一ID |
Joint(关节)的 name |
<joint name="${ns}_wheel_joint"> |
确保关节ID唯一 | |
| 传感器与Gazebo引用 | Gazebo 标签的 reference |
<gazebo reference="${ns}_laser"> |
告诉Gazebo为哪个唯一的link添加传感器 |
插件中的 frameName |
<frameName>${ns}/laser</frameName> |
使TF坐标系唯一,避免TF树冲突 | |
| 控制器与话题 | 插件中的 topicName, cameraName 等 |
<topicName>${ns}/cmd_vel</topicName> |
使控制、图像、点云等话题唯一,实现独立通信 |
插件中的 robotNamespace |
<robotNamespace>${ns}</robotNamespace> |
让插件内部默认的所有话题都归到此命名空间下 |
当你为一个link或joint加了 ${ns} 前缀后,所有引用它的地方都必须使用相同的前缀,否则链接关系会断裂。
-
示例:
<!-- 定义link时加了前缀 --> <link name="${ns}_my_link"/> <!-- 那么在定义joint引用它时,也必须加相同前缀 --> <joint name="${ns}_my_joint" type="fixed"> <parent link="${ns}_base_link"/> <!-- 这里也需要 --> <child link="${ns}_my_link"/> <!-- 必须和link的name一致 --> </joint> <!-- Gazebo引用这个link时也要一致 --> <gazebo reference="${ns}_my_link"> <material>Gazebo/Red</material> </gazebo>
不需要添加 ${ns} 的地方
- 纯计算或属性定义:例如
[xacro:property](xacro:property),[xacro:macro](xacro:macro)内部的局部变量、数学计算。 - 不产生唯一ID的模型属性:例如
<box size="...">,<origin xyz="...">,<mass value="...">等描述尺寸、位置、质量的属性。 - 已封装的宏调用:例如
<xacro:Box_inertial_matrix m="0.1" l="0.2" w="0.1" h="0.05"/>,你只需确保传给它的参数(如尺寸)正确,宏内部如何计算惯性矩阵无需关心命名空间。
总结:一个快速的检查清单
在修改你的 car.xacro 及相关文件时,请按此顺序检查:
- 前提:确保xacro文件顶部已通过
<xacro:arg name="ns"/> 接收参数,并用<xacro:property name="ns" value="${ns}"/>定义为属性。 - 扫描所有
name= 和 reference= :给所有<link>,<joint>,<gazebo reference> 的名字加上${ns}_前缀。 - 检查所有“指代” :确保所有
<parent link>,<child link>,<gazebo reference> 引用的名称,和步骤2中的定义完全一致。 - 配置Gazebo插件:在插件配置中,为
frameName,topicName,cameraName 等字段加上${ns}/ 或${ns}_前缀(风格需统一)。 - 保持风格一致:选择用斜杠(
${ns}/topic)还是下划线(${ns}_topic)连接,并在整个项目中保持一致。
此时可以启动launch文件,在Rviz和Gazebo中查看

4.创建机器人运动的话题发布节点
核心功能是同时向三个不同命名空间下的机器人发布相同的速度控制指令
#include "ros/ros.h"
#include "geometry_msgs/Twist.h"
void moveRobot(ros::Publisher& velPublisher,double linearX,double angularZ)
{
geometry_msgs::Twist velMsg;
velMsg.linear.x = linearX;
velMsg.angular.z = angularZ;
velPublisher.publish(velMsg);
}
int main(int argc, char *argv[])
{
ros::init(argc,argv,"pubControl");
ros::NodeHandle nh;
ros::Publisher robot1VelPub = nh.advertise<geometry_msgs::Twist>("/robot1/cmd_vel",10);
ros::Publisher robot2VelPub = nh.advertise<geometry_msgs::Twist>("/robot2/cmd_vel",10);
ros::Publisher robot3VelPub = nh.advertise<geometry_msgs::Twist>("/robot3/cmd_vel",10);
ros::Rate rate(10);
while (ros::ok())
{
double linearX = 2.2;
double angularZ1 = 2.2;
double angularZ2 = 2.2;
double angularZ3 = 2.2;
moveRobot(robot1VelPub,linearX,angularZ1);
moveRobot(robot2VelPub,linearX,angularZ2);
moveRobot(robot3VelPub,linearX,angularZ3);
ros::spinOnce();
rate.sleep();
}
return 0;
}
最后可以实现三个小车的圆周运动

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)