在windows11 wsl2上进行ros2的安装和配置,环境:ubuntu22.04,ros2 humble, gazebo,rviz,使用slam_toolbox
包进行建图。文件资源可从文末下载。
安装和配置环境
在windows11上安装wsl2,并安装ros2的相关依赖。
安装wsl2
开发人员可以在 Windows 计算机上同时访问 Windows 和 Linux 的强大功能。 通过适用于 Linux 的 Windows 子系统 (WSL),开发人员可以安装 Linux 发行版(例如 Ubuntu、OpenSUSE、Kali、Debian、Arch Linux 等),并直接在 Windows 上使用 Linux 应用程序、实用程序和 Bash 命令行工具,不用进行任何修改,也无需承担传统虚拟机或双启动设置的费用。
# 此命令将启用运行 WSL 并安装 Linux 的 Ubuntu 发行版所需的功能。
wsl --install
# 列出可安装的linux系统
wsl --list --online
# 安装一个指定的linux,安装完成之后需要重启电脑
wsl --install -d <DistroName>
# 列出已安装的wsl发行版
wsl -l
也可以在 Microsoft Store 中安装wsl2,直接搜索ubuntu,安装Ubuntu 22.04.3 LTS 即可。
参考微软官方文档即可,非常简单易用。
https://learn.microsoft.com/zh-cn/windows/wsl/setup/environment
wsl #打开默认的Linux 发行版
wsl -d Ubuntu-22.04 #打开指定发行版
换源
# 查询系统版本号
lsb_release -a
# 或者
cat /etc/issue
sudo vim /etc/apt/sources.list
参考清华源文档,若Ubuntu发行版为22.04,则将文档中的内容替换为
# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
# 以下安全更新软件源包含了官方源与镜像站配置,如有需要可自行修改注释切换
deb http://security.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse
# deb-src http://security.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse
# 预发布软件源,不建议启用
# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse
# # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse
给WSL设置网络代理(选做)
设置代理主要是为了方便克隆Github仓库,如无此需求,可以不设置。
默认情况下WSL的网络模式是NAT模式,在主机打开网络代理时,wsl无代理。参考微软官方文档可知设置 Windows 上的网络接口“镜像”到 Linux 中的方法为:
在C盘C:\Users\<UserName>\
下新建.wslconfig
文件,并添加以下内容:
[wsl2]
networkingMode=mirrored
dnsTunneling=true
autoProxy=true
同时需要将WSL升级到预览版
wsl --update --pre-release
修改完成后,重启WSL即可。
wsl --shutdown
安装ros2
首先打开指定的wsl
wsl -d Ubuntu-22.04
完成各项配置
sudo apt update && sudo apt upgrade
# 使用fishros安装ros2 humble
wget http://fishros.com/install -O fishros && . fishros
鱼香ros的安装包已经预装了海龟例程
sudo apt install ros-humble-turtlesim # 安装海龟功能包
尝试一下海龟例程,在终端(Ctrl+Shift+T)中运行下面的命令
ros2 run turtlesim turtlesim_node
打开一个新的终端(Ctrl+Shift+T),注意终端不要最大化,运行下面的命令
ros2 run turtlesim turtle_teleop_key
即可在第二个终端中使用方向键控制小海龟的移动了。
安装仿真需要的其他工具
安装gazebo
# 安装gazebo仿真软件
sudo apt install gazebo
# 安装gazebo-ros功能包(所有)
sudo apt install ros-humble-gazebo-*
# 安装gazebo_ros插件
sudo apt install ros-humble-gazebo-ros
# 测试是否安装成功
gazebo --verbose -s libgazebo_ros_init.so -s libgazebo_ros_factory.so
效果如下:
“shift+鼠标左键”转换视角,“鼠标左键”平移视角,“滚轮”缩放大小。
设计一个机器人小车
初步设计
设计一个四轮的机器小车,车上安装有激光雷达模块,小车用差速模块驱动。
考虑小车有一个主体body,长方体结构,长300mm,宽200mm,高60毫米,车轮外径100mm,车轮宽度30mm。前后车轮轴轴距为240mm,车轮轮心连接在主体的两个侧面上的垂直中点上,这样body的中心距离地面的距离就是50mm,body下底面(车的底盘)距离地面距离就是20mm。我们这里最终目标是SLAM任务,因此为简便起见,我们不考虑机械结构上的细节。
机器人命名为four_wheel_robot,基坐标系设置在body中心下方30mm处。(基座标是为了在rviz中能让小车车轮着地,没有其他作用。)
简单地设计urdf模型内容如下:
<?xml version="1.0"?>
<robot name="four_wheel_robot">
<link name="base_link"/>
<joint name="base_joint" type="fixed">
<parent link="base_link"/>
<child link="body"/>
<origin xyz="0.0 0.0 0.05" rpy="0 0 0"/>
</joint>
<link name="body">
<visual>
<geometry>
<box size="0.3 0.2 0.06"/>
</geometry>
</visual>
</link>
<joint name="joint_front_right" type="continuous">
<parent link="body"/>
<child link="front_right_wheel"/>
<axis xyz="0 1 0"/>
<origin xyz="0.12 0.115 0" rpy="0 0 0"/>
</joint>
<joint name="joint_front_left" type="continuous">
<parent link="body"/>
<child link="front_left_wheel"/>
<axis xyz="0 1 0"/>
<origin xyz="0.12 -0.115 0" rpy="0 0 0"/>
</joint>
<joint name="joint_back_right" type="continuous">
<parent link="body"/>
<child link="back_right_wheel"/>
<axis xyz="0 1 0"/>
<origin xyz="-0.12 0.115 0" rpy="0 0 0"/>
</joint>
<joint name="joint_back_left" type="continuous">
<parent link="body"/>
<child link="back_left_wheel"/>
<axis xyz="0 1 0"/>
<origin xyz="-0.12 -0.115 0" rpy="0 0 0"/>
</joint>
<link name="front_right_wheel">
<visual>
<origin rpy="1.57079 0 0" xyz="0 0 0"/>
<geometry>
<cylinder length="0.03" radius="0.1"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 0.5"/>
</material>
</visual>
</link>
<link name="front_left_wheel">
<visual>
<origin rpy="1.57079 0 0" xyz="0 0 0"/>
<geometry>
<cylinder length="0.03" radius="0.1"/>
</geometry>
</visual>
</link>
<link name="back_right_wheel">
<visual>
<origin rpy="1.57079 0 0" xyz="0 0 0"/>
<geometry>
<cylinder length="0.03" radius="0.1"/>
</geometry>
</visual>
</link>
<link name="back_left_wheel">
<visual>
<origin rpy="1.57079 0 0" xyz="0 0 0"/>
<geometry>
<cylinder length="0.03" radius="0.1"/>
</geometry>
</visual>
</link>
</robot>
为了检验这个模型的正确性,我们使用可视化工具来看看这个模型。
安装工具包,
sudo apt-get install ros-humble-urdf-tutorial
假设上面的urdf文件命名为 four_wheel_robot.urdf ,保存路径为 /home/ubuntu/bot.urdf ,则执行命令:
ros2 launch urdf_tutorial display.launch.py model:=/home/ubuntu/four_wheel_robot.urdf
即可看到小车模型正确显示了。
默认“base_link”为基坐标系。
上面的urdf文件太啰嗦,我们简化它一下。
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="fourbot">
<xacro:property name="body_length" value="0.3" />
<xacro:property name="body_width" value="0.2" />
<xacro:property name="body_height" value="0.06" />
<xacro:property name="wheel_radius" value="0.1" />
<xacro:property name="wheel_width" value="0.03" />
<xacro:property name="wheel_distance" value="0.24" />
<link name="base_link"/>
<joint name="base_joint" type="fixed">
<parent link="base_link"/>
<child link="body"/>
<origin xyz="0.0 0.0 ${wheel_radius/2-body_height/2}" rpy="0 0 0"/>
</joint>
<link name="body">
<visual>
<geometry>
<box size="${body_length} ${body_width} ${body_height}"/>
</geometry>
</visual>
</link>
<xacro:macro name="wheels" params="fb frontk rightk">
<link name="${fb}_wheel">
<visual>
<origin rpy="1.57079 0 0" xyz="0 0 0"/>
<geometry>
<cylinder length="${wheel_width}" radius="${wheel_radius}"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 0.5"/>
</material>
</visual>
</link>
<joint name="${fb}_joint" type="continuous">
<parent link="body"/>
<child link="${fb}_wheel"/>
<axis xyz="0 1 0"/>
<origin xyz="${frontk*wheel_distance/2} ${rightk*(body_width/2 + wheel_width/2)} 0" rpy="0 0 0"/>
</joint>
</xacro:macro>
<xacro:wheels fb="front_right" frontk ="1" rightk = "1" />
<xacro:wheels fb="front_left" frontk ="1" rightk = "-1" />
<xacro:wheels fb="back_right" frontk ="-1" rightk = "1" />
<xacro:wheels fb="back_left" frontk ="-1" rightk = "-1" />
</robot>
执行与原先同样的显示命令,得到效果为:
校验URDF模型,奇怪的是这种办法不认xacro的简化代码,只认原始代码。
check_urdf bot.urdf
也可以使用软件包查看urdf文件结构
sudo apt install liburdfdom-tools
在urdf文件所在的文件夹下,执行:
urdf_to_graphviz bot.urdf
得到urdf的结构图,是一个PDF文件:
上面的小车模型仅仅是一个图形模型,没有物理属性,我们来添加物理属性。
连杆的碰撞属性容易设置,这里只需与visual标签一致即可,没有特殊要求。为了赋予合理的质量、惯性属性,考虑车轮与车身的材料均为6061铝查询相关资料得6061铝合金密度 $2.75g/cm^3$ ,由上面设计的模型体积,可以计算出车身质量9900g,即9.9kg,每个车轮质量647.625g,即0.65kg
对宽度为$w$,高度为$h$,深度为$d$,质量为$m$ 的实心长方体:
对于半径为$r$,高度为$h$,质量为$m$的实心圆柱体。
有:
gazebo要求urdf文件中必须包含collision 标签、inertial标签,并且颜色设置需要使用指定的颜色标签。这与在rviz中显示的需求是不一样的,修改代码如下:
<gazebo reference="link节点名称">
<material>Gazebo/Blue</material>
</gazebo>
现在的urdf模型为:
<?xml version="1.0"?>
<robot name="fourbot">
<link name="base_link"/>
<joint name="base_joint" type="fixed">
<parent link="base_link"/>
<child link="body"/>
<origin xyz="0.0 0.0 0.05" rpy="0 0 0"/>
</joint>
<link name="body">
<visual>
<origin xyz="0 0 0.0" rpy="0 0 0"/>
<geometry>
<box size="0.3 0.2 0.06"/>
</geometry>
</visual>
<collision>
<origin xyz="0 0 0.0" rpy="0 0 0"/>
<geometry>
<box size="0.3 0.2 0.06"/>
</geometry>
</collision>
<inertial>
<mass value="9.9"/>
<inertia ixx="0.03597" ixy="0" ixz="0" iyy="0.10725" iyz="0" izz="0.07722"/>
</inertial>
</link>
<joint name="joint_front_right" type="continuous">
<parent link="body"/>
<child link="front_right_wheel"/>
<axis xyz="0 1 0"/>
<origin xyz="0.12 0.115 0" rpy="0 0 0"/>
</joint>
<joint name="joint_front_left" type="continuous">
<parent link="body"/>
<child link="front_left_wheel"/>
<axis xyz="0 1 0"/>
<origin xyz="0.12 -0.115 0" rpy="0 0 0"/>
</joint>
<joint name="joint_back_right" type="continuous">
<parent link="body"/>
<child link="back_right_wheel"/>
<axis xyz="0 1 0"/>
<origin xyz="-0.12 0.115 0" rpy="0 0 0"/>
</joint>
<joint name="joint_back_left" type="continuous">
<parent link="body"/>
<child link="back_left_wheel"/>
<axis xyz="0 1 0"/>
<origin xyz="-0.12 -0.115 0" rpy="0 0 0"/>
</joint>
<link name="front_right_wheel">
<visual>
<origin rpy="1.57079 0 0" xyz="0 0 0"/>
<geometry>
<cylinder length="0.03" radius="0.1"/>
</geometry>
</visual>
<collision>
<origin rpy="1.57079 0 0" xyz="0 0 0"/>
<geometry>
<cylinder length="0.03" radius="0.1"/>
</geometry>
</collision>
<inertial>
<mass value="0.65"/>
<inertia ixx="0.000455" ixy="0" ixz="0" iyy="0.000455" iyz="0" izz="0.000135"/>
</inertial>
</link>
<link name="front_left_wheel">
<visual>
<origin rpy="1.57079 0 0" xyz="0 0 0"/>
<geometry>
<cylinder length="0.03" radius="0.1"/>
</geometry>
</visual>
<collision>
<origin rpy="1.57079 0 0" xyz="0 0 0"/>
<geometry>
<cylinder length="0.03" radius="0.1"/>
</geometry>
</collision>
<inertial>
<mass value="0.65"/>
<inertia ixx="0.000455" ixy="0" ixz="0" iyy="0.000455" iyz="0" izz="0.000135"/>
</inertial>
</link>
<link name="back_right_wheel">
<visual>
<origin rpy="1.57079 0 0" xyz="0 0 0"/>
<geometry>
<cylinder length="0.03" radius="0.1"/>
</geometry>
</visual>
<collision>
<origin rpy="1.57079 0 0" xyz="0 0 0"/>
<geometry>
<cylinder length="0.03" radius="0.1"/>
</geometry>
</collision>
<inertial>
<mass value="0.65"/>
<inertia ixx="0.000455" ixy="0" ixz="0" iyy="0.000455" iyz="0" izz="0.000135"/>
</inertial>
</link>
<link name="back_left_wheel">
<visual>
<origin rpy="1.57079 0 0" xyz="0 0 0"/>
<geometry>
<cylinder length="0.03" radius="0.1"/>
</geometry>
</visual>
<collision>
<origin rpy="1.57079 0 0" xyz="0 0 0"/>
<geometry>
<cylinder length="0.03" radius="0.1"/>
</geometry>
</collision>
<inertial>
<mass value="0.65"/>
<inertia ixx="0.000455" ixy="0" ixz="0" iyy="0.000455" iyz="0" izz="0.000135"/>
</inertial>
</link>
<gazebo reference="front_right_wheel">
<material>Gazebo/Black</material>
</gazebo>
<gazebo reference="front_left_wheel">
<material>Gazebo/Black</material>
</gazebo>
<gazebo reference="back_right_wheel">
<material>Gazebo/Black</material>
</gazebo>
<gazebo reference="back_left_wheel">
<material>Gazebo/Black</material>
</gazebo>
<gazebo reference="body">
<material>Gazebo/Red</material>
</gazebo>
</robot>
在gazebo中打开urdf模型
使用命令行打开gazebo
gazebo --verbose -s libgazebo_ros_init.so -s libgazebo_ros_factory.so
成功打开gazebo
解释一下这句命令的含义:
这条命令行用于在 Gazebo 中加载特定的 ROS 插件,并启动 Gazebo 的详细日志模式。
1. gazebo:
这是启动 Gazebo 仿真器的命令。
-
--verbose:
这个选项启动 Gazebo 的详细日志模式。启用此选项后,Gazebo 会在终端中输出更多的调试和状态信息,这对排查问题和了解 Gazebo 内部工作原理非常有帮助。 -
-s libgazebo_ros_init.so:
这个选项加载 Gazebo 的 ROS 初始化插件。libgazebo_ros_init.so
插件的作用是初始化 ROS 环境,确保 Gazebo 可以与 ROS 进行通信。这是加载 ROS 功能到 Gazebo 中的关键一步。 -
-s libgazebo_ros_factory.so:
这个选项加载 Gazebo 的 ROS 工厂插件。libgazebo_ros_factory.so
插件的作用是提供将模型(如 URDF 模型)通过 ROS 加载到 Gazebo 仿真环境中的功能。这使得用户可以使用 ROS 话题和服务来在 Gazebo 中生成和控制模型。
这条命令行启动了 Gazebo 仿真器,并加载了两个重要的 ROS 插件(libgazebo_ros_init.so
和 libgazebo_ros_factory.so
),同时启用了详细日志输出模式。这使得 Gazebo 能够与 ROS 系统集成,并支持通过 ROS 加载和管理仿真模型。
下面在gazebo中加载机器人urdf模型,打开新的终端,执行:
ros2 run gazebo_ros spawn_entity.py -file /home/user/four_wheel_robot.urdf -entity fourbot
解释一下命令的含义
spawn_entity.py
是一个用于在 Gazebo 仿真环境中生成实体(如机器人模型)的 Python 脚本,通常与 ROS 2 一起使用。这个脚本是 Gazebo ROS 包的一部分,用于通过 ROS 2 接口在 Gazebo 中添加实体。spawn_entity.py
脚本的主要功能是从 ROS 参数服务器或直接从文件中获取 URDF 或 SDF 描述,并将其生成到 Gazebo 仿真环境中。它允许用户在运行中的 Gazebo 仿真环境中动态地添加机器人模型或其他实体。可以通过命令行运行,并接受多种参数。以下是常用的参数和示例用法:
ros2 run gazebo_ros spawn_entity.py [参数]
常用参数
-entity ENTITY_NAME
:指定要生成的实体的名称。这个名称将用于 Gazebo 中标识该实体。
-entity my_robot
-topic TOPIC_NAME
:指定包含实体描述(URDF 或 SDF)的 ROS 话题名称。
-topic /robot_description
-file FILE_PATH
:从指定的文件路径加载实体描述(URDF 或 SDF)。
-file /path/to/your/urdf_file.urdf
-x X_COORDINATE
:指定实体在 Gazebo 世界中的 x 坐标。
-x 1.0
-y Y_COORDINATE
:指定实体在 Gazebo 世界中的 y 坐标。
-y 2.0
-z Z_COORDINATE
:指定实体在 Gazebo 世界中的 z 坐标。
-z 0.5
-R ROLL
:指定实体的 roll 角度(旋转)。
-R 0.0
-P PITCH
:指定实体的 pitch 角度(俯仰)。
-P 0.0
-Y YAW
:指定实体的 yaw 角度(偏航)。
-Y 1.57
示例用法
- 从话题加载 URDF 并生成实体:
假设 URDF 已经通过 ROS 参数服务器发布在 /robot_description
话题上,可以使用以下命令将其生成到 Gazebo 中:
ros2 run gazebo_ros spawn_entity.py -topic /robot_description -entity my_robot
- 从文件加载 URDF 并生成实体:
假设 URDF 文件位于 /home/user/my_robot.urdf
,可以使用以下命令将其生成到 Gazebo 中:
ros2 run gazebo_ros spawn_entity.py -file /home/user/my_robot.urdf -entity my_robot
- 指定生成位置和姿态:
可以通过附加参数来指定实体在 Gazebo 世界中的位置和姿态:
ros2 run gazebo_ros spawn_entity.py -topic /robot_description -entity my_robot -x 1.0 -y 2.0 -z 0.5 -R 0.0 -P 0.0 -Y 1.57
添加仿真模块
要使小车能运动,除了具备物理属性之外,还需要有控制模块,这里引入差速驱动模块来驱动小车,再添加激光雷达模块备用。
先添加一个差速驱动模块,在urdf文件中添加:
<gazebo>
<!--差速驱动模块-->
<gazebo>
<plugin name="differential_drive_controller" filename="libgazebo_ros_diff_drive.so">
<!-- Plugin update rate in Hz -->
<update_rate>20</update_rate>
<left_joint>joint_front_left</left_joint>
<right_joint>joint_front_right</right_joint>
<wheel_separation>0.230</wheel_separation>
<wheel_diameter>0.100</wheel_diameter>
<!-- limits -->
<max_wheel_torque>20</max_wheel_torque>
<!-- output -->
<publish_odom>true</publish_odom>
<publish_odom_tf>true</publish_odom_tf>
<publish_wheel_tf>false</publish_wheel_tf>
<odometry_frame>odom</odometry_frame>
<robot_base_frame>base_link</robot_base_frame>
</plugin>
</gazebo>
注意这里的输出配置。
在添加激光lidar模块之前,我们先看看gazebo-ros包中有哪些插件。
ls /opt/ros/humble/lib | grep libgazebo_ros
我看到了一系列的插件,我想有必要了解一下这些插件的含义。但是我始终没有找到相关的完整文档,可能是由于这些插件经常更新导致的。求助chatgpt,得到的回复如下:
- libgazebo_ros_ackermann_drive.so:提供对使用Ackermann转向机构的车辆的控制和仿真。支持前轮转向和后轮驱动的汽车模型。
- libgazebo_ros_bumper.so:提供对碰撞传感器(bumper)的仿真。发布碰撞事件到 ROS 话题。
- libgazebo_ros_camera.so:提供对摄像头传感器的仿真。发布图像数据到 ROS 话题。
- libgazebo_ros_diff_drive.so:提供对差速驱动机器人的控制和仿真。支持左右轮差速驱动的机器人模型。
- libgazebo_ros_elevator.so:提供对电梯(升降机)的控制和仿真。控制升降平台的运动。
- libgazebo_ros_factory.so:支持在仿真中动态添加和移除模型。通过ROS服务加载和删除模型。
- libgazebo_ros_force.so:提供对力传感器的仿真。发布施加在机器人某部分上的力的数据。
- libgazebo_ros_force_system.so:提供对力系统的仿真。管理和应用力系统。
- libgazebo_ros_ft_sensor.so:提供对力/扭矩传感器的仿真。发布力和扭矩数据到 ROS 话题。
- libgazebo_ros_gps_sensor.so:提供对 GPS 传感器的仿真。发布 GPS 数据到 ROS 话题。
- libgazebo_ros_hand_of_god.so:提供对手动移动模型的控制。允许用户通过 GUI 界面手动移动模型。
- libgazebo_ros_harness.so:提供对悬挂系统的仿真。控制和模拟悬挂装置的行为。
- libgazebo_ros_imu_sensor.so:提供对 IMU 传感器的仿真。发布惯性测量单元(IMU)数据到 ROS 话题。
- libgazebo_ros_init.so:初始化 Gazebo ROS 插件。配置和初始化所有 Gazebo ROS 插件。
- libgazebo_ros_joint_pose_trajectory.so:提供对关节轨迹控制的仿真。控制机器人关节的轨迹运动。
- libgazebo_ros_joint_state_publisher.so:提供对关节状态发布的仿真。发布关节位置、速度和加速度数据到 ROS 话题。
- libgazebo_ros_node.so:提供 Gazebo 和 ROS 之间的节点集成。创建并管理 ROS 节点。
- libgazebo_ros_p3d.so:提供对三维位置(P3D)传感器的仿真。发布机器人位置和姿态数据到 ROS 话题。
- libgazebo_ros_planar_move.so:提供对平面运动的控制和仿真。控制机器人在平面上的运动。
- libgazebo_ros_projector.so:提供对投影仪的仿真。模拟投影仪投影图像。
- libgazebo_ros_properties.so:提供对模型属性的管理和仿真。设置和获取模型属性。
- libgazebo_ros_ray_sensor.so:提供对射线传感器的仿真(例如激光雷达)。发布射线传感器数据到 ROS 话题。
- libgazebo_ros_state.so:提供对仿真状态的管理和控制。发布仿真状态数据到 ROS 话题。
- libgazebo_ros_template.so:提供一个 Gazebo ROS 插件模板。作为开发新插件的基础。
- libgazebo_ros_tricycle_drive.so:提供对三轮车驱动的控制和仿真。支持前轮转向和后轮驱动的三轮车模型。
- libgazebo_ros_utils.so:提供各种实用工具函数。为其他 Gazebo ROS 插件提供支持功能。
- libgazebo_ros_vacuum_gripper.so: 提供对真空吸盘的仿真模拟真空吸盘的抓取和释放动作
- libgazebo_ros_video.so:提供对视频传感器的仿真。发布视频数据到 ROS 话题。
- libgazebo_ros_wheel_slip.so:提供对轮胎打滑的仿真。模拟轮胎在不同表面上的打滑行为。
在前面的工作中已经使用到了libgazebo_ros_factory
插件、 libgazebo_ros_diff_drive
插件。
下面我们要使用libgazebo_ros_ray_sensor
插件来仿真激光雷达。
参考官方文档
在urdf文件中添加:
<link name="laser_link">
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.1 0.1 0.2"/>
</geometry>
</collision>
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.1 0.1 0.2"/>
</geometry>
</visual>
<inertial>
<mass value="0.05" />
<origin xyz="0 0 0" rpy="0 0 0"/>
<inertia ixx="2e-4" ixy="0" ixz="0" iyy="8e-5" iyz="0" izz="2e-4" />
</inertial>
</link>
<joint name="laser_joint" type="fixed">
<parent link="body"/>
<child link="laser_link"/>
<origin xyz="0.0 0.0 0.13" rpy="0 0 0"/>
</joint>
<gazebo reference="laser_link">
<material>Gazebo/Blue</material>
</gazebo>
<!--激光雷达模块-->
<gazebo reference="laser_link">
<sensor name="laser_sensor" type="ray">
<always_on>true</always_on>
<pose>0 0 0 0 0 0</pose>
<!-- 当为true时,在gpu激光器的扫描区域内可见半透明激光射线。这可能是一个有用的信息可视化,也可能是一种麻烦。 -->
<visualize>true</visualize>
<!-- 发布频率,不宜太高,否则可能导致卡顿 -->
<update_rate>5</update_rate>
<ray>
<scan>
<horizontal>
<samples>360</samples>
<resolution>1.0</resolution>
<min_angle>-1.570796</min_angle>
<max_angle>1.570796</max_angle>
</horizontal>
</scan>
<range>
<min>0.25</min>
<max>5.0</max>
<resolution>0.01</resolution>
</range>
<noise>
<type>gaussian</type>
<mean>0.0</mean>
<stddev>0.01</stddev>
</noise>
</ray>
<plugin name="laserscan" filename="libgazebo_ros_ray_sensor.so">
<!-- 输出配置 -->
<ros>
<remapping>~/out:=scan</remapping>
</ros>
<output_type>sensor_msgs/LaserScan</output_type>
<frameName>laser_link</frameName>
</plugin>
</sensor>
</gazebo>
好了,一个四轮小车设计得差不多了,接下来我们将它导入到gazebo仿真环境中查看。
gazebo --verbose -s libgazebo_ros_init.so -s libgazebo_ros_factory.so
# 打开新的终端
ros2 run gazebo_ros spawn_entity.py -file /home/user/four_wheel_robot.urdf -entity fourbot
创建功能包
参考前面的博客创建功能包
首先我们需要创建一个节点,实现调用gazebo、rviz2、机器人模型。
新建文件夹名为rob1_ws,在文件夹中新建文件夹src,在src文件夹中新建功能包名为robbody,
mkdir -p rob1_ws/src
cd rob1_ws/src
ros2 pkg create robbody --dependencies rclcpp geometry_msgs sensor_msgs --build-type ament_cmake
解释一下创建功能包命令的含义:“create robbody”是指创建名为robbody的功能包,“build-type ament_cmake”指功能包类型为cmake,“dependencies”指的是这个功能包的依赖,功能包需要依赖于“rclcpp”包内容,可以类比于C程序中库函数的概念。
打开生成的robbody文件夹,先看看CMakeList.txt文件,其中会有:
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
这就对应了创建功能包时指令中指定的内容,如果后续需要其他依赖包,也需要在这里添加。
下面尝试创建一个简单的节点,在robbody/src/文件夹下的
cd ~/rob1_ws/src/robbody/src
touch node_simple.cpp
# 先放在这里,暂时不用
在robbody文件夹下新建文件夹“urdf_models”用于专门存放urdf模型文件。
cd ~/rob1_ws/src/robbody
mkdir urdf_models
mkdir launch
将此前的fourbot模型文件拷贝至urdf_models文件夹下,假设其名称为“four_wheel_robot.urdf”。
在launch文件夹下创建launch文件,文件命名为node_simple.launch.py
,这个文件需要的功能为:
- 启动gazebo环境
- 加载机器人模型到gazebo环境中
- 启动robot_state_publisher节点,发布机器人模型的tf变换
- 启动joint_state_publisher节点,发布机器人关节的状态
- 启动rviz2,可视化机器人模型和传感器数据
import os
from launch import LaunchDescription
from launch.actions import ExecuteProcess
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
def generate_launch_description():
robot_name_in_model = 'fourbot'
package_name = 'robbody'
urdf_name = "four_wheel_robot.urdf"
# 获取包路径
pkg_share = FindPackageShare(package=package_name).find(package_name)
urdf_model_path = os.path.join(pkg_share, f'urdf_models/{urdf_name}')
# 读取URDF文件内容
try:
with open(urdf_model_path, 'r') as infp:
robot_description = infp.read()
except Exception as e:
raise RuntimeError(f"Failed to read URDF file: {urdf_model_path}, error: {e}")
# 启动Gazebo服务器
start_gazebo_cmd = ExecuteProcess(
cmd=['gazebo', '--verbose', '-s', 'libgazebo_ros_init.so', '-s', 'libgazebo_ros_factory.so'],
output='screen')
# 启动robot_state_publisher节点
start_robot_state_publisher_cmd = Node(
package='robot_state_publisher',
executable='robot_state_publisher',
name='robot_state_publisher',
output='screen',
parameters=[{'robot_description': robot_description, 'use_sim_time': True}]
)
# 启动joint_state_publisher节点
start_joint_state_publisher_cmd = Node(
package='joint_state_publisher',
executable='joint_state_publisher',
name='joint_state_publisher',
output='screen',
parameters=[{'use_sim_time': True}]
)
# 启动RViz2
start_rviz_cmd = Node(
package='rviz2',
executable='rviz2',
name='rviz2',
output='screen',
parameters=[{'use_sim_time': True}]
)
# 加载机器人模型到Gazebo中
spawn_robot_cmd = Node(
package='gazebo_ros',
executable='spawn_entity.py',
arguments=['-entity', robot_name_in_model, '-file', urdf_model_path],
output='screen',
parameters=[{'use_sim_time': True}]
)
ld = LaunchDescription()
# 添加启动命令到LaunchDescription
ld.add_action(start_gazebo_cmd)
ld.add_action(start_robot_state_publisher_cmd)
ld.add_action(start_joint_state_publisher_cmd)
ld.add_action(start_rviz_cmd)
ld.add_action(spawn_robot_cmd)
return ld
在CMakeList.txt文件中添加内容:
install(DIRECTORY
urdf_models
launch
DESTINATION share/${PROJECT_NAME}/
)
除了launch中的5条任务外,还需要添加一个节点,用于控制小车运动。这里我们使用rqt_robot_steering
包,它是一个基于ROS的可视化界面,可以发布geometry_msgs/msg/Twist
消息,默认话题为/cmd_vel
,这是差速控制插件订阅的话题,通过它可以控制机器人前进、后退、左转、右转。
# 安装rqt_robot_steering包
sudo apt install ros-humble-rqt-robot-steering
ros2 run rqt_robot_steering rqt_robot_steering
# 或者,控制小海龟的软件包也可以有相同的功能,选用一个即可
sudo apt install ros-humble-teleop-twist-keyboard
ros2 run teleop_twist_keyboard teleop_twist_keyboard
安装完rqt_robot_steering包之后,编译并执行,注意是在工作空间的文件夹中
cd ~/rob1_ws
colcon build
source install/setup.bash
ros2 launch robbody node_simple.launch.py
可以看到成功打开了gazebo环境、rviz2、机器人模型、joint_state_publisher、robot_state_publisher节点。
此时我们修改rviz的配置,设置 Fixed Frame 为 odom
,添加'RobotModel',选择topic为'/robot_description',添加'LaserScan',选择topic为'/scan',保存配置。
(配置可参阅fishros教程)
在新的终端中执行
ros2 run rqt_robot_steering rqt_robot_steering
打开rviz2,可以看到机器人模型和激光雷达数据。
我们也可以自己写一个节点,发布/cmd_vel话题,控制小车运动,这里就不再赘述,本篇笔记的重点是定位与建图。
SLAM建图
绘制测试地图
首先简单地随便画一个测试使用的地图,参照fishros教程
具体步骤为:
打开gazebo,选择左上角Edit--Building Editor,选择create Walls,然后在界面的上半部分白色网格中自定义墙体,注意比例尺,建图的范围要与小车的大小相适应,不能太小叶不宜太大。
画完之后点击右上角File--Exit,此时弹出保存界面,自定义文件名称并保存即可。然后就进入了gazebo界面,再添加一些方形物体和圆柱形物体作为障碍物,添加完毕后保存为.world
文件。
在前面的工作空间中,与launch、urdf_models文件夹同级新建一个maps
文件夹,将刚才保存的.world
文件放入其中。
cd ~/rob1_ws/src/robbody/
mkdir maps
cp ~/gazebo_worlds/my_map.world maps/test_map.world
修改launch文件,使得gazebo启动时自动打开刚才保存的.world
文件。
gazebo_world_path = os.path.join(pkg_share, 'maps/test_map.world')
start_gazebo_cmd = ExecuteProcess(
cmd=['gazebo', '--verbose', '-s', 'libgazebo_ros_init.so', '-s', 'libgazebo_ros_factory.so', gazebo_world_path],
output='screen')
修改CmakeList.txt文件,添加maps文件夹的编译:
install(DIRECTORY
urdf_models
launch
maps
DESTINATION share/${PROJECT_NAME}/
)
然后编译,运行,可以在rviz中看到雷达识别到了障碍物的位置。
安装SLAM建图软件包
有很多开源的SLAM建图软件包,典型代表有: Gmapping、Hector SLAM、Cartographer、RTAB-Map、ORB-SLAM2/ORB-SLAM3等。
Gmapping是一种基于粒子滤波的2D激光SLAM算法,适用于ROS(Robot Operating System),不适用于ROS2。简单易用,适合初学者;在ROS社区中广泛使用。Gmapping GitHub
Hector SLAM是一种基于快速响应和高分辨率的2D激光SLAM算法,不需要轮速计数据。适用于无轮速计的小车,性能较好,实时性强。
Cartographer是Google推出的SLAM库,支持2D和3D建图,适用于复杂环境下的高精度建图。高精度,支持多种传感器输入,活跃的社区支持。它已被广泛应用于工业实践。
RTAB-Map(Real-Time Appearance-Based Mapping)是一种RGB-D、激光和立体相机支持的图像SLAM算法,适用于2D和3D建图。支持多种传感器,适用于室内和室外环境,支持大规模建图。
ORB-SLAM2/ORB-SLAM3是基于特征点的视觉SLAM算法,支持单目、立体和RGB-D相机。精度高,支持回环检测,适用于复杂的视觉环境。
由于Cartographer比较复杂,这里先从一个简单点的slam_toolbox
开始。
安装slam_toolbox软件包:
sudo apt-get install ros-humble-slam-toolbox
安装完成后打开配置文件
# 这个配置文件夹下的四个文件对应了不同情形下的建图参数
cd /opt/ros/humble/share/slam_toolbox/config/
# 这里使用在线、异步建图
sudo vim mapper_params_online_async.yaml
阅读并核对配置文件,要求配置文件中的参数要和前面的urdf模型设置相匹配,例如base_frame
需要由默认的base_footprint
改为base_link
,如果不修改配置则会导致建图失败!
修改完成后保存并退出。
运行SLAM建图
打开前面的ros包工作空间,编译运行
cd ~/rob1_ws
colcon build
source install/setup.bash
ros2 launch robbody node_simple.launch.py
在新的终端中打开控制程序包
ros2 run rqt_robot_steering rqt_robot_steering
打开新的终端,启动SLAM建图
ros2 launch slam_toolbox online_async_launch.py
在rviz中,添加map,选择topic为/map
,操纵小车开始移动,可以看到SLAM建图的过程。
建图完成后还要保存地图,这里使用nav2_map_server
软件包保存地图。
# 安装nav2_map_server软件包
sudo apt install ros-humble-nav2-map-server
# 可以通过help查看帮助信息
ros2 run nav2_map_server map_saver_cli --help
把地图保存到maps
文件夹中,并命名为get_fourbot_map.yaml
。
# -t 是话题参数,-f 是保存的文件名
cd ~/ros2_ws/src/robbody/maps
ros2 run nav2_map_server map_saver_cli -t map -f get_fourbot_map
获得了两个文件,一个是.pgm
格式的地图文件,一个是.yaml
格式的地图描述文件。
程序汇总
上面的方案需要单独在新的终端中运行slam_toolbox
节点和rqt_robot_steering
节点,有点麻烦,可以将它们一起塞进launch文件中。
#启动rqt_robot_steering节点
start_rqt_robot_steering_cmd = Node(
package='rqt_robot_steering',
executable='rqt_robot_steering',
name='rqt_robot_steering',
output='screen'
)
# 启动slam_toolbox节点
slam_toolbox_cmd = Node(
package='slam_toolbox',
executable='async_slam_toolbox_node',
name='slam_toolbox',
output='screen',
parameters=[{'use_sim_time': True}, {'base_frame': 'base_link'}]
)
ld.add_action(slam_toolbox_cmd)
ld.add_action(start_rqt_robot_steering_cmd)
编译后运行,就方便很多了。
手动操纵小车在地图里跑一跑,可以看到SLAM建图的过程,建图完成后保存地图即可。
实验总结
这篇笔记的重点有二,一是WSL的使用,二是一个简单的建图任务。我没有从源码编译建图软件包,也没有使用更常用的Cartographer,而是选择了slam_toolbox,只求简便。
存在的不足是,小车模型的设计不太好看,车轮大小与车身大小比例失调。绘制测试地图时,也没有考虑好尺寸问题,导致地图特别大。手动操纵小车时很不方便,比较难操纵。
资源下载,压缩文件内包含:
- 四轮小车模型文件:
four_wheel_robot.urdf
- launch文件:
node_simple.launch.py
- rviz配置文件:
fourbot.rviz
- 随便绘制的测试地图环境:
test_map.world
- readme.txt
参阅
- 本站博客:ROS2学习笔记1-安装、编译和基本概念
- 本站博客:ROS2学习笔记本2-仿真软件与避障机器人实例
- 微软官方:适用于 Linux 的 Windows 子系统文档
- 微软官方:镜像模式网络
- 微软官方:.wslconfig
- 清华源
- ubuntu 20.04 设置国内镜像源(阿里源、清华源)
- fishros教程
- https://index.ros.org/
- 使用gazebo加载URDF
- ROS入门(七)——仿真机器人三(Gazebo+Xacro)
- ROS2学习笔记之C++编写简单发布订阅节点篇
- ROS2学习遇到的问题1---TF_OLD_DATA
- gazebo_ros_pkgs
- GhatGPT-4o