ros-humble仿真-建图-导航
注意:如下教程基于安装了ros-humble-desktop的情况
1. 创建包
ros2 pkg create --build-type ament_cmake car_sim_bringup
其中目录结构如下
什么目录没有就直接mkdir创建就行
car_sim_bringup/
├── CMakeLists.txt
├── config
│ ├── custom_nav2_params.yaml
│ └── diff_drive_controller.yaml
├── include
│ └── car_sim_bringup
├── launch
│ └── sim.launch.py
├── maps
│ ├── room.pgm
│ └── room.yaml
├── package.xml
├── src
├── urdf
│ └── robot.urdf.xacro
└── worlds
└── indoor.world
1.1创建机器人描述文件
在urdf目录下新建robot.urdf.xacro
向其中写入如下内容
注意导致小车无法旋转的摩擦力方向
其中还加入了激光雷达,imu,和四轮差速控制器插件
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="four_wheel_robot">
<!-- 参数定义 -->
<xacro:property name="chassis_length" value="0.4"/>
<xacro:property name="chassis_width" value="0.3"/>
<xacro:property name="chassis_height" value="0.1"/>
<xacro:property name="wheel_radius" value="0.05"/>
<xacro:property name="wheel_width" value="0.04"/>
<xacro:property name="wheel_x_offset" value="0.12"/>
<xacro:property name="wheel_y_offset" value="0.18"/>
<!-- 激光雷达参数 -->
<xacro:property name="lidar_radius" value="0.04"/>
<xacro:property name="lidar_length" value="0.04"/>
<!-- ==================== 1. 底盘与基准坐标系 ==================== -->
<link name="base_footprint"/>
<joint name="base_footprint_joint" type="fixed">
<parent link="base_footprint"/>
<child link="base_link"/>
<origin xyz="0 0 ${wheel_radius}" rpy="0 0 0"/>
</joint>
<link name="base_link">
<visual>
<origin xyz="0 0 ${chassis_height/2}"/>
<geometry>
<box size="${chassis_length} ${chassis_width} ${chassis_height}"/>
</geometry>
<material name="blue">
<color rgba="0.2 0.2 0.8 1.0"/>
</material>
</visual>
<collision>
<origin xyz="0 0 ${chassis_height/2}"/>
<geometry>
<box size="${chassis_length} ${chassis_width} ${chassis_height}"/>
</geometry>
</collision>
<inertial>
<origin xyz="0 0 ${chassis_height/2}"/>
<mass value="5.0"/>
<inertia ixx="0.041" ixy="0" ixz="0" iyy="0.071" iyz="0" izz="0.104"/>
</inertial>
</link>
<!-- ==================== 2. 车轮宏定义与实例化 ==================== -->
<xacro:macro name="wheel" params="prefix x_reflect y_reflect">
<link name="${prefix}_wheel">
<visual>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
<material name="black">
<color rgba="0.1 0.1 0.1 1.0"/>
</material>
</visual>
<collision>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
</collision>
<inertial>
<mass value="0.5"/>
<inertia ixx="0.00038" ixy="0" ixz="0" iyy="0.00038" iyz="0" izz="0.000625"/>
</inertial>
</link>
<joint name="${prefix}_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="${prefix}_wheel"/>
<origin xyz="${x_reflect * wheel_x_offset} ${y_reflect * wheel_y_offset} 0" rpy="-1.5708 0 0"/>
<axis xyz="0 0 1"/>
<limit effort="50.0" velocity="10.0"/>
</joint>
<gazebo reference="${prefix}_wheel">
<mu1>1.0</mu1>
<mu2>0.2</mu2>
<!-- 这个不加会导致横向和纵向摩擦系数一致,从而导致小车无法滑动转圈 -->
<fdir1>1 0 0</fdir1> <!-- 指定轮子滚动切向为局部坐标系 X 轴 -->
<kp>10000000.0</kp>
<kd>1.0</kd>
<material>Gazebo/DarkGrey</material>
</gazebo>
</xacro:macro>
<xacro:wheel prefix="front_left" x_reflect="1" y_reflect="1" />
<xacro:wheel prefix="front_right" x_reflect="1" y_reflect="-1" />
<xacro:wheel prefix="rear_left" x_reflect="-1" y_reflect="1" />
<xacro:wheel prefix="rear_right" x_reflect="-1" y_reflect="-1" />
<!-- ==================== 3. 2D 激光雷达传感器 ==================== -->
<link name="laser_link">
<visual>
<geometry>
<cylinder radius="${lidar_radius}" length="${lidar_length}"/>
</geometry>
<material name="red">
<color rgba="0.8 0.1 0.1 1.0"/>
</material>
</visual>
<collision>
<geometry>
<cylinder radius="${lidar_radius}" length="${lidar_length}"/>
</geometry>
</collision>
<inertial>
<mass value="0.125"/>
<inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0001"/>
</inertial>
</link>
<!-- 雷达安装在车顶前方 0.08m 处,抬高半个雷达高度 -->
<joint name="laser_joint" type="fixed">
<parent link="base_link"/>
<child link="laser_link"/>
<origin xyz="0.08 0 ${chassis_height + lidar_length/2}" rpy="0 0 0"/>
</joint>
<!-- Gazebo 2D 激光雷达插件 (ray sensor) -->
<gazebo reference="laser_link">
<material>Gazebo/Red</material>
<sensor name="lidar_sensor" type="ray">
<always_on>true</always_on>
<visualize>false</visualize>
<update_rate>10</update_rate>
<ray>
<scan>
<horizontal>
<samples>360</samples>
<resolution>1</resolution>
<min_angle>-3.14159265</min_angle>
<max_angle>3.14159265</max_angle>
</horizontal>
</scan>
<range>
<min>0.12</min>
<max>12.0</max>
<resolution>0.01</resolution>
</range>
<noise>
<type>gaussian</type>
<mean>0.0</mean>
<stddev>0.01</stddev>
</noise>
</ray>
<plugin name="lidar_plugin" filename="libgazebo_ros_ray_sensor.so">
<ros>
<remapping>~/out:=scan</remapping>
</ros>
<output_type>sensor_msgs/LaserScan</output_type>
<frame_name>laser_link</frame_name>
</plugin>
</sensor>
</gazebo>
<!-- ==================== 4. IMU 传感器 ==================== -->
<link name="imu_link">
<visual>
<geometry>
<box size="0.02 0.02 0.01"/>
</geometry>
<material name="green">
<color rgba="0.1 0.8 0.1 1.0"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.02 0.02 0.01"/>
</geometry>
</collision>
<inertial>
<mass value="0.01"/>
<inertia ixx="1e-5" ixy="0" ixz="0" iyy="1e-5" iyz="0" izz="1e-5"/>
</inertial>
</link>
<!-- IMU 安装在底盘内部质心几何中心 -->
<joint name="imu_joint" type="fixed">
<parent link="base_link"/>
<child link="imu_link"/>
<origin xyz="0 0 ${chassis_height/2}" rpy="0 0 0"/>
</joint>
<!-- Gazebo IMU 传感器插件 -->
<gazebo reference="imu_link">
<material>Gazebo/Green</material>
<sensor name="imu_sensor" type="imu">
<always_on>true</always_on>
<update_rate>100</update_rate>
<imu>
<angular_velocity>
<x>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>2e-4</stddev>
</noise>
</x>
<y>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>2e-4</stddev>
</noise>
</y>
<z>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>2e-4</stddev>
</noise>
</z>
</angular_velocity>
<linear_acceleration>
<x>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>1.7e-2</stddev>
</noise>
</x>
<y>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>1.7e-2</stddev>
</noise>
</y>
<z>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>1.7e-2</stddev>
</noise>
</z>
</linear_acceleration>
</imu>
<plugin name="imu_plugin" filename="libgazebo_ros_imu_sensor.so">
<ros>
<remapping>~/out:=imu/data</remapping>
</ros>
<initial_orientation_as_reference>false</initial_orientation_as_reference>
<frame_name>imu_link</frame_name>
</plugin>
</sensor>
</gazebo>
<!-- ==================== 5. ros2_control 硬件接口 ==================== -->
<ros2_control name="GazeboSystem" type="system">
<hardware>
<plugin>gazebo_ros2_control/GazeboSystem</plugin>
</hardware>
<xacro:macro name="wheel_control" params="joint_name">
<joint name="${joint_name}">
<command_interface name="velocity">
<param name="min">-10</param>
<param name="max">10</param>
</command_interface>
<state_interface name="position"/>
<state_interface name="velocity"/>
</joint>
</xacro:macro>
<xacro:wheel_control joint_name="front_left_wheel_joint"/>
<xacro:wheel_control joint_name="front_right_wheel_joint"/>
<xacro:wheel_control joint_name="rear_left_wheel_joint"/>
<xacro:wheel_control joint_name="rear_right_wheel_joint"/>
</ros2_control>
<!-- ==================== 6. 控制器参数与重映射 ==================== -->
<xacro:arg name="controller_params_file" default="$(find-pkg-share your_robot_package)/config/diff_drive_controller.yaml"/>
<gazebo>
<plugin filename="libgazebo_ros2_control.so" name="gazebo_ros2_control">
<parameters>$(arg controller_params_file)</parameters>
<ros>
<remapping>/diff_drive_base_controller/cmd_vel_unstamped:=/cmd_vel</remapping>
<remapping>/diff_drive_base_controller/odom:=/odom</remapping>
</ros>
</plugin>
</gazebo>
</robot>
1.2 写入世界文件给gazebo用
在worlds目录下创建indoor.world,并写入如下内容
<?xml version="1.0" ?>
<sdf version="1.6">
<world name="indoor_world">
<!-- 基础光源与地面 -->
<include>
<uri>model://sun</uri>
</include>
<include>
<uri>model://ground_plane</uri>
</include>
<physics type="ode">
<real_time_update_rate>1000.0</real_time_update_rate>
<max_step_size>0.001</max_step_size>
</physics>
<!-- 室内建筑墙体与障碍物模型 (10m x 8m 多隔间结构) -->
<model name="indoor_environment">
<static>true</static>
<!-- 外墙: 北 -->
<link name="wall_north">
<pose>0 4 0.5 0 0 0</pose>
<collision name="col"><geometry><box><size>10 0.15 1.0</size></box></geometry></collision>
<visual name="vis"><geometry><box><size>10 0.15 1.0</size></box></geometry><material><ambient>0.8 0.8 0.8 1</ambient></material></visual>
</link>
<!-- 外墙: 南 -->
<link name="wall_south">
<pose>0 -4 0.5 0 0 0</pose>
<collision name="col"><geometry><box><size>10 0.15 1.0</size></box></geometry></collision>
<visual name="vis"><geometry><box><size>10 0.15 1.0</size></box></geometry><material><ambient>0.8 0.8 0.8 1</ambient></material></visual>
</link>
<!-- 外墙: 西 -->
<link name="wall_west">
<pose>-5 0 0.5 0 0 0</pose>
<collision name="col"><geometry><box><size>0.15 8 1.0</size></box></geometry></collision>
<visual name="vis"><geometry><box><size>0.15 8 1.0</size></box></geometry><material><ambient>0.8 0.8 0.8 1</ambient></material></visual>
</link>
<!-- 外墙: 东 -->
<link name="wall_east">
<pose>5 0 0.5 0 0 0</pose>
<collision name="col"><geometry><box><size>0.15 8 1.0</size></box></geometry></collision>
<visual name="vis"><geometry><box><size>0.15 8 1.0</size></box></geometry><material><ambient>0.8 0.8 0.8 1</ambient></material></visual>
</link>
<!-- 室内隔墙 1 (左侧房间隔离,留出门洞) -->
<link name="inner_wall_1">
<pose>-1.8 1.2 0.5 0 0 0</pose>
<collision name="col"><geometry><box><size>0.15 5.5 1.0</size></box></geometry></collision>
<visual name="vis"><geometry><box><size>0.15 5.5 1.0</size></box></geometry><material><ambient>0.7 0.7 0.7 1</ambient></material></visual>
</link>
<!-- 室内隔墙 2 (右侧房间隔离,留出门洞) -->
<link name="inner_wall_2">
<pose>1.8 -1.2 0.5 0 0 0</pose>
<collision name="col"><geometry><box><size>0.15 5.5 1.0</size></box></geometry></collision>
<visual name="vis"><geometry><box><size>0.15 5.5 1.0</size></box></geometry><material><ambient>0.7 0.7 0.7 1</ambient></material></visual>
</link>
<!-- 房间内圆柱障碍物 1 -->
<link name="obstacle_cylinder_1">
<pose>-3.5 -2.0 0.5 0 0 0</pose>
<collision name="col"><geometry><cylinder><radius>0.3</radius><length>1.0</length></cylinder></geometry></collision>
<visual name="vis"><geometry><cylinder><radius>0.3</radius><length>1.0</length></cylinder></geometry><material><ambient>0.3 0.6 0.9 1</ambient></material></visual>
</link>
<!-- 房间内圆柱障碍物 2 -->
<link name="obstacle_cylinder_2">
<pose>3.5 2.0 0.5 0 0 0</pose>
<collision name="col"><geometry><cylinder><radius>0.3</radius><length>1.0</length></cylinder></geometry></collision>
<visual name="vis"><geometry><cylinder><radius>0.3</radius><length>1.0</length></cylinder></geometry><material><ambient>0.3 0.6 0.9 1</ambient></material></visual>
</link>
</model>
</world>
</sdf>
1.3 编写launch文件
在launch目录下创建 sim.launch.py,并写入如下内容
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription, RegisterEventHandler
from launch.event_handlers import OnProcessExit
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
import xacro
def generate_launch_description():
pkg_share = get_package_share_directory('car_sim_bringup')
gazebo_ros_pkg = get_package_share_directory('gazebo_ros')
# 1. 获取文件路径
xacro_file = os.path.join(pkg_share, 'urdf', 'robot.urdf.xacro')
controller_params_file = os.path.join(pkg_share, 'config', 'diff_drive_controller.yaml')
world_file = os.path.join(pkg_share, 'worlds', 'indoor.world')
# 2. 解析 Xacro
robot_description_raw = xacro.process_file(
xacro_file,
mappings={'controller_params_file': controller_params_file}
).toxml()
# 3. 基础节点
node_robot_state_publisher = Node(
package='robot_state_publisher',
executable='robot_state_publisher',
output='screen',
parameters=[{
'robot_description': robot_description_raw,
'use_sim_time': True
}]
)
# 启动 Gazebo 并载入 indoor.world
launch_gazebo = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(gazebo_ros_pkg, 'launch', 'gazebo.launch.py')
),
launch_arguments={'world': world_file}.items()
)
# 生成小车实体(指定初始安全坐标 x=0, y=0, z=0.05)
node_spawn_entity = Node(
package='gazebo_ros',
executable='spawn_entity.py',
arguments=[
'-topic', 'robot_description',
'-entity', 'four_wheel_robot',
'-x', '0.0',
'-y', '0.0',
'-z', '0.05'
],
output='screen'
)
# 控制器 Spawner
joint_state_broadcaster_spawner = Node(
package='controller_manager',
executable='spawner',
arguments=['joint_state_broadcaster'],
output='screen'
)
diff_drive_spawner = Node(
package='controller_manager',
executable='spawner',
arguments=['diff_drive_base_controller'],
output='screen'
)
return LaunchDescription([
node_robot_state_publisher,
launch_gazebo,
node_spawn_entity,
RegisterEventHandler(
event_handler=OnProcessExit(
target_action=node_spawn_entity,
on_exit=[joint_state_broadcaster_spawner],
)
),
RegisterEventHandler(
event_handler=OnProcessExit(
target_action=joint_state_broadcaster_spawner,
on_exit=[diff_drive_spawner],
)
)
])
1.4 写入差速控制器配置文件
由于引入了差速控制器,所以需要一个单独的配置文件来和已有的模型关联起来
在config目录下,创建diff_drive_controller.yaml文件并且写入如下内容
controller_manager:
ros__parameters:
update_rate: 50
diff_drive_base_controller:
type: diff_drive_controller/DiffDriveController
joint_state_broadcaster:
type: joint_state_broadcaster/JointStateBroadcaster
diff_drive_base_controller:
ros__parameters:
# 左右侧分别填入前后两轮关节名
left_wheel_names: ["front_left_wheel_joint", "rear_left_wheel_joint"]
right_wheel_names: ["front_right_wheel_joint", "rear_right_wheel_joint"]
# 左右轮距与轮半径(必须与模型一致)
wheel_separation: 0.36
wheel_radius: 0.05
# 四轮差速转向补偿系数(通常在 1.1 ~ 1.5 之间调节)
wheel_separation_multiplier: 1.2
publish_rate: 50.0
odom_frame_id: odom
base_frame_id: base_footprint
pose_covariance_diagonal: [0.001, 0.001, 0.001, 0.001, 0.001, 0.01]
twist_covariance_diagonal: [0.001, 0.001, 0.001, 0.001, 0.001, 0.01]
open_loop: false
enable_odom_tf: true
cmd_vel_timeout: 0.5
use_stamped_vel: false
2.安装nav2和slam_toolbox
sudo apt update
sudo apt install -y \
ros-humble-slam-toolbox \
ros-humble-navigation2 \
ros-humble-nav2-bringup
2.1 自定义nav2配置文件
先复制一份其默认的配置文件
cp /opt/ros/humble/share/nav2_bringup/params/nav2_params.yaml \
~/ros2_ws/src/当前包/config/custom_nav2_params.yaml
主要修改一下其机器人半径,即robot_radius为footprint(因为不是圆形机器人)
在custom_nav2_params.yaml中,找到local和global的costmap,将其中的robot_radius给注释掉,替换成如下的准确的正方形信息
footprint: "[ [0.22, 0.17], [0.22, -0.17], [-0.22, -0.17], [-0.22, 0.17] ]"
2.2 更新CMakeLists.txt
添加如下内容,把资源让cmake装到share目录下,方便launch脚本找到
install(DIRECTORY
launch
DESTINATION share/${PROJECT_NAME}
)
install(DIRECTORY
urdf
DESTINATION share/${PROJECT_NAME}
)
install(DIRECTORY
config
DESTINATION share/${PROJECT_NAME}
)
install(DIRECTORY
worlds
DESTINATION share/${PROJECT_NAME}
)
2.3 构建并加载环境变量
cd ~/ws
colcon build
source ./install/setup.bash
3.1 启动launch文件
ros2 launch car_sim_bringup sim.launch.py
会启动机器人状态发布节点
gazebo仿真
差速控制器节点
3.3 启动rviz2
rviz2
这里我没在launch文件中一起启动rviz2,直接启动,然后手动添加添加需要的内容就行了

