Pipeline Integration Deep Dive¶
This document connects the RRT* global planner with the MPC-based tracker by walking through the runtime hand-off points, contracts, and numerical considerations encoded in the source code. Use it when embedding the pipeline in research experiments or when swapping out individual components.
End-to-End Control Flow¶
- Configuration loading –
src.config.load_config()deserialises YAML intoPipelineConfig, exposing strongly-typed stage parameters and default seeds so reproducible runs do not depend on global state. - Stage construction –
pipeline.orchestrator.PipelineOrchestrator.run()initialisesMapStage,PlanningStage, andTrajectoryTrackerwith the relevant sub-configs while configuring the requested Matplotlib backend. It also logs per-stage timings for observability. - Map preparation –
MapStage.build()guarantees that a grayscale map exists (regenerating it viamaps.generator.MapGeneratorwhen requested), inflates obstacles, and returns aMapArtifactsdataclass bundling the occupancy grid, start/goal pose, inflation masks, and workspace extents for planners operating in continuous space. - Planning –
PlanningStage.plan()executesplanning.rrt_star.RRTStarPlanneron the inflated occupancy grid. It wraps the resultingPlanResultinsidePlanningArtifactsand applies optional Catmull-Rom smoothing so the control stage receives curvature-aware waypoints without extra pre-processing. - Tracking –
TrajectoryTracker.track()converts the selected path into an MPC reference usingcontrol.ref_builder.build_reference, solves the MPC viacontrol.mpc_controller.MPCController, and optionally streams prediction frames to the visualisation helpers. The closed-loop state history is stored in aTrackingResultfor downstream analysis. - Aggregation – The orchestrator returns
PipelineResult, exposing.map,.plan, and.controlattributes so CLI commands, notebooks, or tests can inspect intermediate artefacts without re-running stages.
Planner/Controller Interface¶
MapArtifactsexposes both discrete (raw and inflated occupancy grids) and continuous representations (workspace bounds). RRT* consumes the inflated grid directly, while the inflation mask supports richer visualisation overlays.PlanResult.pathis guaranteed to contain the smoothed trajectory when available.PlanResult.raw_pathremains accessible, giving numerical experiments access to the unsmoothed exploration tree.TrajectoryTrackerreadsPlanResult.successbefore starting MPC tracking; unsuccessful runs raise immediately so experiments can retry planning or report the failure.
MPC Execution Strategy¶
MPCConfig.to_parameters()rescales the vehicle wheelbase into pixel units and instantiatesMPCParameters, keeping geometry consistent with the map resolution provided byMapConfig._solve_with_relaxation()attempts a nominal solve first. If OSQP reports infeasibility, the helper reduces the reference speed by 40% and widens rate limits before retrying. Persistent failures are logged and bubble up asNoneto terminate the loop gracefully.- The main loop in
TrajectoryTracker.track()integrates the discrete bicycle model (control.vehicle_model.f_discrete) with the chosen control input and records progress every 10% of the configured simulation steps. Distance-based path advancement ensures the reference window slides forward only after the vehicle leaves the current waypoint neighbourhood. - Visualisation hooks (
viz.visualization.plot_predictionandviz.record.FrameRecorder) are optional and do not affect the returned artefacts, enabling headless evaluation while still supporting offline GIF generation.
Determinism & Scaling Considerations¶
- Planning and map generation use explicit RNG seeds (
PlannerConfig.random_seed,MapConfig.generator_seed), ensuring identical trees and maps across runs when input parameters match. - NumPy-based occupancy inflation and reference construction avoid Python loops, keeping per-step latency low as map resolution increases.
- OSQP warm starts combined with modest horizons (default 15 steps) strike a balance between responsiveness and solve time; adjust
MPCConfig.horizonand penalty matrices when targeting higher-speed manoeuvres.
Extending the Pipeline¶
- Replace
MapStagewith a custom implementation that still returnsMapArtifactsto integrate external occupancy providers (e.g. ROS2 costmaps). - Register new planners inside
PlanningStage.plan()by dispatching on a config key and returning a populatedPlanResult. - Swap the MPC with an alternative controller by implementing the
TrajectoryTracker.track()contract;PipelineResultwill continue to expose the new state history for analysis and tooling. - Use the artefact dataclasses as the single source of truth for new metadata fields (e.g. control inputs, cost metrics) so both CLI and API consumers pick up changes transparently.