Perception › ROS 2 implementation › Acronym glossary

Port the models, prove the system

How the Python BEVFusion + SimpleTrack implementation becomes a composed, testable ROS 2 graph; where segmentation and occupancy fit; and which tests distinguish a faster system from a system that is merely wrong faster. Start with the architecture and message reference.

Symbols used on this page are defined in the Nomenclature → metrics.

1 · Map every Python contract to an executable node

Reference pipelineROS 2 equivalentWhy this seam exists
sensor_source_nuscenes.pynuscenes_replay_nodeStamped sensor-topic replay for integration, never a GT input to perception.
contracts.SensorFrame, _frame_to_data_dict()SensorFrame + frame_sync_node + calibration cacheOne sweep-anchored, immutable snapshot with valid-view mask and pose.
ImageAug3D, Normalize, point loadingimage_ingest_node + lidar_decoder_nodeFused image preprocessing; packet accumulation and point-time de-skew before GPU encode.
BEVFusion camera backbone, depth and BEVPoolcamera_encoder_nodeIndependent CUDA stream and measured stage deadline.
Voxel encoder, sparse convolutionlidar_encoder_nodeParallel with camera, common metric BEV geometry.
ConvFuser and TransFusion/CenterHeadbev_fuser_node + det_head_nodeFallback to LiDAR-only branch; isolated box decoding and head version.
Detection/Detections, global conversionDetection3D/Detections3D + box transformExplicit axes, dimensions, yaw and frame timestamp.
TrackerBank, MOTModel, Kalman, hit managertracker_node + per-class ClassTrackerRotated GIoU, two-stage Hungarian matching, birth/COASTED/death and covariance.
Localization and sinksExternal EKF/TF + viz_node + health_nodeMap/odom continuity, status and failures observable to GNC.
pipeline.py loopROS graph + executor/QoS + latest-only bufferExplicit bounded callbacks rather than serial in-memory calls.

Python remains excellent for training, rapid exploration and official devkit evaluation. A Python bridge can validate bring-up. The composed C++/TensorRT graph buys bounded queueing, observable per-link failures, shared GPU context and concurrency; it must earn complexity through measurement rather than assumption.

2 · Define a numerical contract before enabling TensorRT

ArrowTensor / spatial invariant
Ingest → camera encoderSix normalized FP16 images, $[6,3,256,704]$ NCHW; camera intrinsics updated for crop/resize.
Decoder → LiDAR encoder$[N,5]$ FP32 $(x,y,z,intensity,\Delta t)$; past-sweep $\Delta t$ sign must match training.
Frame sync → encodersPer-view LiDAR-to-image and augmentation $4\times4$ matrices, valid camera mask, ego pose at sweep time.
Camera / LiDAR → fuser$[1,80,180,180]$ and $[1,256,180,180]$ FP16 BEV respectively, identical frame and 0.6 m cell resolution.
Fuser → heads$[1,512,180,180]$ FP16; ready CUDA event and same-process buffer owner.
Detection → trackerAt most 300 $(x,y,z,l,w,h,yaw,score,label)$ boxes; compare ego-frame and transformed map-frame conventions.

The reference point-cloud range is $[-54,-54,-5,54,54,3]$ m; voxel size $[.075,.075,.2]$ m yields a $1440\times1440\times40$ grid, downsampled by 8 to $180\times180$ BEV. LSS depth samples $1$ to $60$ m by $.5$ m. Assert $grid/8=bev_{H,W}$ at startup. Misinterpreting $(z,y,x)$ voxel order, changing the point time sign, swapping length/width, or treating a box bottom as center produces plausible pictures and wrong trajectories.

  1. Graph-only: synthetic input, stub backend, one container, 11 model/health nodes, no unexpected frame drops.
  2. Preprocess parity: same nuScenes frame, Python vs ROS FP16 image and $[N,5]$ point tensors; compare with a defined absolute tolerance.
  3. Encoder parity: same input into both, compare camera and LiDAR BEV features by numerical errors and per-channel cosine similarity; identify depth/voxel indexing errors.
  4. Detection parity: freeze confidence/NMS and compare box count, center, yaw, size, class/score.
  5. Tracking parity: inject identical detections into both trackers for a complete scene, compare association and lifecycle/ID sequence.
  6. Only then performance: FP16/TensorRT, pinned uploads, preallocated buffers, stream overlap, CUDA graphs. Re-run quality after every optimization.