记得map添加三个,一个地图,一个local_cost_map,一个global_cost_map (启动nav2后就有了)
然后等会退出的时候问是否保存,选择save,这样下次这些话题就自己添加上了
3.3 启动slam_toolbox
slam_toolbox的配置我先没整自定义的,暂时没必要
直接启动就行
ros2 launch slam_toolbox online_async_launch.py
使用键盘控制器控制着建图,速度稍微慢点
ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -p stamped:=false
自己控制着建图完成后,直接保存地图即可
进入maps目录,直接运行
ros2 run nav2_map_server map_saver_cli -f room
然后就会在当前目录下创建room.yaml和rooms.pgm文件
可以启动nav2
由于slam_toolbox启动后会发布/map话题,所以这里已经可以使用不加载地图的nav2了
ros2 launch nav2_bringup navigation_launch.py params_file:=./config/custom_nav2_params.yaml use_sim_time:=true
有/clock话题,就可以把use_sim_time:=true带上
3.4 启动nav2导航
这里就采用加载地图的nav2启动方式
ros2 launch nav2_bringup bringup_launch.py map:=./maps/room.yaml params_file:=./config/custom_nav2_params.yaml use_sim_time:=true
需要发布一个初始位置
记得把Fixed Frame切换为map
然后使用上面的2D Pose Estimate发布一个带方向的初始位置,amcl节点会根据激光雷达数据帮忙在局部校准一下odom->map的映射关系
然后用2D Goal Pose开始测试导航就行
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)