第 17 節

Robot Navigation with Navigation2 (Physical Robot)

0瀏覽次數0訪問次數--跳出率--平均停留

Preparation Work

  1. physical object
  2. Already ran through robot navigation Navigation2 (Simulation) once.
  3. This chapter only covers the general implementation approach. Generally, once you understand the simulation section, reading through the main ideas of the physical section will be enough to grasp how to implement it.
  4. This chapter is not from Zhao Xuzuo's tutorial; it is my own implementation approach, provided for reference only. Teacher Zhao may have a better method, but he hasn't released the hardware section tutorial yet.
  5. This chapter uses a 4-wheel Mecanum wheel implementation, for reference only.

All of the code below is in this GitHub repository:

https://github.com/CyberNaviRobot/CyberRobot\_ROS2\_WS

The tutorials below only include implementation ideas and won't include source code, so it's recommended to clone this repository and check the source code.

https://docs.nav2.org/configuration/index.html

SLAM localization and mapping

slam_toolbox

Based on the node description above, we need to subscribe to /scan and /tf.

Generally, LiDAR manuals provide source code for publishing /scan, so please refer to your hardware's manual for this.

/tf requires us to publish the transformation from odom_frame to base_frame. We must use C++ code to dynamically publish the coordinate transformation for odom.

However, here you need to publish /odom in order to know the robot's position and orientation, so that the robot's location in the map can be inferred.

Ensure that all parameters of the SLAM Toolbox are set correctly, especially the coordinate systems and similar settings. Other parameters can be fine-tuned according to the documentation.

Ensure the topic published by the LiDAR is /scan.

First, start the LiDAR.

Restart the URDF model and publish TF at the same time.

Finally, start SLAM.

ros2 launch mycar_slam_slam_toolbox online_sync_launch.py use_sim_time:=false

cartographer

According to the Cartographer documentation, we only need /scan and /odom.

First, open the serial port receiving node to receive data transmitted from the STM32.

Then open the odometry node and publish the odom topic.

Publish TF again. You can directly use launch to start robot_state_publisher and joint_state_publisher, and open the URDF model to publish TF.

Start the LiDAR node and publish the scan topic.

After /odom, /scan, and TF are all published, open Cartographer for mapping, then check whether the TF for map has been published.

Check the TF tree as follows:

The mapping is as follows

Map service

Save the map (serialization)

mkdir ./map
ros2 run nav2_map_server map_saver_cli -f map/my_map

Read the map (deserialization)

ros2 launch mycar_map_server map_server.launch.py

AMCL Adaptive Monte Carlo Localization

First, map data is needed to publish the /map topic.

Second, LiDAR data is needed, specifically the /scan topic.

Then a coordinate transform message is needed, specifically the /tf topic.

Then the /initial_pose topic is the initial position on the 2D map. It can be published using rviz2 or via C++ code.

Then you need to modify the parameters:

This is the official website for the parameter:

https://docs.nav2.org/configuration/packages/configuring-amcl.html#

After modifying the Launch, then modify the params parameters.

Here, OmniChassis refers not only to omnidirectional wheel chassis, but to the broader category of omnidirectional wheel chassis — for example, both omnidirectional wheel chassis and Mecanum wheel chassis fall under this category. Of course, you can also define custom chassis types.

Set use_sim_time to False at the top of this configuration file.

There are too many topics involved, so we've listed them in a table:

  1. Subscribed topics