3 · Segmentation is three different engineering problems

TaskOutput / consumerStatus and trade-off
Per-camera 2D semanticImage-space $[H,W]$ classes for sensor QA and lane cuesOptional node. Lifting into metric space needs depth, extrinsics and ground assumptions; IPM fails on slopes.
BEV map segmentationEgo-centric $[K,H,W]$ probabilities for drivable area/costmapShared BEV feature head in seg_head_node; lower priority than detection.
3D occupancy$[X,Y,Z,C]$ voxel state for height-aware collisionProposed/stub, not shipped. Valuable for overhangs; expensive memory and inference.

The six BEV map channels are drivable area, pedestrian crossing, walkway, stop line, carpark and divider. They overlap: use per-channel sigmoids, not mutually exclusive softmax (a crosswalk is also drivable). BevGrid is channel-first: $idx=cHW+rW+col$, with forward $x$ mapping to column and left $y$ to row. Publish a multi-channel probability grid and a separate planner-compatible single-channel drivable OccupancyGrid. Near a free/occupied decision boundary represent unknown rather than assert free. Document whether occupancy values encode free=0 / occupied=100 / unknown=-1 and when a grid is stale.

If image segmentation is needed, reuse GPU images; publish compact per-view labels at a lower rate on a low-priority stream, and undistort → infer depth → transform → rasterize before calling it a BEV map. A 200×200×16 voxel grid with 18 FP16 class channels is about 23 MB per frame; keep that on the GPU and downsample only for debugging. Example planning budget: detection head ~15 ms high priority, BEV segmentation ~8 ms lower priority, optional occupancy ~25 ms and six-camera 2D head ~20 ms; enabling all may exceed the 100 ms period. Profile on hardware rather than assuming stream overlap makes compute free.

Evaluate BEV with per-class IoU $IoU_c=TP_c/(TP_c+FP_c+FN_c)$ (also range slices), image boundaries with boundary F1, occupancy using occupied-voxel/ray IoU. A mean dominated by drivable road surface says little about rare stop-line misses or ramps.

4 · Debug from the first violated contract

SymptomFirst checksLikely root cause
No topic dataros2 topic info --verbose; endpoint names/remaps, offered/requested QoSReliable observer of best-effort stream, volatile vs transient local mismatch, unrouted group.
Latency grows each frameQueue depth, stamp age, drop count, retransmit and per-callback tracesReliable large images/clouds, stale backlog, subscriber blocked by group.
Standalone works; container failsForwarded NodeOptions, callback groups, stream/workspace ownershipIntra-process disabled, serialization or GPU context race.
Detections consistently displacedSigned ego-frame error, calibration matrices and timestamp, box-convention testsResize/intrinsics drift, voxel order, pose age or length/width swap.
Frame-rate fine, tail age awfulCapture-to-publish histograms (p99/p99.9), CUDA event and executor traceThread wakeup jitter, GPU thermal stall or blocked stream dependency.
ros2 node list
ros2 topic info /lidar/points --verbose
ros2 topic echo /lidar/points --qos-reliability best_effort --once
ros2 component list
ros2 param dump /det_head
ros2 run perception_py latency_report
ros2 topic echo /perception/status

Trace callback dispatch and publish-to-callback delay with ros2_tracing/LTTng (or CARET to reconstruct chains); correlate stamps to CUDA/NVTX ranges using Nsight Systems. Look for unexpected memcpy between BEV stages, no camera/LiDAR kernel overlap, steady-state cudaMalloc, device-wide synchronization and unpinned uploads. Profiling within a callback alone misses executor waiting: include capture and end-to-end age.

5 · A test pyramid that can falsify the design

