Archive STM32 firmware to legacy/stm32/: - src/, include/, lib/USB_CDC/, platformio.ini, test stubs, flash_firmware.py - test/test_battery_adc.c, test_hw_button.c, test_pid_schedule.c, test_vesc_can.c, test_can_watchdog.c - USB_CDC_BUG.md Rename: stm32_protocol → esp32_protocol, mamba_protocol → balance_protocol, stm32_cmd_node → esp32_cmd_node, stm32_cmd_params → esp32_cmd_params, stm32_cmd.launch.py → esp32_cmd.launch.py, test_stm32_protocol → test_esp32_protocol, test_stm32_cmd_node → test_esp32_cmd_node Content cleanup across all files: - Mamba F722S → ESP32-S3 BALANCE - BlackPill → ESP32-S3 IO - STM32F722/F7xx → ESP32-S3 - stm32Mode/Version/Port → esp32Mode/Version/Port - STM32 State/Mode labels → ESP32 State/Mode - Jetson Nano → Jetson Orin Nano Super - /dev/stm32 → /dev/esp32 - stm32_bridge → esp32_bridge - STM32 HAL → ESP-IDF docs/SALTYLAB.md: - Update "Drone FC Details" to describe ESP32-S3 BALANCE board (Waveshare ESP32-S3 Touch LCD 1.28) - Replace verbose "Self-Balancing Control" STM32 section with brief note pointing to SAUL-TEE-SYSTEM-REFERENCE.md TEAM.md: Update Embedded Firmware Engineer role to ESP32-S3 / ESP-IDF No new functionality — cleanup only. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
392 lines
14 KiB
YAML
392 lines
14 KiB
YAML
# Nav2 parameters — SaltyBot (Jetson Orin Nano Super / ROS2 Humble)
|
||
#
|
||
# Robot: differential-drive self-balancing two-wheeler
|
||
# robot_radius: 0.15 m
|
||
# max_vel_x: 1.0 m/s
|
||
# max_vel_theta: 1.5 rad/s
|
||
#
|
||
# Localization: RTAB-Map (publishes /map + map→odom TF + /rtabmap/odom)
|
||
# → No AMCL, no map_server needed.
|
||
#
|
||
# Sensors:
|
||
# /scan — RPLIDAR A1M8 (obstacle layer)
|
||
# /camera/depth/color/points — RealSense D435i (voxel layer)
|
||
#
|
||
# Output: /cmd_vel (Twist) — ESP32-S3 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
|
||
min_vel_x: -0.25 # allow limited reverse
|
||
min_vel_y: 0.0
|
||
max_vel_x: 1.0
|
||
max_vel_y: 0.0
|
||
max_vel_theta: 1.5
|
||
min_speed_xy: 0.0
|
||
max_speed_xy: 1.0
|
||
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 ────────────────────────────────────────────────────────
|
||
velocity_smoother:
|
||
ros__parameters:
|
||
use_sim_time: false
|
||
smoothing_frequency: 20.0
|
||
scale_velocities: false
|
||
feedback: "OPEN_LOOP"
|
||
max_velocity: [1.0, 0.0, 1.5]
|
||
min_velocity: [-0.25, 0.0, -1.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.15
|
||
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.55 # robot_radius (0.15) + 0.40m padding
|
||
|
||
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.15
|
||
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
|
||
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
|
||
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.55
|
||
|
||
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
|