Topicinterfacedescription
/goal_posegeometry_msgs/msg/PoseStampedNavigation target point, used to trigger a navigation task
/tftf2_msgs/msg/TFMessageCoordinate transformation message, used for conversion between different coordinate systems.
/odomnav_msgs/msg/OdometryOdometry data provides the robot's position and motion information.
Topicinterfacedescription
/global_costmap/footprintgeometry_msgs/msg/PolygonThe footprint information of a robot (or any mobile platform). The footprint is the shape of the space the robot occupies on the map, typically represented as a polygon.
/mapnav_msgs/msg/OccupancyGridPublish the environment map, especially the Occupancy Grid Map used for navigation.
/scansensor_msgs/msg/LaserScanLaser scanning data.
Topicinterfacedescription
/odomnav_msgs/msg/OdometryThe robot's odometry information, including position, velocity, and orientation.
/speed_limitnav2_msgs/msg/SpeedLimitSpeed limit information during navigation, used to dynamically adjust the robot's movement speed.
Topicinterfacedescription
/local_costmap/footprintgeometry_msgs/msg/PolygonFootprint polygon of a robot or mobile platform, used for local costmap calculation.
/scansensor_msgs/msg/LaserScanLaser scanner scan data, used for environmental perception and obstacle avoidance.
Topicinterfacedescription
/clockrosgraph_msgs/msg/ClockROS system time
/cmd_vel_teleopgeometry_msgs/msg/TwistTeleoperation commands for controlling the robot's linear and angular velocity.
/local_costmap/costmap_rawnav2_msgs/msg/CostmapRaw data of the local costmap
/local_costmap/published_footprintgeometry_msgs/msg/PolygonStampedThe robot's published footprint in the local costmap
/preempt_teleopstd_msgs/msg/EmptyTeleoperation preemption signal, used to interrupt the current teleoperation.
Topicinterfacedescription
/global_costmap/costmap_rawnav2_msgs/msg/CostmapRaw data of the global costmap, used for path planning.
/global_costmap/published_footprintgeometry_msgs/msg/PolygonStampedThe robot's footprint representation in the global costmap
Topicinterfacedescription
/cmd_vel_navgeometry_msgs/msg/TwistTopic for receiving velocity control commands from other nodes
  1. Published topics
Topicinterfacedescription
/plannav_msgs/msg/PathGlobal path from current position to target point
Topicinterfacedescription
/global_costmap/costmapnav_msgs/msg/OccupancyGridPublish the current state of the global costmap.
/global_costmap/costmap_rawnav2_msgs/msg/CostmapRaw costmap data without further processing.
/global_costmap/costmap_updatesmap_msgs/msg/OccupancyGridUpdateThe update of the global costmap, this message can efficiently update the map.
/global_costmap/published_footprintgeometry_msgs/msg/PolygonStampedPublish the robot's footprint, which is the shape of the space the robot occupies on the map.
Topic NameMessage typedescription
/cmd_vel_navgeometry_msgs/msg/TwistPublish control commands, including linear and angular velocity, to move the robot along the planned path.
/cost_cloudsensor_msgs/msg/PointCloud2Publish point cloud data in the cost map for obstacle avoidance and path planning.
/local_plannav_msgs/msg/PathPublish the local path planning result, i.e., how the robot should reach a point near the current target point.
/markervisualization_msgs/msg/MarkerArrayPublish visualization markers to display information such as paths and obstacles in visualization tools like RViz.
/received_global_plannav_msgs/msg/PathPublishes the global path received from the global planner, i.e., the path from the current position to the target point.
/transformed_global_plannav_msgs/msg/PathPublish the globally transformed path after coordinate transformation, ensuring the path is consistent with the robot's current coordinate system.
Topicinterfacedescription
/local_costmap/clearing_endpointssensor_msgs/msg/PointCloud2Remove obstacle point cloud data from the cost map, typically used for dynamic obstacle handling.
/local_costmap/costmapnav_msgs/msg/OccupancyGridLocal cost map, representing the traversability of the environment around the robot.
/local_costmap/costmap_rawnav2_msgs/msg/CostmapUnprocessed local cost map, which may contain more detailed information.
/local_costmap/costmap_updatesmap_msgs/msg/OccupancyGridUpdateLocal costmap update information, including which areas have changed.
/local_costmap/published_footprintgeometry_msgs/msg/PolygonStampedThe published robot footprint polygon, with timestamps indicating the publication time.
/local_costmap/voxel_gridnav2_msgs/msg/VoxelGridVoxel grid data, used for spatial partitioning and optimization in cost map generation.
Topicinterfacedescription
/cmd_velgeometry_msgs/msg/TwistSpeed command sent to the low-level controller.
Topicinterfacedescription
/plan_smoothednav_msgs/msg/PathThe smoothed global path
Topicinterfacedescription
/cmd_velgeometry_msgs/msg/TwistPublish the topic for speed control commands after processing or smoothing.

