AutonomousGR is a ROS 2 Humble autonomy stack for an F1TENTH-style vehicle using a ZED 2i stereo camera. It detects track cones, estimates their 3D positions, builds a centerline, avoids depth obstacles, follows the safe path with pure pursuit, and supervises the final Ackermann command.
The stack targets:
- Jetson Orin Nano, CUDA, TensorRT, and ZED 2i on the vehicle
- NVIDIA Isaac Sim 6.0.1 on Ubuntu 22.04 with an RTX GPU
- ROS 2 Humble and
ackermann_msgs/AckermannDriveStamped
Left image -> TensorRT cone detector -> depth localization -> left/right cones
|
v
centerline /path/nominal
|
Registered depth -> inflated local obstacle grid -----------+
v
obstacle avoidance -> /path
|
v
pure pursuit -> /drive_candidate
|
Obstacle grid + freshness + braking envelope ---------------------------+
v
drive safety -> /drive
Core nodes:
cone_detector_node: TensorRT/CUDA cone detection.cone_localizer_node: registered-depth projection intobase_link.path_planner_node.py: Delaunay/greedy cone pairing and spline-smoothed nominal centerline.depth_obstacle_node: ground/hood filtering, point projection, grid inflation, and/obstacles/local_grid.obstacle_avoidance_node.py: laterally shifted paths constrained by obstacles and cone boundaries.pure_pursuit_node: steering and speed generation on/drive_candidate.drive_safety_node: sole/drivepublisher; stops or limits speed using obstacle clearance and data freshness.stereo_depth_node: optional OpenCV SGBM depth generation for Isaac stereo qualification.
The externally important contracts remain:
/path nav_msgs/Path in base_link
/drive ackermann_msgs/AckermannDriveStamped
cone_nav/ ROS 2 package, nodes, launch files, config, RViz, URDF
isaac_sim/ Isaac scene, container launcher, and topic checks
Python Detection Code/ dataset conversion, training, and webcam utilities
fsoco.yaml YOLO dataset configuration
URCA Documents/ project documents
Install normal ROS dependencies, then provide system-level CUDA, TensorRT, the ZED ROS 2 wrapper, and optional ORB-SLAM3:
source /opt/ros/humble/setup.bash
rosdep install --from-paths . --ignore-src -r -yThe trained YOLO26 checkpoint is included at
models/fsoco_yolo26n/weights/best.pt. Export it on the target NVIDIA GPU:
python3 -m venv .venv-export --system-site-packages
source .venv-export/bin/activate
python -m pip install --upgrade pip ultralytics onnx onnxslim
yolo export \
model=models/fsoco_yolo26n/weights/best.pt \
format=engine imgsz=640 batch=1 dynamic=False \
quantize=32 nms=False device=0 workspace=4
cp models/fsoco_yolo26n/weights/best.engine \
cone_nav/models/cone_yolo.engineThe final engine belongs at:
cone_nav/models/cone_yolo.engine
The Isaac configuration expects the YOLO26 nms=False FP32 output
[N,6] or [1,N,6] containing x1, y1, x2, y2, confidence, class.
Legacy cx, cy, width, height engines remain selectable with
trt_bbox_format:=cxcywh. Classes are 0=blue, 1=yellow, and
2=orange/big cone. TensorRT engines are GPU- and TensorRT-version-specific;
build the production engine on the target platform.
colcon build --packages-select cone_nav --cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo
source install/setup.bashros2 launch cone_nav real.launch.pyThis starts the ZED wrapper, cone pipeline, obstacle avoidance, safety supervisor, RViz, and ORB-SLAM3. Disable ORB-SLAM when it is not installed or calibrated:
ros2 launch cone_nav real.launch.py enable_orbslam:=falseThe supplied ORB-SLAM YAML is only a starting point. Replace its intrinsics, stereo baseline, IMU noise values, and body-camera transform before trusting its output. The reactive planner itself does not require or consume ORB-SLAM odometry.
ZED wrapper topic names vary by release. Defaults use the repository's original names, while launch arguments support newer naming:
ros2 launch cone_nav real.launch.py \
left_image_topic:=/zed2i/zed_node/left/color/rect/image \
right_image_topic:=/zed2i/zed_node/right/color/rect/image \
camera_info_topic:=/zed2i/zed_node/left/color/rect/camera_infoConfirm the installed wrapper's topics with ros2 topic list. Registered depth defaults to /zed2i/zed_node/depth/depth_registered.
Useful modes:
# Compute avoidance but publish the nominal path; drive safety remains active.
ros2 launch cone_nav real.launch.py avoidance_shadow_mode:=true
# Controlled regression bypass of avoidance and grid command checks.
ros2 launch cone_nav real.launch.py avoidance_enabled:=falseThe Isaac integration provides a Leatherback Ackermann vehicle, ZED 2i-like 0.12 m stereo pair, RGB, camera info, registered depth, /clock, cone corridor, collision obstacles, and native /drive control.
On an Ubuntu RTX host or Brev VM:
Allow inbound TCP 49100 and UDP 47998 from only the workstation that will
run the WebRTC client. Do not expose the unauthenticated stream to the public
Internet.
export ROS_DOMAIN_ID=0
export PUBLIC_IP=<host-public-ip> # Only required for livestreaming.
ENABLE_LIVESTREAM=1 ./isaac_sim/run_brev.shFor headless operation:
ENABLE_LIVESTREAM=0 ./isaac_sim/run_brev.sh --obstacle-scenario centerThe launcher defaults to a 10 GB RTX 3080-safe camera profile: two 960x540
cameras at 20 Hz. Override it only after checking GPU memory usage:
ISAAC_RESOLUTION_WIDTH=1280 \
ISAAC_RESOLUTION_HEIGHT=720 \
ISAAC_CAMERA_RATE=30 \
ENABLE_LIVESTREAM=0 ./isaac_sim/run_brev.shMonitor the GPU in another terminal with watch -n 1 nvidia-smi. If Isaac Sim
approaches the 10 GB VRAM limit, keep the default profile or reduce the camera
rate to 15. The detector still uses a 640x640, batch-1 TensorRT input.
Available scenarios are clear, center, right, narrow, and blocked.
In another terminal on the same host:
source /opt/ros/humble/setup.bash
source install/setup.bash
ros2 launch cone_nav isaac.launch.py \
enable_rviz:=false \
engine_path:=/absolute/path/to/cone_yolo.engineIsaac and all autonomy nodes must share ROS_DOMAIN_ID, Fast DDS, and simulation time.
Ground-truth renderer depth is the deterministic default for functional and collision tests:
ros2 launch cone_nav isaac.launch.py depth_mode:=ground_truthStereo mode runs SGBM on the synchronized left/right images and publishes registered 32FC1 depth:
ros2 launch cone_nav isaac.launch.py depth_mode:=stereoUse ground truth to validate navigation and stereo mode to expose calibration, texture, correspondence, and dropout problems. The vehicle uses the ZED SDK's stereo-derived registered depth.
./isaac_sim/check_topics.sh
ros2 topic hz /sim/camera/image_raw
ros2 topic hz /sim/camera/depth
ros2 topic hz /obstacles/local_grid
ros2 topic info -v /drive_candidate
ros2 topic info -v /driveOnly after RGB, depth, camera intrinsics, TF, detections, obstacle grid, empty-path stopping, and single /drive ownership are confirmed:
ros2 launch cone_nav isaac.launch.py \
drive_enabled:=true \
enable_rviz:=false \
engine_path:=/absolute/path/to/cone_yolo.engineIsaac defaults to a 0.5 m/s target and 1.0 m/s maximum. ORB-SLAM3 is disabled because the starter scene does not publish a calibrated /sim/imu.
The safety path is intentionally separate from planning:
- Invalid or out-of-range depth is discarded; ground and the vehicle hood are filtered.
- Obstacles are inflated before candidate-path collision checks.
- Candidate paths must remain inside the cone corridor.
- No feasible path produces an empty
/path, which stops pure pursuit. - Stale paths, grids, depth, or
/drive_candidatemessages produce zero speed. drive_safety_nodeis the only final/drivepublisher.- Speed is limited using
v * latency + v^2 / (2 * deceleration) + margin. - Isaac starts disarmed.
Primary tuning lives in params.yaml and isaac_params.yaml:
track_half_width: 0.75
lookahead_distance: 1.5
speed_target: 1.5
wheelbase: 0.32
obstacle_inflation_radius: 0.22
avoidance_max_lateral_offset: 0.55
safety_grid_timeout_sec: 0.20
safety_braking_deceleration: 2.0
safety_stop_margin: 0.30Measure real braking performance before increasing vehicle speed. The defaults are starting values, not a validated physical safety case.
ros2 launch cone_nav sim.launch.py sim_type:=f1tenth
ros2 launch cone_nav sim.launch.py sim_type:=fsaeLegacy simulation bypasses obstacle avoidance by default because these bridges do not guarantee registered stereo depth. Enable it only after verifying /sim/camera/image_raw, right image, depth, camera info, timestamps, frames, and TF:
ros2 launch cone_nav sim.launch.py sim_type:=f1tenth avoidance_enabled:=trueThe FSAE path uses ackermann_to_twist_node for a typed /drive to /cmd_vel conversion.
Source-level tests available on any machine:
python3 -m unittest discover -s cone_nav/test -p 'test_*.py' -v
bash -n isaac_sim/run_brev.sh isaac_sim/check_topics.shBounded Isaac smoke test on the RTX host:
ENABLE_LIVESTREAM=0 ./isaac_sim/run_brev.sh \
--test-steps 120 --obstacle-scenario centerFull compatibility requires a successful target colcon build, Isaac runtime test, ROS topic/type/rate checks, and disarmed stop-behavior validation. macOS cannot perform the ROS/Isaac runtime portion.
The repository retains lightweight dataset and model tools:
python3 "Python Detection Code/ConvertToYOLO.py" # FSOCO -> three-class YOLO data
python3 "Python Detection Code/ConeDetection.py" # train YOLO26
python3 "Python Detection Code/WebcamDetection.py" # quick camera/model checkConversion reads ~/Downloads/fsoco_bounding_boxes_train by default; set
FSOCO_DIR to use another source. It pairs .jpg, .jpeg, and .png images
(case-insensitive extensions) with matching annotations, sorts them before the
seeded train/validation shuffle, and clips boxes to the image before normalization
and small-box filtering. Conversion checks that the source is a directory and
discovers image/annotation pairs before replacing output. An invalid source, no
matching pairs, or an error during discovery leaves existing output untouched.
Once pairs are found, conversion deletes and recreates data/yolo_format, so
preserve any needed output before rerunning it. This preflight does not validate
every image or annotation; processing failures can still occur after replacement.
Annotation discovery tries <image-stem>.json, then <image-filename>.json,
and accepts only files. Boxes with NaN or infinite coordinates are skipped;
other valid boxes in the same annotation are still converted.
Converted image and label names share the original image stem plus a stable
16-character SHA-256 suffix derived from its path relative to FSOCO_DIR,
including the source extension. This distinguishes repeated stems across team
folders and image formats. Existing converted files are not renamed automatically.
Training initializes from models/fsoco_yolo26n/weights/last.pt when it is a
file, otherwise best.pt, then yolo26n.pt if neither exists as a file. This
loads weights into a new training run; the script does not request resume=True.
It selects CUDA when available, otherwise CPU; set TRAIN_DEVICE to override
that choice. Apple MPS remains disabled by default.
The webcam utility defaults to camera 0 and selects the first model file in its
fallback list: YOLO26 best.pt, then legacy YOLOv8 best.pt and last.pt.
Use --model to select a specific file and --camera for another camera index:
python3 "Python Detection Code/WebcamDetection.py" \
--camera 1 --model /absolute/path/to/best.pt --conf 0.25 --imgsz 640Press q in the preview window to quit.
Training outputs and datasets are intentionally ignored by Git. Only the TensorRT runtime contract and class mapping are required by cone_nav.
- The planner is reactive and camera-relative; it does not perform global-map planning.
- ORB-SLAM3 is optional and requires real stereo/IMU calibration.
- Isaac ground-truth depth validates navigation but not stereo correspondence quality; use
depth_mode:=stereofor that. - A TensorRT engine must match the target GPU, CUDA, and TensorRT versions.
- The stack uses standard ROS 2 messages and defines no custom interfaces.
The avoidance grid helpers reject nonfinite coordinates, origins, and resolutions, nonpositive resolutions, and coordinate conversions that overflow. Collision checks reject sampled path points outside the grid, in unknown or occupied cells, or in cells missing from the grid data. These checks apply at the sampled path points; they do not check the segments between points.