ROS2高效学习第十章 -- 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 高效学习系列,但今天发出最后一篇博客,历时半载,感谢媳妇的支持和自己的坚持,我们很快再见。

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值