Since Zhao Xuzuo rewrote the official source code into the workspace, this can be cumbersome for beginners. For beginners, it is recommended to use the official bringup node. Below is a summary of the usage method based on the official Wiki: (Here, the official bringup node is chosen instead of Teacher Zhao Xuzuo's node.)

Below is the configured nav2.launch.py file:

import os

from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration

# from launch_ros.actions import Node

def generate_launch_description():
    navigation2_dir = get_package_share_directory('nav05_navigation2')
    nav2_bringup_dir = get_package_share_directory('nav2_bringup')

    # launch的参数的优先级比yaml的参数优先级高
    use_sim_time = LaunchConfiguration('use_sim_time', default='flase')
    map_yaml_path = LaunchConfiguration('map',default=os.path.join(navigation2_dir,'map','house.yaml'))
    nav2_param_path = LaunchConfiguration('params_file',default=os.path.join(navigation2_dir,'params','nav2.yaml'))

    return LaunchDescription([
        DeclareLaunchArgument('use_sim_time',default_value=use_sim_time,description='Use simulation (Gazebo) clock if true'),
        DeclareLaunchArgument('map',default_value=map_yaml_path,description='Full path to map file to load'),
        DeclareLaunchArgument('params_file',default_value=nav2_param_path,description='Full path to param file to load'),

        IncludeLaunchDescription(
            PythonLaunchDescriptionSource([nav2_bringup_dir,'/launch','/bringup_launch.py']),
            launch_arguments={
                'map': map_yaml_path,
                'use_sim_time': use_sim_time,
                'params_file': nav2_param_path}.items(),
        ),
    ])

Here is rviz2.launch.py:

import os

from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node

def generate_launch_description():
    navigation2_dir = get_package_share_directory('nav05_navigation2')

    use_sim_time = LaunchConfiguration('use_sim_time', default='false')

    rviz_config_dir = os.path.join(navigation2_dir,'rviz','nav2.rviz')

    return LaunchDescription([
        DeclareLaunchArgument('use_sim_time',default_value=use_sim_time,description='Use simulation (Gazebo) clock if true'),

        Node(
            package='rviz2',
            executable='rviz2',
            name='rviz2',
            arguments=['-d', rviz_config_dir],
            parameters=[{'use_sim_time': use_sim_time}],
            output='screen'),
    ])

Below is the nav2.yaml configuration file (differential drive model + DWE local planner):

amcl:
  ros__parameters:
    use_sim_time: false
    alpha1: 0.2
    alpha2: 0.2
    alpha3: 0.2
    alpha4: 0.2
    alpha5: 0.2
    base_frame_id: "base_link"
    beam_skip_distance: 0.5
    beam_skip_error_threshold: 0.9
    beam_skip_threshold: 0.3
    do_beamskip: false
    global_frame_id: "map"
    lambda_short: 0.1
    laser_likelihood_max_dist: 2.0
    laser_max_range: 100.0
    laser_min_range: -1.0
    laser_model_type: "likelihood_field"
    max_beams: 60
    max_particles: 2000
    min_particles: 500
    odom_frame_id: "odom"
    pf_err: 0.05
    pf_z: 0.99
    recovery_alpha_fast: 0.0
    recovery_alpha_slow: 0.0
    resample_interval: 1
    robot_model_type: "nav2_amcl::DifferentialMotionModel"
    save_pose_rate: 0.5
    sigma_hit: 0.2
    tf_broadcast: true
    transform_tolerance: 1.0
    update_min_a: 0.2
    update_min_d: 0.25
    z_hit: 0.5
    z_max: 0.05
    z_rand: 0.5
    z_short: 0.05
    scan_topic: scan

amcl_map_client:
  ros__parameters:
    use_sim_time: false

amcl_rclcpp_node:
  ros__parameters:
    use_sim_time: false

bt_navigator:
  ros__parameters:
    use_sim_time: false
    global_frame: map
    robot_base_frame: base_link
    odom_topic: /odom
    bt_loop_duration: 10
    default_server_timeout: 20

    # 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' are use defaults:

    # nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml

    # nav2_bt_navigator/navigate_through_poses_w_replanning_and_recovery.xml

    # They can be set here or via a RewrittenYaml remap from a parent launch file to Nav2.
    plugin_lib_names:
    - nav2_compute_path_to_pose_action_bt_node
    - nav2_compute_path_through_poses_action_bt_node
    - nav2_smooth_path_action_bt_node
    - nav2_follow_path_action_bt_node
    - nav2_spin_action_bt_node
    - nav2_wait_action_bt_node
    - nav2_back_up_action_bt_node
    - nav2_drive_on_heading_bt_node
    - nav2_clear_costmap_service_bt_node
    - nav2_is_stuck_condition_bt_node
    - nav2_goal_reached_condition_bt_node
    - nav2_goal_updated_condition_bt_node
    - nav2_globally_updated_goal_condition_bt_node
    - nav2_is_path_valid_condition_bt_node
    - nav2_initial_pose_received_condition_bt_node
    - nav2_reinitialize_global_localization_service_bt_node
    - nav2_rate_controller_bt_node
    - nav2_distance_controller_bt_node
    - nav2_speed_controller_bt_node
    - nav2_truncate_path_action_bt_node
    - nav2_truncate_path_local_action_bt_node
    - nav2_goal_updater_node_bt_node
    - nav2_recovery_node_bt_node
    - nav2_pipeline_sequence_bt_node
    - nav2_round_robin_node_bt_node
    - nav2_transform_available_condition_bt_node
    - nav2_time_expired_condition_bt_node
    - nav2_path_expiring_timer_condition
    - nav2_distance_traveled_condition_bt_node
    - nav2_single_trigger_bt_node
    - nav2_is_battery_low_condition_bt_node
    - nav2_navigate_through_poses_action_bt_node
    - nav2_navigate_to_pose_action_bt_node
    - nav2_remove_passed_goals_action_bt_node
    - nav2_planner_selector_bt_node
    - nav2_controller_selector_bt_node
    - nav2_goal_checker_selector_bt_node
    - nav2_controller_cancel_bt_node
    - nav2_path_longer_on_approach_bt_node
    - nav2_wait_cancel_bt_node
    - nav2_spin_cancel_bt_node
    - nav2_back_up_cancel_bt_node
    - nav2_drive_on_heading_cancel_bt_node

bt_navigator_rclcpp_node:
  ros__parameters:
    use_sim_time: false

controller_server:
  ros__parameters:
    use_sim_time: false
    controller_frequency: 20.0
    min_x_velocity_threshold: 0.001
    min_y_velocity_threshold: 0.5
    min_theta_velocity_threshold: 0.001
    failure_tolerance: 0.3
    progress_checker_plugin: "progress_checker"
    goal_checker_plugins: ["general_goal_checker"] # "precise_goal_checker"
    controller_plugins: ["FollowPath"]

    # Progress checker parameters
    progress_checker:
      plugin: "nav2_controller::SimpleProgressChecker"
      required_movement_radius: 0.5
      movement_time_allowance: 10.0

    # Goal checker parameters
    #precise_goal_checker:

    #  plugin: "nav2_controller::SimpleGoalChecker"

    #  xy_goal_tolerance: 0.25

    #  yaw_goal_tolerance: 0.25

    #  stateful: True
    general_goal_checker:
      stateful: True
      plugin: "nav2_controller::SimpleGoalChecker"
      xy_goal_tolerance: 0.25
      yaw_goal_tolerance: 0.25

    # DWB parameters
    FollowPath:
      plugin: "dwb_core::DWBLocalPlanner"
      debug_trajectory_details: True
      min_vel_x: 0.0
      min_vel_y: 0.0
      max_vel_x: 0.26
      max_vel_y: 0.0
      max_vel_theta: 1.0
      min_speed_xy: 0.0
      max_speed_xy: 0.26
      min_speed_theta: 0.0

      # Add high threshold velocity for turtlebot 3 issue.

      # https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/75
      acc_lim_x: 2.5
      acc_lim_y: 0.0
      acc_lim_theta: 3.2
      decel_lim_x: -2.5
      decel_lim_y: 0.0
      decel_lim_theta: -3.2
      vx_samples: 20
      vy_samples: 5
      vtheta_samples: 20
      sim_time: 1.7
      linear_granularity: 0.05
      angular_granularity: 0.025
      transform_tolerance: 0.2
      xy_goal_tolerance: 0.25
      trans_stopped_velocity: 0.25
      short_circuit_trajectory_evaluation: True
      stateful: True
      critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"]
      BaseObstacle.scale: 0.02
      PathAlign.scale: 32.0
      PathAlign.forward_point_distance: 0.1
      GoalAlign.scale: 24.0
      GoalAlign.forward_point_distance: 0.1
      PathDist.scale: 32.0
      GoalDist.scale: 24.0
      RotateToGoal.scale: 32.0
      RotateToGoal.slowing_factor: 5.0
      RotateToGoal.lookahead_time: -1.0

controller_server_rclcpp_node:
  ros__parameters:
    use_sim_time: false

local_costmap:
  local_costmap:
    ros__parameters:
      update_frequency: 5.0
      publish_frequency: 2.0
      global_frame: odom
      robot_base_frame: base_link
      use_sim_time: false
      rolling_window: true
      width: 3
      height: 3
      resolution: 0.05
      robot_radius: 0.22
      plugins: ["static_layer", "obstacle_layer", "voxel_layer", "inflation_layer"]
      inflation_layer:
        plugin: "nav2_costmap_2d::InflationLayer"
        cost_scaling_factor: 3.0
        inflation_radius: 0.55
      obstacle_layer:
        plugin: "nav2_costmap_2d::ObstacleLayer"
        enabled: True
        observation_sources: scan
        scan:
          topic: /scan
          max_obstacle_height: 2.0
          clearing: True
          marking: True
          data_type: "LaserScan"
          raytrace_max_range: 3.0
          raytrace_min_range: 0.0
          obstacle_max_range: 2.5
          obstacle_min_range: 0.0
      voxel_layer:
        plugin: "nav2_costmap_2d::VoxelLayer"
        enabled: True
        publish_voxel_map: True
        origin_z: 0.0
        z_resolution: 0.05
        z_voxels: 16
        max_obstacle_height: 2.0
        mark_threshold: 0
        observation_sources: scan
        scan:
          topic: /scan
          max_obstacle_height: 2.0
          clearing: True
          marking: True
          data_type: "LaserScan"
          raytrace_max_range: 3.0
          raytrace_min_range: 0.0
          obstacle_max_range: 2.5
          obstacle_min_range: 0.0
      static_layer:
        plugin: "nav2_costmap_2d::StaticLayer"
        map_subscribe_transient_local: True
      always_send_full_costmap: True

  local_costmap_client:
    ros__parameters:
      use_sim_time: false
  local_costmap_rclcpp_node:
    ros__parameters:
      use_sim_time: false

global_costmap:
  global_costmap:
    ros__parameters:
      update_frequency: 1.0
      publish_frequency: 1.0
      global_frame: map
      robot_base_frame: base_link
      use_sim_time: false
      robot_radius: 0.22
      resolution: 0.05
      track_unknown_space: true
      plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
      obstacle_layer:
        plugin: "nav2_costmap_2d::ObstacleLayer"
        enabled: True
        observation_sources: scan
        scan:
          topic: /scan
          max_obstacle_height: 2.0
          clearing: True
          marking: True
          data_type: "LaserScan"
          raytrace_max_range: 3.0
          raytrace_min_range: 0.0
          obstacle_max_range: 2.5
          obstacle_min_range: 0.0
      static_layer:
        plugin: "nav2_costmap_2d::StaticLayer"
        map_subscribe_transient_local: True
      inflation_layer:
        plugin: "nav2_costmap_2d::InflationLayer"
        cost_scaling_factor: 3.0
        inflation_radius: 0.55
      always_send_full_costmap: True

  global_costmap_client:
    ros__parameters:
      use_sim_time: false
  global_costmap_rclcpp_node:
    ros__parameters:
      use_sim_time: false

map_server:
  ros__parameters:
    use_sim_time: false
    yaml_filename: "house.yaml"

map_saver:
  ros__parameters:
    use_sim_time: false
    save_map_timeout: 5.0
    free_thresh_default: 0.25
    occupied_thresh_default: 0.65
    map_subscribe_transient_local: True

planner_server:
  ros__parameters:
    expected_planner_frequency: 20.0
    use_sim_time: false
    planner_plugins: ["GridBased"]
    GridBased:
      plugin: "nav2_navfn_planner/NavfnPlanner"
      tolerance: 0.5
      use_astar: false
      allow_unknown: true

planner_server_rclcpp_node:
  ros__parameters:
    use_sim_time: false

smoother_server:
  ros__parameters:
    use_sim_time: false
    smoother_plugins: ["simple_smoother"]
    simple_smoother:
      plugin: "nav2_smoother::SimpleSmoother"
      tolerance: 1.0e-10
      max_its: 1000
      do_refinement: True

behavior_server:
  ros__parameters:
    costmap_topic: local_costmap/costmap_raw
    footprint_topic: local_costmap/published_footprint
    cycle_frequency: 10.0
    behavior_plugins: ["spin", "backup", "drive_on_heading", "wait"]
    spin:
      plugin: "nav2_behaviors/Spin"
    backup:
      plugin: "nav2_behaviors/BackUp"
    drive_on_heading:
      plugin: "nav2_behaviors/DriveOnHeading"
    wait:
      plugin: "nav2_behaviors/Wait"
    global_frame: odom
    robot_base_frame: base_link
    transform_tolerance: 0.1
    use_sim_time: false
    simulate_ahead_time: 2.0
    max_rotational_vel: 1.0
    min_rotational_vel: 0.4
    rotational_acc_lim: 3.2

robot_state_publisher:
  ros__parameters:
    use_sim_time: false

waypoint_follower:
  ros__parameters:
    loop_rate: 20
    stop_on_failure: false
    waypoint_task_executor_plugin: "wait_at_waypoint"
    wait_at_waypoint:
      plugin: "nav2_waypoint_follower::WaitAtWaypoint"
      enabled: True
      waypoint_pause_duration: 200

velocity_smoother:
  ros__parameters:
    use_sim_time: false
    smoothing_frequency: 20.0
    scale_velocities: False
    feedback: "OPEN_LOOP"
    max_velocity: [0.26, 0.0, 1.0]
    min_velocity: [-0.26, 0.0, -1.0]
    max_accel: [2.5, 0.0, 3.2]
    max_decel: [-2.5, 0.0, -3.2]
    odom_topic: "odom"
    odom_duration: 0.1
    deadband_velocity: [0.0, 0.0, 0.0]
    velocity_timeout: 1.0

Multi-vehicle formation

音乐页