LayerContract / evidence
Unit, on any laptopBox-frame conversion, sweep pairing under dead camera, buffer ownership, GPU host-reference kernels, tracker association/ID lifetimes, detection matcher and MOTA accumulator.
Synthetic integration, CPU stubCompose the nodes, publish deterministic frames, verify stamps, partial-view path, QoS compatibility, no unwarranted queue growth and exact evaluator totals.
Dataset parity, offline GPUFrozen nuScenes scenes: per-stage tensor tolerances, box and ID parity; run official evaluation separately from online approximations.
Hardware stress + fault injectionStop a camera, kill LiDAR, lose PTP, impose 1% network loss, restart planner, slow GPU and fill recorder disk; measure transition-to-safe-output.
colcon build --symlink-install --cmake-args -DPERCEPTION_WITH_CUDA=OFF
colcon test --event-handlers console_direct+
colcon test-result --verbose
ros2 launch perception_bringup full_system.launch.py backend:=stub source:=synthetic

A successful CPU-stub latency test proves plumbing, not the real BEVFusion latency or accuracy. Run thermal and sustained-load tests on the deployment SoC; require quality and latency together. Route rosbag recording to a separate low-priority process; record sensor stamps, raw packets, calibrated state, outputs, status, per-stage latency and evaluation results if GT exists. Do not record process-local CUDA pointer topics: they cannot be meaningfully replayed. Include negative tests for QoS mismatches and lifecycle restart, not just sunny-day scenes.

6 · Explain it at senior level: challenge drills

For every answer give a choice, the alternative you rejected, a failure mode and a measurement. Practice these aloud:

  1. Six cameras + LiDAR: justify QoS for each link; why is a reliable 6 MB sweep often worse than one lost sweep?
  2. Why does a multithreaded executor sometimes serialize an entire node? Show explicit callback groups, affinity and a service deadlock fix.
  3. Distinguish intra-process move, shared fan-out, loaned SHM and a GPU handle; prove each rather than labeling everything “zero copy.”
  4. Whiteboard the Python SensorFrame-to-BEVFusion-to-tracker mapping; where do temporal calibration and per-point LiDAR offsets come from?
  5. Why is GNC not in the GPU container? Which topics and freshness limits form its contract, and how does it stop if perception dies?
  6. Why does BEV map segmentation use sigmoids? When is 3D occupancy worth its 23 MB per frame?
  7. mAP improved, but pedestrian recall at 40 m got worse. Which number limits speed? How do you find a threshold vs sensor vs calibration failure?
  8. MOTA .71, recall .88, IDF1 field .90: which definitions are actually implemented? Does this dashboard compute sequence-level IDF1?
  9. A LiDAR-only fallback “works” on a sunny dataset; how do you test spray, low-return pedestrians and sensor common-mode loss?
90-second architecture story: drivers publish time-stamped best-effort sensor streams; an 11-node composed GPU container uploads once, pairs the nearest six images to each sweep, runs independent camera/LiDAR BEV branches, fuses and detects, then tracks in a stable frame. One image/tensor allocation stays on-device; streams synchronize by events. Separate executors prioritize ingest, inference and housekeeping; GNC and evaluator live in separate processes. Results are age-gated; a watchdog forces degradation or a safe stop. Unit parity, hardware traces and fault injection — not a nominal ROS graph screenshot — establish whether that design closes.

7 · Source guide: the complete ROS 2 study set

The tutorial's pages map to all twelve source chapters. Continue at the section that answers your next question:

Source chapterWeb tutorial treatment
01 · Core conceptsMiddleware/time/TF, services/params.
02 · Executors and callback groupsDispatch, groups, priority and deadlocks.
03 · QoS trade-offsSensor-by-sensor matrix and compatibility.
04 · Zero copyFour mechanisms and fixed-capacity SHM.
05 · Perception architectureGraph, requirements, nodes, all message types.
06 · Composition and containersSingle process, lifecycle and scheduling.
07 · GNC integrationControl contract, frames and degraded response.
08 · BEVFusion ROS 2 mappingPython → nodes and tensor parity.
09 · Segmentation and BEV2D, BEV, occupancy and their budget.
10 · Testing, profiling, debuggingDecision tree and traces/CI/fault injection.
11 · Online evaluationComplete metric mathematics and runtime errors.
99 · Interview drillsTrade-off and failure-mode questions with links throughout these pages.