ros2 高级组件其五之 gazebo
1 前言和资料
上篇文章ROS2高效学习第十章 – ros2 高级组件其四之 webots,我们引入了 webots 仿真工具。早在 ros1 系列博客 ROS高效进阶系列 中,我们大量使用了 gazebo ,讲解了 gazebo 建模和仿真的各个细节。
本文我们将在 ros2 humble 下,再次安装 gazebo11,仍然实现差速轮式机器人,并通过 /cmd_vel 控制机器人在仿真空间内活动。本文的重点将放在如何使用 ros2 python launch 文件,实现 gazebo 仿真。
本文参考资料如下:
(1)ROS高效进阶第一章 – ROS高级组件之 gazebo
(2)ROS高效进阶第二章 – 以差速轮式机器人为例,学习URDF机器人建模与xacro优化
(3)ROS高效进阶第三章 – 以差速轮式机器人为例,使用Gazebo构建机器人仿真平台
(4)ROS高效进阶第七章 – 机器人综合应用之迷宫寻宝
(5)古月 gazebo
本系列博客汇总:ROS2 高效学习系列。
2 正文
2.1 安装 gazebo 11
# 安装命令
sudo apt install ros-humble-gazebo-ros*
# 测试能否正常启动
# 首次启动请保持网络畅通,gazebo 会从远端拉模型到本地,耗费一定的时间,请耐心等待
ros2 launch gazebo_ros gazebo.launch.py
2.2 gazebo_mbot_go_maze
(1)创建 gazebo_mbot_go_maze 软件包以及相关文件
cd ~/colcon_ws/src
ros2 pkg create --build-type ament_cmake --license Apache-2.0 gazebo_mbot_go_maze --dependencies rclcpp rclcpp_components rosidl_default_generators rclcpp_action std_msgs geometry_msgs
cd gazebo_mbot_go_maze
mkdir launch tool worlds urdf rviz
touch launch/mbot_gazebo_launch.py tool/mbot_teletop.py
(2)worlds 和 urdf 目录内的文件最初来自 ROS高效进阶第七章 – 机器人综合应用之迷宫寻宝,并稍加调试。这里不详细讲解这些文件了,读者可以从这里直接下载使用:gazebo_mbot_go_maze。
(3)mbot_teletop.py:根据键盘方向键,使用 ros2 接口,通过发 /cmd_vel ,控制机器人行动的脚本
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
import sys, select, termios, tty
class MbotTeletop(Node):
def __init__(self, name):
super().__init__(name)
self._help_msg = """
Control mbot!
---------------------------
Moving around:
u i o
j k l
m , .
q/z : increase/decrease max speeds by 10%
w/x : increase/decrease only linear speed by 10%
e/c : increase/decrease only angular speed by 10%
space key, k : force stop
anything else : stop smoothly
CTRL-C to quit
"""
self._move_bindings = {
'i':(1,0),
'o':(1,-1),
'j':(0,1),
'l':(0,-1),
'u':(1,1),
',':(-1,0),
'.':(-1,1),
'm':(-1,-1),
}
self._speed_bindings = {
'q':(1.1,1.1),
'z':(.9,.9),
'w':(1.1,1),
'x':(.9,1),
'e':(1,1.1),
'c':(1,.9),
}
self._cmd_pub = self.create_publisher(Twist, "/cmd_vel", 10)
self._settings = termios.tcgetattr(sys.stdin)
def getKey(self):
tty.setraw(sys.stdin.fileno())
rlist, _, _ = select.select([sys.stdin], [], [], 0.1)
if rlist:
key = sys.stdin.read(1)
else:
key = ''
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self._settings)
return key
def run(self):
x = 0
th = 0
status = 0
count = 0
target_speed = 0
target_turn = 0
control_speed = 0
control_turn = 0
speed = .2
turn = 1
try:
print(self._help_msg)
print("currently:\tspeed %s\tturn %s " % (speed,turn))
while(1):
key = self.getKey()
# 运动控制方向键(1:正方向,-1负方向)
if key in self._move_bindings.keys():
x = self._move_bindings[key][0]
th = self._move_bindings[key][1]
count = 0
# 速度修改键
elif key in self._speed_bindings.keys():
speed = speed * self._speed_bindings[key][0] # 线速度增加0.1倍
turn = turn * self._speed_bindings[key][1] # 角速度增加0.1倍
count = 0
print("currently:\tspeed %s\tturn %s " % (speed,turn))
if (status == 14):
print (self._help_msg)
status = (status + 1) % 15
# 强制停止键
elif key == ' ' or key == 'k' :
x = 0
th = 0
control_speed = 0
control_turn = 0
print("currently:\tforce stop !!")
# 其他键都是慢慢停止键
else:
count = count + 1
if count > 4:
x = 0
th = 0
if (key == '\x03'):
break
# 目标速度=速度值*方向值
target_speed = speed * x
target_turn = turn * th
# 速度限位,防止速度增减过快
if target_speed > control_speed:
control_speed = min( target_speed, control_speed + 0.02 )
elif target_speed < control_speed:
control_speed = max( target_speed, control_speed - 0.02 )
else:
control_speed = target_speed
# 角度限位,防止角度增减过快
if target_turn > control_turn:
control_turn = min( target_turn, control_turn + 0.1 )
elif target_turn < control_turn:
control_turn = max( target_turn, control_turn - 0.1 )
else:
control_turn = target_turn
# 创建并发布twist消息
twist = Twist()
twist.linear.x = float(control_speed)
twist.linear.y = float(0)
twist.linear.z = float(0)
twist.angular.x = float(0)
twist.angular.y = float(0)
twist.angular.z = float(control_turn)
self._cmd_pub.publish(twist)
except Exception as e:
print(e)
finally:
twist = Twist()
self._cmd_pub.publish(twist)
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self._settings)
def main(args=None):
rclpy.init(args=args)
node = MbotTeletop("Tele_cmd")
node.run()
rclpy.shutdown()
if __name__ == "__main__":
main()
(4)mbot_gazebo_launch.py
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription, TimerAction
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
import xacro
def generate_launch_description():
package_name='gazebo_mbot_go_maze'
# 使用 gazebo 绘图制作的迷宫仿真环境
world_file_path = 'worlds/maze_room.world'
pkg_path = os.path.join(get_package_share_directory(package_name))
world_path = os.path.join(pkg_path, world_file_path)
# 本文测试过程中,保存的 rviz 配置文件
rviz_path = os.path.join(pkg_path, 'rviz/mbot_gazebo.rviz')
# urdf 文件,里面就是差速轮式机器人
pkg_path = os.path.join(get_package_share_directory(package_name))
xacro_file = os.path.join(pkg_path,'urdf','mbot_base.xacro')
robot_description_config = xacro.process_file(xacro_file)
# robot_state_publisher 用于把 gazebo 中机器人的 TF 信息发出来
params = {'robot_description': robot_description_config.toxml(), 'use_sim_time': True}
mbot = Node(
package='robot_state_publisher',
executable='robot_state_publisher',
output='screen',
parameters=[params]
)
# 启动 gazebo ,加载迷宫仿真环境
gazebo = IncludeLaunchDescription(
PythonLaunchDescriptionSource([os.path.join(
get_package_share_directory('gazebo_ros'), 'launch', 'gazebo.launch.py'
)]), launch_arguments={'world':world_path}.items()
)
# 使用 gazebo 中的 spawn_entity,把 robot_description 指定的机器人 urdf 加载进 gazebo,并指定加载位置为坐标原点
spawn_x_val = '0.0'
spawn_y_val = '0.0'
spawn_z_val = '0.0'
spawn_yaw_val = '0.0'
spawn_entity = Node(package='gazebo_ros', executable='spawn_entity.py',
arguments=['-topic', 'robot_description',
'-entity', 'mbot',
'-x', spawn_x_val,
'-y', spawn_y_val,
'-z', spawn_z_val,
'-Y', spawn_yaw_val],
output='screen')
# 启动 rviz2,方便查看
rviz_node = Node(
package='rviz2',
executable='rviz2',
name='rviz2',
arguments=['-d', rviz_path])
# 依次启动所有节点
return LaunchDescription([
mbot,
gazebo,
spawn_entity,
TimerAction(
period=3.0,
actions=[rviz_node],
)
])
(5)编译和运行
cd ~/colcon_ws/src
colcon build --packages-select gazebo_mbot_go_maze
source install/local_setup.bash
ros2 launch gazebo_mbot_go_maze mbot_gazebo_launch.py
# 再开一个终端,启动机器人控制脚本
# 光标停留在这里,使用键盘方向键控制机器人行动
ros2 run gazebo_mbot_go_maze mbot_teletop.py
rviz 的具体配置
启动时初始位置截图
使用方向键让机器人找到红球宝藏
2.3 下一代 gazebo
目前, gazebo 已经推出了下一代版本 Ignition ,但是资料比较少,本文没有深入研究,这里保留部分资料链接:ros2 Ignition,以及安装方式:
sudo apt install ros-humble-ros-gz
# 测试命令
ign gazebo -v 4 -r visualize_lidar.sdf
3 总结
本文样例托管在本人的 github 上:gazebo_mbot_go_maze,欢迎复现。
本文作为 ros2 高级组件系列的最后一篇,也将是 ROS2 高效学习系列博客的最后一篇。从开始规划 ROS2 高效学习系列,但今天发出最后一篇博客,历时半载,感谢媳妇的支持和自己的坚持,我们很快再见。