9. Technical Architecture¶
This document is a code-level architecture reference for the ROS 2 stack under src/.
It complements the higher-level overview pages by documenting:
- package boundaries and build model
- launch composition and runtime profiles
- topic contracts and command arbitration
- localization/navigation/perception/control internals
- parameter/config layering
- safety behavior and known architectural caveats
1. Architecture Scope¶
The workspace implements a modular ROS 2 system for an Ackermann JetRacer platform with these major planes:
- Hardware plane: serial motor/IMU/odom bridge + LiDAR bringup/filtering.
- State-estimation plane: EKF fusion into a canonical
/odom. - Perception plane: camera + classical vision + YOLO detections.
- Behavior plane: semantic and LiDAR emergency override logic.
- Planning plane: Nav2 + SLAM + multipoint patrol + map tooling.
- Command plane: topic-priority arbitration and command adaptation.
- Human I/O plane: teleop + voice capture/TTS/ASR helpers.
2. Package Topology¶
| Package | Build Type | Primary Role | Installed Executables |
|---|---|---|---|
jetracer_bringup |
ament_cmake |
Cross-package launch orchestration | launch-only package |
jetracer_description |
ament_cmake |
URDF/xacro, TF geometry, RViz assets, joint state animator | joint_state_publisher.py |
jetracer_gazebo |
ament_cmake |
Gazebo Harmonic simulation (Ackermann drive, camera, LiDAR, IMU) | launch-only package |
jetracer_hardware |
ament_cmake |
MCU serial bridge, LiDAR launch, scan filtering, calibration | jetracer_serial_node, laser_filter.py, calibrate_linear.py |
jetracer_localization |
ament_cmake |
EKF localization launch/config | launch-only package |
jetracer_perception |
ament_cmake |
Camera and vision nodes (classical + YOLO) | color_tracking.py, object_tracking.py, motion_detect.py, face_detect.py, yolo_detection.py |
jetracer_lane_following |
ament_cmake |
High-speed lane-following pipeline with selectable lateral control (stanley/mpc) |
lane_following_node.py (+ installed math libs) |
jetracer_navigation |
ament_cmake |
Nav2 integration, rrt_star_planner |
Provides high-level path planning (Nav2) and local obstacle avoidance (RRT*) and converts generic geometry Twist to Ackermann Twist. |
jetracer_behavior |
ament_python |
Priority safety/semantic behavioral overrides | semantic_behavior.py, collision_assurance.py, safety_supervisor.py, slip_monitor.py |
jetracer_teleop |
ament_cmake |
keyboard/joystick teleoperation | teleop_key.py, teleop_joy.py |
jetracer_voice |
ament_cmake |
VAD/ASR/TTS/assistant helpers | vad.py, tts_en.py, tts_cn.py, voice_commander.py, ginput.py, iat.py, aiui.py |
flowchart TB
B[jetracer_bringup]
D[jetracer_description]
G[jetracer_gazebo]
H[jetracer_hardware]
L[jetracer_localization]
P[jetracer_perception]
LF[jetracer_lane_following]
N[jetracer_navigation]
BE[jetracer_behavior]
T[jetracer_teleop]
V[jetracer_voice]
B --> D
B --> H
B --> L
B --> N
B --> P
B --> LF
B --> BE
B --> T
B --> V
G --> D
P --> BE
LF --> H
N --> H
L --> N
3. Build and Packaging Model¶
3.1 Build-system split¶
- Most packages are
ament_cmakeand install Python scripts viainstall(PROGRAMS ...)intolib/<package>. jetracer_behaviorisament_pythonwith setuptools console scripts.
3.2 Implications¶
- Script paths are stable for ROS launch execution (
package + executable), independent of source-tree location. - The lane-following libs are installed beside
lane_following_node.py; the node explicitly adjustssys.pathto import sibling modules at runtime. jetracer_localization/scripts/odom_pose_to_odometry.pyis intentionally not installed to avoid duplicate/odompublishers when EKF is active.
4. Launch Architecture¶
4.1 Layered launch model¶
jetracer_bringup is the orchestration layer. Package-level launches remain the component boundaries.
flowchart TD
A[jetracer_bringup/*.launch.py] --> B[jetracer.launch.py base]
A --> C[nav.launch.py full nav]
A --> D[slam.launch.py mapping]
A --> E[slam_nav.launch.py mapping + nav]
A --> F[autonomy.launch.py AI behavior stack]
B --> B1[robot_state_publisher]
B --> B2[jetracer_hardware/hardware.launch.py]
B --> B3[jetracer_localization/localization.launch.py]
B --> B4[cmd_vel_to_steering.py]
B --> B5[twist_mux]
C --> B
C --> C1[lidar.launch.py]
C --> C2[csi_camera.launch.py]
C --> C3[jetracer_navigation/nav.launch.py]
D --> B
D --> C1
D --> C2
D --> D1[jetracer_navigation/slam.launch.py]
E --> B
E --> C1
E --> C2
E --> E1[jetracer_navigation/slam_nav.launch.py]
4.2 Runtime profiles (what starts what)¶
- Base robot profile:
jetracer_bringup/jetracer.launch.py - robot description publishing
- serial hardware bridge
- EKF localization
- nav command adapter
twist_muxarbitration- Navigation profile:
jetracer_bringup/nav.launch.py - base profile + LiDAR + camera + Nav2 map-based stack
- SLAM profile:
jetracer_bringup/slam.launch.py - base profile + LiDAR + camera + selected SLAM backend
- SLAM+Nav profile:
jetracer_bringup/slam_nav.launch.py - base profile + LiDAR + camera + SLAM + Nav2 without map server/AMCL
- Autonomy AI profile:
jetracer_bringup/autonomy.launch.py - camera + lane follower + YOLO + behavior nodes + foxglove bridge
- Additional launch arguments vs. base profile:
| Argument | Default | Description |
|---|---|---|
dry_run |
false |
Suppress all physical motor commands |
safe_mode |
false |
Cap speed at safe_mode_max_speed_ms (0.20 m/s) |
debug_mode |
false |
Enable debug image publishing |
start_yolo |
true |
Skip GPU YOLO inference to save memory |
start_camera |
true |
Start CSI camera pipeline |
start_base |
true |
Start hardware + EKF + twist_mux |
start_lidar |
true |
Start RPLidar driver |
sequenceDiagram
participant U as Operator
participant BL as bringup/nav.launch.py
participant JL as jetracer.launch.py
participant HW as hardware.launch.py
participant EKF as localization.launch.py
participant NV as navigation/nav.launch.py
U->>BL: ros2 launch jetracer_bringup nav.launch.py
BL->>JL: include base stack
JL->>HW: include serial hardware node
JL->>EKF: include EKF node
JL->>JL: start cmd_vel_to_steering + twist_mux
BL->>BL: include lidar + csi camera
BL->>NV: include Nav2 stack (map/amcl/planner/controller/bt)
NV->>JL: controller output remap cmd_vel -> cmd_vel_nav
JL->>NV: adapter publishes cmd_vel_nav_steer into mux path
5. Command and Arbitration Plane¶
5.1 Contract on Twist.angular.z¶
The stack uses two command semantics:
- Nav2 internal output (
cmd_vel_nav): yaw-rate-like command from Nav2 controllers. - Actuator-facing command topics (
cmd_vel_*into mux): steering-angle-like command expected by JetRacer MCU.
cmd_vel_to_steering.py bridges these semantics for Nav2 by applying:
steering = atan(wheelbase * angular_z / linear_x) (with low-speed guard and clamp).
5.2 Arbitration path¶
flowchart
CAM --> YOLO[yolo_detection]
YOLO --> SB[semantic_behavior]
LDR --> RRT[rrt_star_planner]
LF -->|cmd_vel_lane| MUX[twist_mux]
SB -->|cmd_vel_behavior| MUX
CA[collision_assurance] -->|cmd_vel_behavior| MUX
RRT -->|cmd_vel_rrt P6| MUX
SS[safety_supervisor] -->|cmd_vel_safety P255| MUX
T[teleop_key / teleop_joy] -->|cmd_vel_teleop P10| MUX
V[color/line/object/face] -->|cmd_vel_vision P4| MUX
N[Nav2 controller_server] -->|cmd_vel_nav| S[cmd_vel_to_steering]
S -->|cmd_vel_nav_steer P3| MUX
MUX -->|cmd_vel| H[jetracer_serial_node]
twist_mux priorities (src/jetracer_bringup/config/twist_mux.yaml):
| Topic | Priority | Timeout | Publisher |
|---|---|---|---|
cmd_vel_safety |
255 | 0.2 s | safety_supervisor — hardware faults |
cmd_vel_teleop |
10 | 0.1 s | teleop keyboard / joystick |
cmd_vel_behavior |
8 | 0.3 s | semantic_behavior, collision_assurance |
cmd_vel_rrt |
6 | 0.5 s | rrt_star_planner |
cmd_vel_lane |
5 | 0.3 s | lane follower (legacy Twist) |
cmd_vel_vision |
4 | 0.5 s | classical vision trackers |
cmd_vel_nav_steer |
3 | 0.5 s | Nav2 controller (after steering adapter) |
The lane follower also publishes drive_lane (AckermannDriveStamped) as the
clean semantic control interface. The existing twist_mux chain still consumes
cmd_vel_lane for compatibility with the downstream serial bridge.
5.3 Safety characteristics in command plane¶
- All autonomy nodes default to
start=falsewhere motion is possible. - Teleop nodes publish explicit zero
Twistwhen inactive/shutdown. - Serial node applies
command_timeout_sec(default1.0) and transmits zero command when stale.
stateDiagram-v2
[*] --> Idle
Idle --> TeleopActive: cmd_vel_teleop alive
Idle --> BehaviorStop: cmd_vel_behavior alive
Idle --> LaneActive: cmd_vel_lane alive
Idle --> VisionActive: cmd_vel_vision alive
Idle --> NavActive: cmd_vel_nav_steer alive
NavActive --> BehaviorStop: behavior topic appears
NavActive --> TeleopActive: teleop topic appears
VisionActive --> LaneActive: lane topic appears
LaneActive --> BehaviorStop: behavior topic appears
LaneActive --> TeleopActive: teleop topic appears
BehaviorStop --> TeleopActive: teleop topic appears
TeleopActive --> Idle: timeout/no messages
BehaviorStop --> Idle: timeout/no messages
LaneActive --> Idle: timeout/no messages
VisionActive --> Idle: timeout/no messages
NavActive --> Idle: timeout/no messages
6. Hardware and Sensor Plane¶
6.1 jetracer_serial_node responsibilities¶
- Opens serial port (
/dev/ttyACM0default) and configures UART. - Publishes:
/imu/odom_raw/motor/lvel,/motor/rvel,/motor/lset,/motor/rset- Subscribes:
/cmd_vel- Sends three frame types to MCU:
- velocity command frame
- PID/linear/servo parameter frame
- coefficient frame
- Parses incoming frames using a byte-wise state machine and checksum guard.
- Optionally publishes
odom -> base_footprintTF (publish_odom_transform, default false).
sequenceDiagram
participant MUX as twist_mux
participant MCU as jetracer_serial_node
participant RP as RP2040 MCU
participant EKF as robot_localization
MUX->>MCU: /cmd_vel
MCU->>RP: velocity frame (0x11)
MCU->>RP: params frame (0x12) on update
MCU->>RP: coeff frame (0x13) on update
RP-->>MCU: telemetry frame (imu/odom/motor data)
MCU->>EKF: /imu + /odom_raw
MCU->>MUX: motor telemetry topics
6.2 LiDAR and filtering¶
rplidar_ros/rplidar_nodepublishes/scan(jetracer_hardware/launch/lidar.launch.py).- Optional
laser_filter.pyconverts/scanto/filteredscanvia: - distance truncation
- angle-window masking (
laser_angle).
6.3 Calibration helper¶
calibrate_linear.py:
- uses TF (
odom_frametobase_frame) to measure traveled distance - commands
/cmd_veluntil test-distance error is within tolerance - can be started/stopped dynamically via
start_test.
7. Localization and TF Plane¶
7.1 EKF fusion (robot_localization)¶
jetracer_localization/config/ekf.yaml fuses:
odom0: /odom_rawimu0: /imu
Key design choices:
two_d_mode: trueworld_frame: odom- EKF output remapped
odometry/filtered -> /odom - IMU fusion is restricted to yaw-orientation/yaw-rate channels to avoid contaminating EKF with pre-fused acceleration from MCU AHRS.
7.2 TF frame hierarchy¶
Static/kinematic frames come from URDF (jetracer.urdf.xacro) through robot_state_publisher:
base_footprintbase_link- steering/wheel joints
camera_linkimu_linklaser_linkjetson_link
Dynamic frames:
odom -> base_footprint: typically from EKF.map -> odom: from AMCL (nav mode) or SLAM backend (mapping modes).
flowchart TD
MAP[map]
ODOM[odom]
BF[base_footprint]
BLK[base_link]
CAM[camera_link]
IMU[imu_link]
LASER[laser_link]
JET[jetson_link]
FL[front_left_wheel_link]
FR[front_right_wheel_link]
RL[rear_left_wheel_link]
RR[rear_right_wheel_link]
MAP --> ODOM
ODOM --> BF
BF --> BLK
BLK --> CAM
BLK --> IMU
BLK --> LASER
BLK --> JET
BLK --> FL
BLK --> FR
BLK --> RL
BLK --> RR
8. Perception and Behavior Plane¶
8.1 Camera ingest¶
jetracer_perception/launch/csi_camera.launch.py starts gscam_node:
- namespace default:
csi_cam_0 - topic path includes
image_raw(+ camera info) - Jetson-oriented GStreamer pipeline defaults via
nvarguscamerasrc. - launch arguments expose capture width, height, FPS, flip method, and calibration file URL.
8.2 Lane-following pipeline (High-Definition Autonomy)¶
lane_following_node.py executes per camera frame using a MultiThreadedExecutor to isolate high-intensity CV tasks from odometry parsing:
- compressed image decode
- optional frame rectification from
CameraInfo - resize to 320x240 (High-Definition space)
- Perspective Warp: transform to Bird's-Eye View (BEV)
- Luminance Thresholding: isolate white tape on black ground
- Sliding Window Polynomial Fit: fit 2nd-degree parabolas to detected pixels
- keep the solved centerline in BEV and convert it to a pseudo-metric vehicle frame
- project the same centerline back to camera space only for debug overlays
- selectable lateral control (
stanleyormpc) + PID longitudinal control - publish
drive_lane, legacycmd_vel_lane, metric waypoints, and debug image
Inputs:
- image: configurable compressed topic (default
csi_cam_0/image_raw/compressed) - calibration:
csi_cam_0/camera_info - odometry:
/odom
Outputs:
drive_lane(AckermannDriveStamped) — primary Ackermann commandcmd_vel_lane(Twist) — legacy compatibility fortwist_muxlane_following/waypoints(Float32MultiArray) — vehicle-frame waypoints (m)lane_following/waypoints_camera(Float32MultiArray) — camera-space overlay pointslane_following/path(nav_msgs/Path) — planned centerline for RViz2lane_following/markers(visualization_msgs/MarkerArray) — lookahead sphere, steering arrow, status cubelane_following/status(String) —GOOD/WEAK/LOSTlane_following/confidence(Float32) — tracking confidence 0–1lane_following/control_fps(Float32) — control loop ratelane_following/frame_age_sec(Float32) — camera frame latencylane_following/debug_image/compressed(CompressedImage) — gated atdebug_fps_limitHz
Key parameters:
| Parameter | Default | Description |
|---|---|---|
start |
false |
Safety gate — must be set true to move |
lateral_controller_type |
stanley |
stanley or mpc |
max_speed_ms |
0.35 |
Maximum forward speed (m/s) |
enable_markers |
true |
Publish RViz2 markers (disable on Jetson to save CPU) |
debug_fps_limit |
5.0 |
Max Hz for debug image (bandwidth control) |
max_frame_age_sec |
0.20 |
Skip control if camera frame is older than this |
flowchart LR
IMG[csi_cam_0/image_raw/compressed] --> DEC[cv_bridge decode]
INFO[csi_cam_0/camera_info] --> RECT[Undistort/Rectify]
DEC --> RECT
RECT --> RSZ[Resize 320x240]
RSZ --> WARP[Perspective Warp]
WARP --> LUM[Luminance Threshold]
LUM --> HIST[Histogram Sliding Window]
HIST --> POLY[Parabolic Fit]
POLY --> BEV[Centerline in BEV]
BEV --> VF[Vehicle-frame waypoints]
BEV --> UNWARP[Inverse Perspective Warp]
UNWARP --> CAMWP[Camera-space waypoints]
VF --> CURV[TargetSpeedFromCurvature]
VF --> ST[Lateral Controller Selector]
ODOM["/odom"] --> PID[PID Longitudinal]
CURV --> PID
ST --> ACK[drive_lane]
PID --> ACK
ST --> TW[cmd_vel_lane legacy]
PID --> TW
VF --> DBG1[lane_following/waypoints]
CAMWP --> DBG2[lane_following/waypoints_camera]
CAMWP --> DBG3[lane_following/debug_image/compressed]
flowchart TD
CFG[lateral_controller_type param]
WP2[Vehicle-frame waypoints]
ODO2["/odom speed"]
CFG --> SEL{stanley or mpc}
WP2 --> SEL
ODO2 --> SEL
SEL -->|stanley| STAN[Stanley law\nlookahead + smoothing]
SEL -->|mpc| MPC[Linear MPC horizon solve\nQ/R tuning]
STAN --> OUT[steering angle in rad]
MPC --> OUT
OUT --> ACK2[drive.steering_angle]
OUT --> TW2[legacy Twist angular.z = steering angle]
8.3 Classical vision behaviors¶
Nodes publishing cmd_vel_vision:
color_tracking.pyobject_tracking.pyface_detect.py
Shared pattern:
- compressed image subscribe (queue depth 1)
- start-gated motion output
- annotated debug image publish
Additional outputs:
face_detect/count(int)motion_detect/statusfrommotion_detect.py(non-driving detector).
8.4 YOLO semantic detection¶
yolo_detection.py:
- loads Ultralytics model from parameterized
model_path - attempts configured device (default
cuda:0) with CPU fallback on failures - publishes:
perception/yolo_detections(vision_msgs/Detection2DArray)- optional
perception/yolo_debug/compressed
8.5 Behavior override nodes¶
semantic_behavior.py:
- subscribes YOLO detections
- triggers stop if configured class (default
stop sign) exceeds image-area threshold - runs timed stop + cooldown state machine
- publishes zero commands on
cmd_vel_behaviorwhile active
collision_assurance.py:
- subscribes
/scan - checks forward cone for obstacle under distance threshold
- publishes repeated zero commands on
cmd_vel_behaviorwhile blocked
Both are priority-8 sources into twist_mux.
flowchart TD
YOLO[perception/yolo_detections] --> SB[semantic_behavior]
SCAN["/scan"] --> CA[collision_assurance]
SB -->|zero Twist when trigger active| BEHAV[cmd_vel_behavior]
CA -->|zero Twist when blocked| BEHAV
BEHAV --> MUX[twist_mux priority 8]
MUX --> CMD["/cmd_vel"]
9. Navigation, SLAM, and Mission Plane¶
9.1 Nav2 (map-based mode)¶
jetracer_navigation/launch/nav.launch.py starts:
map_serveramclplanner_servercontroller_server(remappedcmd_vel -> cmd_vel_nav)behavior_serverbt_navigator- lifecycle manager
- optional
multipoint_nav.py
9.2 Planner/controller architecture¶
From nav2_params.yaml:
- Planner:
SmacPlannerHybridwithmotion_model_for_search: ACKERMANN. - Controller:
RegulatedPurePursuitController. - AMCL model forced to
OmniMotionModelas practical approximation for car-like platform. - Footprint set to chassis-like polygon for both local/global costmaps.
9.3 SLAM backends¶
slam.launch.py supports:
slam_toolbox(managed lifecycle node + lifecycle manager)cartographer_ros(node + occupancy grid node usingconfig/cartographer/jetracer.lua)
slam_nav.launch.py composes SLAM + Nav2 controller/planner/bt stack for online mapping navigation.
9.4 Multipoint mission runner¶
multipoint_nav.py:
- consumes RViz
/clicked_point - drives sequential
NavigateToPosegoals via action client - supports looping and single retry of failed goals
- publishes indexed marker labels for waypoint visualization
- resets patrol list on
initialposeupdates (optional forwarding to alternate topic).
flowchart LR
CP[clicked_point] --> MP[multipoint_nav.py]
IP[initialpose] --> MP
MP --> MK[path_point MarkerArray]
MP --> ACT[Nav2 NavigateToPose Action]
ACT --> CTRL[controller_server]
CTRL -->|cmd_vel_nav| ADP[cmd_vel_to_steering]
ADP -->|cmd_vel_nav_steer| MUX[twist_mux]
MUX --> CMD["/cmd_vel"]
flowchart TD
SL[slam.launch.py]
ST[slam_toolbox async node]
LM[lifecycle_manager_slam]
CT[cartographer_node]
CG[cartographer_occupancy_grid_node]
SL -->|slam_backend=slam_toolbox| ST
SL -->|slam_backend=slam_toolbox| LM
SL -->|slam_backend=cartographer| CT
SL -->|slam_backend=cartographer| CG
10. Voice and Human I/O Plane¶
10.1 Teleop¶
teleop_joy.py: deadman-button gated joystick tocmd_vel_teleop, timer-based publish loop.teleop_key.py: terminal keyboard control tocmd_vel_teleop, with speed scaling bindings.
10.2 Voice stack¶
vad.py is a mode-driven orchestrator:
- records VAD-gated utterances to runtime files
- routes to helper scripts (
ginput.py,iat.py,aiui.py) based on mode - publishes recognized text on
chatter - optionally plays assistant/TTS audio outputs
TTS subscribers:
tts_en.pysubscribesspeak(gTTS)tts_cn.pysubscribesspeak(XFYun WebSocket API)
Offline command navigation:
voice_commander.pyuses Vosk+PyAudio and publishes/goal_posewhen keywords are matched.
Credential strategy:
- API secrets are parameterized (not hardcoded).
- Runtime artifacts are written under
~/.ros/jetracer_voiceby default.
stateDiagram-v2
[*] --> Record
Record --> Play: mode=play
Record --> ASR_EN: mode=asr_en
Record --> TALK_EN: mode=talk_en
Record --> ASR_CN: mode=asr_cn
Record --> TALK_CN: mode=talk_cn
ASR_EN --> PublishText
TALK_EN --> PublishText
TALK_EN --> PlayAssistantAudio
ASR_CN --> PublishText
TALK_CN --> PublishText
TALK_CN --> PlayTTS_CN
Play --> Record
PublishText --> Record
PlayAssistantAudio --> Record
PlayTTS_CN --> Record
11. Topic Contract Reference (Key Runtime Interfaces)¶
| Topic | Type | Producer(s) | Consumer(s) | Notes |
|---|---|---|---|---|
/cmd_vel |
geometry_msgs/Twist |
twist_mux |
jetracer_serial_node |
final actuator command |
cmd_vel_safety |
Twist |
safety_supervisor |
twist_mux |
priority 255 halt |
cmd_vel_teleop |
Twist |
teleop nodes | twist_mux |
highest manual priority |
cmd_vel_behavior |
Twist |
behavior nodes | twist_mux |
emergency semantic/LiDAR stops |
drive_lane |
AckermannDriveStamped |
lane follower | optional direct consumers | primary lane-control command |
cmd_vel_lane |
Twist |
lane follower | twist_mux |
legacy steering-angle compatibility |
cmd_vel_vision |
Twist |
classical vision trackers | twist_mux |
perception-driven tracking |
cmd_vel_nav |
Twist |
Nav2 controller_server | cmd_vel_to_steering |
pre-adaptation Nav2 command |
cmd_vel_nav_steer |
Twist |
cmd_vel_to_steering |
twist_mux |
adapted Nav2 command |
/imu |
sensor_msgs/Imu |
serial node | EKF, safety_supervisor | MCU-origin IMU |
/imu/data |
sensor_msgs/Imu |
simulation bridge | EKF, safety_supervisor | sim IMU |
/odom_raw |
nav_msgs/Odometry |
serial node | EKF | raw odom |
/odom |
nav_msgs/Odometry |
EKF | Nav2 + lane follower | canonical odometry |
/scan |
sensor_msgs/LaserScan |
rplidar_node or sim bridge |
Nav2, collision assurance | primary LiDAR feed |
/filteredscan |
LaserScan |
laser_filter | optional consumers | only when filter launch is used |
csi_cam_0/image_raw/compressed |
sensor_msgs/CompressedImage |
camera pipeline or sim | perception/lane nodes | default camera stream |
csi_cam_0/camera_info |
sensor_msgs/CameraInfo |
camera pipeline or sim | lane follower | calibration data |
lane_following/path |
nav_msgs/Path |
lane follower | RViz2 | planned centerline in base_link |
lane_following/markers |
visualization_msgs/MarkerArray |
lane follower | RViz2 | lookahead pt, steering arrow, status cube |
lane_following/status |
std_msgs/String |
lane follower | debug tools | GOOD/WEAK/LOST |
lane_following/confidence |
std_msgs/Float32 |
lane follower | debug tools | 0–1 tracking confidence |
lane_following/control_fps |
std_msgs/Float32 |
lane follower | debug tools | control loop rate |
lane_following/frame_age_sec |
std_msgs/Float32 |
lane follower | debug tools | camera frame latency |
lane_following/waypoints |
Float32MultiArray |
lane follower | debug/tuning tools | vehicle-frame waypoints (m) |
lane_following/waypoints_camera |
Float32MultiArray |
lane follower | debug/tuning tools | camera-space overlay points |
perception/yolo_detections |
vision_msgs/Detection2DArray |
YOLO node | semantic behavior | semantic signal |
battery_state |
sensor_msgs/BatteryState |
serial node | safety_supervisor | battery voltage |
/control/slipping |
std_msgs/Bool |
slip_monitor | safety_supervisor | wheel slip flag |
/control/collision_blocked |
std_msgs/Bool |
collision_assurance | safety_supervisor | LiDAR blocked flag |
/diagnostics |
diagnostic_msgs/DiagnosticArray |
serial node, thermal_monitor | safety_supervisor | system health |
/goal_pose |
geometry_msgs/PoseStamped |
voice commander / RViz | Nav2 BT navigator | goal API topic |
clicked_point |
geometry_msgs/PointStamped |
RViz tool | multipoint_nav | mission waypoint input |
12. Parameter and Configuration Layering¶
12.1 Central config¶
src/jetracer_bringup/config/main_config.yaml provides shared runtime tuning for:
- lane following
- YOLO detection
- semantic behavior
- collision assurance
- nav command adapter
- foxglove bridge
- multipoint navigation
- voice commander (parameter block available)
12.2 Domain configs¶
- hardware:
jetracer_hardware/config/hardware.yaml - EKF:
jetracer_localization/config/ekf.yaml - Nav2:
jetracer_navigation/config/nav2_params.yaml - multipoint nav:
jetracer_navigation/config/multipoint_nav.yaml - SLAM toolbox:
jetracer_navigation/config/slam_toolbox_online.yaml - camera calibration:
jetracer_perception/config/cam_640x480.yaml
12.3 Effective precedence¶
- node-declared defaults in code
- YAML passed via launch
- explicit launch-argument parameter overrides
- runtime dynamic parameter updates (where callbacks are implemented)
13. Safety and Fault-Tolerance Summary¶
13.1 safety_supervisor (priority 255)¶
jetracer_behavior/jetracer_behavior/safety_supervisor.py sits at the top of
the twist_mux hierarchy (priority 255, timeout 0.2 s) and publishes halt
commands on cmd_vel_safety when any fault is active.
Fault sources monitored:
| Fault key | Input topic | Type | Trigger |
|---|---|---|---|
battery_low |
battery_state |
sensor_msgs/BatteryState |
voltage < min_voltage (9.5 V) |
thermal_critical |
/diagnostics |
diagnostic_msgs/DiagnosticArray |
any zone > max_temp (82 °C) |
hardware_failed |
/diagnostics |
DiagnosticArray |
serial node diagnostic ERROR |
slipping |
/control/slipping |
std_msgs/Bool |
wheel slip detected |
collision_blocked |
/control/collision_blocked |
Bool |
LiDAR forward cone blocked |
Fail-safe watchdog: if any monitored input goes silent for input_timeout_sec
(default 2.0 s), the corresponding fault activates automatically. This prevents
silent feed failures from leaving the vehicle unprotected.
Teleop awareness: when a human is on the joystick (cmd_vel_teleop active),
battery and thermal soft-faults are suppressed so the operator retains authority.
Hard faults (hardware failure, collision) override even teleop.
Clean shutdown: publishes three zero-velocity frames before node exit.
13.2 thermal_monitor¶
jetracer_hardware/scripts/thermal_monitor.py enumerates all Linux thermal
zones via sysfs (/sys/class/thermal/thermal_zone*) and reports GPU, PLL, AO,
and CPU temperatures to /diagnostics. Also publishes CPU usage from /proc/stat.
13.3 slip_monitor¶
jetracer_behavior/jetracer_behavior/slip_monitor.py compares commanded velocity
with odometry-reported velocity to detect wheel slip. Publishes /control/slipping.
13.4 Hardware driver safety¶
- Dry-run mode (
dry_run:=true): suppresses all physical serial writes. - Acceleration ramp (
max_accel_mps2): prevents sudden velocity jumps. - Startup guardrail (
startup_speed_limit_ms): caps speed for 30 s after boot. - Serial reconnect: automatically re-opens the port after USB disconnect.
- Command timeout: serial node zeroes commands after
command_timeout_secsilence.
13.5 Summary¶
- Motion gating:
start=falseby default for all autonomous nodes. - Priority override: behavior/safety nodes can forcibly halt at higher mux priority.
- Explicit stop-on-exit: driving nodes publish zero command in
finallyblocks. - Lifecycle enforcement: SLAM managed node is explicitly lifecycle-managed.
14. Known Architectural Caveats¶
autonomy.launch.pystarts AI perception/behavior nodes but does not include base hardware/mux stack by default whenstart_base:=false; it is typically used as an overlay profile on top ofjetracer.launch.py.laser_filter.pypublishesfilteredscan, while default Nav2 and behavior wiring consumescan; filtered data is opt-in and requires explicit remap/use.odom_pose_to_odometry.pyexists for compatibility, but is intentionally not installed in ROS 2 packaging to avoid duplicate/odompublishers when EKF is active.main_config.yamlcontainsvoice_commanderparameters, but no top-level bringup launch currently instantiatesvoice_commander.py.- In
jetracer_serial_node, updatingsend_period_secvia dynamic parameters updates the variable but does not recreate the already-created send timer. jetracer_gazebosimulation never startsjetracer_hardwareto prevent accidental real motor commands when simulation and hardware share aROS_DOMAIN_ID.enable_markers:=falseshould be set inmain_config.yamlfor Jetson Nano deployments to avoid CPU overhead from RViz2 marker publishing during autonomous driving.
15. Extension Guidelines¶
- Add new command-producing autonomy nodes by publishing a dedicated
cmd_vel_*topic and adding it totwist_mux.yamlwith explicit priority/timeout. - Keep Nav2 output semantics isolated behind
cmd_vel_to_steeringso actuator path remains physically meaningful. - Preserve single-source ownership of
/odomandodom->base_footprintto avoid estimator/TF conflicts. - For new perception modules, follow existing compressed-image I/O pattern and low queue depths to reduce latency.
- For new cloud/voice integrations, continue parameterizing credentials and writing runtime artifacts outside source tree.
[!TIP] Use this page as the implementation contract when adding new stack features. If a node changes topic semantics or priority behavior, update this document in the same PR.