sl-controls 285178b2f9 feat: Configure Nav2 navigation stack conservative speeds (Issue #475)
- Update max_vel_x to 0.3 m/s (conservative for FC + hoverboard ESC)
- Update max_vel_theta to 0.5 rad/s (conservative for FC + hoverboard ESC)
- Set robot_radius to 0.22 m for 0.4m x 0.4m footprint
- Configure velocity smoother with conservative limits
- Both DWB local planner and velocity smoother updated for consistency
- RPLIDAR (/scan) + depth_to_laserscan (/depth_scan) costmap layers enabled
- NavFn global planner, DWB local planner configured

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-05 14:50:41 -05:00

415 lines
16 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Nav2 parameters — SaltyBot (Jetson Orin Nano Super / ROS2 Humble)
#
# Robot: differential-drive self-balancing two-wheeler
# robot_radius: 0.22 m (0.4m x 0.4m footprint)
# footprint: 0.4 x 0.4 m
# max_vel_x: 0.3 m/s (conservative for FC + hoverboard ESC, Issue #475)
# max_vel_theta: 0.5 rad/s (conservative for FC + hoverboard ESC, Issue #475)
#
# Localization: RTAB-Map (publishes /map + map→odom TF + /rtabmap/odom)
# → No AMCL, no map_server needed.
#
# Sensors (Issue #478 — Costmap configuration):
# /scan — RPLIDAR A1M8 360° LaserScan (obstacle layer)
# /depth_scan — RealSense D435i depth_to_laserscan (obstacle layer)
# /camera/depth/color/points — RealSense D435i PointCloud2 (voxel layer, local only)
#
# Inflation:
# inflation_radius: 0.3m (robot_radius 0.15m + 0.15m padding)
#
# Output: /cmd_vel (Twist) — STM32 bridge consumes this topic.
bt_navigator:
ros__parameters:
use_sim_time: false
global_frame: map
robot_base_frame: base_link
odom_topic: /rtabmap/odom
bt_loop_duration: 10
default_server_timeout: 20
wait_for_service_timeout: 1000
action_server_result_timeout: 900.0
navigators: ['navigate_to_pose', 'navigate_through_poses']
navigate_to_pose:
plugin: "nav2_bt_navigator/NavigateToPoseNavigator"
navigate_through_poses:
plugin: "nav2_bt_navigator/NavigateThroughPosesNavigator"
# default_bt_xml_filename is set at launch time via nav2.launch.py
error_code_names:
- compute_path_error_code
- follow_path_error_code
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_assisted_teleop_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_goal_updated_controller_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_assisted_teleop_cancel_bt_node
- nav2_drive_on_heading_cancel_bt_node
bt_navigator_navigate_through_poses_rclcpp_node:
ros__parameters:
use_sim_time: false
bt_navigator_navigate_to_pose_rclcpp_node:
ros__parameters:
use_sim_time: false
# ── Controller Server (DWB — local trajectory follower) ─────────────────────
controller_server:
ros__parameters:
use_sim_time: false
controller_frequency: 10.0 # Hz — Orin can handle 10Hz easily
min_x_velocity_threshold: 0.001
min_y_velocity_threshold: 0.5 # not holonomic — effectively disabled
min_theta_velocity_threshold: 0.001
failure_tolerance: 0.3
progress_checker_plugin: "progress_checker"
goal_checker_plugins: ["general_goal_checker"]
controller_plugins: ["FollowPath"]
progress_checker:
plugin: "nav2_controller::SimpleProgressChecker"
required_movement_radius: 0.2 # Issue #479: Minimum 20cm movement to detect progress
movement_time_allowance: 10.0 # 10 second window to detect movement, then attempt recovery
general_goal_checker:
stateful: true
plugin: "nav2_controller::SimpleGoalChecker"
xy_goal_tolerance: 0.25
yaw_goal_tolerance: 0.25
FollowPath:
plugin: "dwb_core::DWBLocalPlanner"
debug_trajectory_details: false
# Velocity limits (conservative for FC + hoverboard ESC, Issue #475)
min_vel_x: -0.15 # allow limited reverse (half of max forward)
min_vel_y: 0.0
max_vel_x: 0.3 # conservative: 0.3 m/s linear
max_vel_y: 0.0
max_vel_theta: 0.5 # conservative: 0.5 rad/s angular
min_speed_xy: 0.0
max_speed_xy: 0.3 # match max_vel_x
min_speed_theta: 0.0
# Acceleration limits (differential drive)
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
# Trajectory sampling
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
# ── Planner Server (NavFn global path planner) ───────────────────────────────
planner_server:
ros__parameters:
use_sim_time: false
expected_planner_frequency: 20.0
planner_plugins: ["GridBased"]
GridBased:
plugin: "nav2_navfn_planner/NavfnPlanner"
tolerance: 0.5
use_astar: false
allow_unknown: true
# ── Smoother Server ──────────────────────────────────────────────────────────
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 (recovery behaviors) ────────────────────────────────────
# Issue #479: Recovery behaviors with conservative speed/distance limits for FC+Hoverboard
# ESC priority: E-stop (Issue #459) takes priority over any recovery behavior.
behavior_server:
ros__parameters:
use_sim_time: false
local_costmap_topic: local_costmap/costmap_raw
global_costmap_topic: global_costmap/costmap_raw
local_footprint_topic: local_costmap/published_footprint
global_footprint_topic: global_costmap/published_footprint
cycle_frequency: 10.0
behavior_plugins: ["spin", "backup", "wait"]
# Spin recovery: In-place 90° rotation to escape local deadlock
spin:
plugin: "nav2_behaviors/Spin"
spin_dist: 1.5708 # 90 degrees (π/2 radians)
max_rotational_vel: 0.5 # Conservative: 0.5 rad/s for self-balancer stability
min_rotational_vel: 0.25 # Minimum angular velocity
rotational_acc_lim: 1.6 # Half of max (3.2) for smooth accel on self-balancer
time_allowance: 10.0 # max 10 seconds to complete 90° rotation
# Backup recovery: Reverse 0.3m at 0.1 m/s to escape obstacles
# Conservative for FC (Flux Capacitor) + Hoverboard ESC drivetrain
backup:
plugin: "nav2_behaviors/BackUp"
backup_dist: 0.3 # Reverse 0.3 meters (safe distance for deadlock escape)
backup_speed: 0.1 # Very conservative: 0.1 m/s reverse
max_backup_vel: 0.15 # Absolute max reverse velocity
min_backup_vel: 0.05 # Minimum backup velocity threshold
time_allowance: 5.0 # max 5 seconds (0.3m at 0.1m/s = 3s normal)
# Wait recovery: Pause 5 seconds to let obstacles move away
wait:
plugin: "nav2_behaviors/Wait"
wait_duration: 5.0 # 5 second pause
local_frame: odom
global_frame: map
robot_base_frame: base_link
transform_tolerance: 0.1
simulate_ahead_time: 2.0
max_rotational_vel: 0.5 # Conservative: self-balancer stability limit
min_rotational_vel: 0.25
rotational_acc_lim: 1.6 # Half of main limit for recovery behaviors
# ── Waypoint Follower ────────────────────────────────────────────────────────
waypoint_follower:
ros__parameters:
use_sim_time: false
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 ────────────────────────────────────────────────────────
# Conservative speeds for FC + hoverboard ESC (Issue #475)
velocity_smoother:
ros__parameters:
use_sim_time: false
smoothing_frequency: 20.0
scale_velocities: false
feedback: "OPEN_LOOP"
max_velocity: [0.3, 0.0, 0.5] # conservative: 0.3 m/s linear, 0.5 rad/s angular
min_velocity: [-0.15, 0.0, -0.5]
max_accel: [2.5, 0.0, 3.2]
max_decel: [-2.5, 0.0, -3.2]
odom_topic: /rtabmap/odom
odom_duration: 0.1
deadband_velocity: [0.0, 0.0, 0.0]
velocity_timeout: 1.0
# ── Local Costmap ────────────────────────────────────────────────────────────
local_costmap:
local_costmap:
ros__parameters:
use_sim_time: false
update_frequency: 10.0 # Hz — Orin can sustain 10Hz
publish_frequency: 5.0
global_frame: odom
robot_base_frame: base_link
rolling_window: true
width: 3
height: 3
resolution: 0.05
robot_radius: 0.22
# Footprint: [x, y] in base_link frame, in counterclockwise order
# Robot footprint ~0.4m x 0.4m, with 2m lookahead buffer for controller stability
footprint: "[[-0.2, -0.2], [-0.2, 0.2], [0.2, 0.2], [0.2, -0.2]]"
plugins: ["obstacle_layer", "voxel_layer", "inflation_layer"]
obstacle_layer:
plugin: "nav2_costmap_2d::ObstacleLayer"
enabled: true
observation_sources: scan surround_cameras
scan:
topic: /scan
max_obstacle_height: 0.80
clearing: true
marking: true
data_type: "LaserScan"
raytrace_max_range: 8.0
raytrace_min_range: 0.0
obstacle_max_range: 7.5
obstacle_min_range: 0.0
# 4× IMX219 camera-detected obstacles (chairs, glass, people)
surround_cameras:
topic: /surround_vision/obstacles
min_obstacle_height: 0.05
max_obstacle_height: 1.50
clearing: false
marking: true
data_type: "PointCloud2"
raytrace_max_range: 3.5
raytrace_min_range: 0.0
obstacle_max_range: 3.0
obstacle_min_range: 0.0
voxel_layer:
plugin: "nav2_costmap_2d::VoxelLayer"
enabled: true
publish_voxel_map: false
origin_z: 0.0
z_resolution: 0.05
z_voxels: 16
max_obstacle_height: 0.80
mark_threshold: 0
observation_sources: depth_camera
depth_camera:
topic: /camera/depth/color/points
min_obstacle_height: 0.05 # above floor level
max_obstacle_height: 0.80
marking: true
clearing: true
data_type: "PointCloud2"
raytrace_max_range: 4.0
raytrace_min_range: 0.0
obstacle_max_range: 3.5
obstacle_min_range: 0.0
inflation_layer:
plugin: "nav2_costmap_2d::InflationLayer"
cost_scaling_factor: 3.0
inflation_radius: 0.3 # robot_radius (0.15) + 0.15m padding for safety
always_send_full_costmap: false
# ── Global Costmap ───────────────────────────────────────────────────────────
global_costmap:
global_costmap:
ros__parameters:
use_sim_time: false
update_frequency: 5.0
publish_frequency: 1.0
global_frame: map
robot_base_frame: base_link
robot_radius: 0.22
# Footprint: [x, y] in base_link frame, in counterclockwise order
footprint: "[[-0.2, -0.2], [-0.2, 0.2], [0.2, 0.2], [0.2, -0.2]]"
resolution: 0.05
track_unknown_space: true
plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
static_layer:
plugin: "nav2_costmap_2d::StaticLayer"
map_subscribe_transient_local: true # for RTAB-Map's transient-local /map
obstacle_layer:
plugin: "nav2_costmap_2d::ObstacleLayer"
enabled: true
# Issue #478: RPLIDAR + RealSense depth_scan
observation_sources: scan depth_scan surround_cameras
scan:
topic: /scan
max_obstacle_height: 0.80
clearing: true
marking: true
data_type: "LaserScan"
raytrace_max_range: 8.0
raytrace_min_range: 0.0
obstacle_max_range: 7.5
obstacle_min_range: 0.0
# RealSense D435i depth_to_laserscan (synthetic LaserScan from depth)
depth_scan:
topic: /depth_scan
max_obstacle_height: 0.80
clearing: true
marking: true
data_type: "LaserScan"
raytrace_max_range: 6.0
raytrace_min_range: 0.1
obstacle_max_range: 5.5
obstacle_min_range: 0.0
surround_cameras:
topic: /surround_vision/obstacles
min_obstacle_height: 0.05
max_obstacle_height: 1.50
clearing: false
marking: true
data_type: "PointCloud2"
raytrace_max_range: 3.5
raytrace_min_range: 0.0
obstacle_max_range: 3.0
obstacle_min_range: 0.0
inflation_layer:
plugin: "nav2_costmap_2d::InflationLayer"
cost_scaling_factor: 3.0
inflation_radius: 0.3 # robot_radius (0.15) + 0.15m padding for safety
always_send_full_costmap: false
# ── Map Server / Map Saver (not used — RTAB-Map provides /map) ───────────────
map_server:
ros__parameters:
use_sim_time: false
yaml_filename: ""
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