ROS实践-虚拟仿真平台Stage/Gazebo
1. 区别
Stage 和 Gazebo 都是 ROS 生态系统中的仿真工具,但它们的功能侧重点不同。
Stage:轻量级的 2D 仿真工具,适用于大规模机器人群体仿真。
Gazebo:功能更强大的 3D 物理仿真环境,支持复杂物理交互、传感器建模和机器人动力学仿真。
2. Stage仿真
(1)安装stage仿真软件
使用命令:sudo apt-get install ros-noetic-stage-ros来安装。这个平台中有一些自带的.world地图文件,我们可以加载它自带的地图,也可以自己来编写地图文件。
(2)编写控制代码
功能:控制机器人走圆形路线。
#!/usr/bin/python
import rospy
from geometry_msgs.msg import Twistdef cmd_velmodule():# 初始化ROS节点,节点名称为 'circle_drawer',anonymous=False 使其名称唯一rospy.init_node('circle_drawer', anonymous=False) # 创建一个Publisher,发布消息到 /cmd_vel 话题,消息类型为 Twist,队列大小为10cmd_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10) # 设置发布频率为 10Hzrate = rospy.Rate(10) # 创建一个 Twist 消息对象,用于存储速度指令twist = Twist()rospy.loginfo("Start controlling robot to draw a circle") # 记录日志信息# 进入主循环,持续发布速度指令,直到节点被终止while not rospy.is_shutdown():twist.linear.x = 0.2 # 设置线速度,单位:m/stwist.angular.z = 0.4 # 设置角速度,单位:rad/s(正值表示逆时针旋转)cmd_pub.publish(twist) # 发布速度指令到 /cmd_vel 话题rate.sleep() # 休眠以保持 10Hz 的循环频率
if __name__ == "__main__":try:cmd_velmodule() # 运行控制函数except rospy.ROSInterruptException:pass # 异常处理,当节点被中断时,安全退出
(3)虚拟平台显示
启动主节点,使用命令运行虚拟地图:rosrun stage_ros stageros $(rospack find stage_ros)/world/willow-erratic.world
地图运行完毕后,运行上面我们自己编写的代码文件,就可以看到小车在按照我们编程程序的轨迹在虚拟地图上运行(按照圆形轨迹运动)。
3. Cazebo仿真
(1)安装Gazebo。
4. 疑难解答
问题1:代码中发布的话题是如何和虚拟平台上的机器人联系起来的呢?
在 ROS 机器人仿真或实际应用中,Python 脚本通过
rospy.Publisher
将Twist
速度指令发布到/cmd_vel
话题。而机器人本体(无论是 Gazebo 仿真环境中的机器人,还是实际硬件机器人)通常会运行一个运动控制器(如diff_drive_controller
或move_base
),该控制器会订阅/cmd_vel
话题,并解析其中的线速度(linear.x)和角速度(angular.z)指令,进而驱动底盘运动。在 Stage(Stage ROS 或 Stagefaa) 进行仿真时,机器人模型 并不是 直接由 URDF 定义的,而是通过 world 文件(
.world
) 进行描述的。在 Gazebo 仿真环境中,机器人通常是通过 URDF/SDF 文件定义的,并且 Gazebo 插件(如
gazebo_ros_control
或gazebo_ros_diff_drive
)会负责订阅/cmd_vel
话题并控制模型的物理运动。而在实际机器人中,控制器通常运行在嵌入式系统(如 Raspberry Pi 或 STM32)上,解析/cmd_vel
指令,并将其转换为电机驱动信号,使机器人按照给定的速度移动。问题2:那么这个机器人模型是在哪定义的呢?如何查看它订阅的哪个话题?
机器人模型通常定义在 URDF(.urdf 或 .xacro) 或 SDF(.sdf) 文件中,用于描述机器人的结构、关节和传感器等信息。而
.world
文件 主要用于 Gazebo 仿真环境,负责定义仿真场景,包括机器人、传感器、地形和其他环境元素。在仿真中,机器人一般会 订阅某个速度控制话题(例如
/cmd_vel
),并根据接收到的指令进行运动。如果场景中只有一个机器人,通常会默认订阅/cmd_vel
话题来接收速度指令并执行相应动作。问题3:既然是读取/cmd_vel话题,那么如果有多个机器人模型需要执行不同的动作怎么办呢?
对于多个机器人,每个机器人需要订阅不同的控制话题,例如
/cmd_vel_robot1
和/cmd_vel_robot2
,以便分别控制各自的运动。每个机器人都可以通过不同的话题接收各自的控制命令。比如,机器人 A 和机器人 B 可以分别使用/cmd_vel_robot1
和/cmd_vel_robot2
这两个话题。在 ROS 中,你只需要确保每个机器人订阅的是不同的命令话题,这样每个机器人就能接收到对应的控制指令。(1)修改python脚本文件,使得不同的机器人模型订阅不同的话题。
#!/usr/bin/python import rospy from geometry_msgs.msg import Twistdef cmd_velmodule_robot1():rospy.init_node('multi_robot_controller', anonymous=False) # 初始化ROS节点cmd_pub_robot1 = rospy.Publisher('/cmd_vel_robot1', Twist, queue_size=10) # 发布到 /cmd_vel_robot1 话题cmd_pub_robot2 = rospy.Publisher('/cmd_vel_robot2', Twist, queue_size=10) # 发布到 /cmd_vel_robot2 话题rate = rospy.Rate(10) # 10Hztwist1 = Twist()twist2 = Twist()rospy.loginfo("Start controlling robots")while not rospy.is_shutdown():# 控制机器人1twist1.linear.x = 0.2 # 机器人1的线速度twist1.angular.z = 0.4 # 机器人1的角速度cmd_pub_robot1.publish(twist1) # 发布速度指令到机器人1# 控制机器人2twist2.linear.x = 0.1 # 机器人2的线速度twist2.angular.z = 0.6 # 机器人2的角速度cmd_pub_robot2.publish(twist2) # 发布速度指令到机器人2rate.sleep() # 休眠,保证10Hz循环if __name__ == "__main__":try:cmd_velmodule_robot1()except rospy.ROSInterruptException:pass
(2)修改虚拟地图文件.world
robot (name "roomba1"pose [-8.0 6.0 0.0 0.0] drive "diff"cmd_vel_topic "/cmd_vel_robot1" # 机器人1订阅这个话题 )robot (name "roomba2"pose [8.0 -6.0 0.0 0.0] drive "diff"cmd_vel_topic "/cmd_vel_robot2" # 机器人2订阅这个话题 )