Central engineering reference and operations manual for the MRDT Autonomy Software.
View the Project on GitHub MissouriMRDT/Autonomy_Software
Return to RoveSoDocs Guides for Today, Tomorrow, and Forever.
The PurePursuitController class (src/algorithms/controllers/PurePursuitController.h) provides an alternative geometric path-tracking controller that calculates the steering angle required to pursue a lookahead waypoint located a set distance down the reference path.
Pure Pursuit is a widely established geometric tracking algorithm. Unlike PID heading control (which aims at the nearest node) or Stanley control (which evaluates perpendicular cross-track error to the nearest line segment), Pure Pursuit fits a circular arc between the rover’s current pose and a dynamic “lookahead” point on the reference trajectory.
The controller exposes constructor arguments and runtime mutators:
| Parameter | Default | Purpose and Impact |
|---|---|---|
dLookaheadDistance |
2.0 meters |
Distance along the path where the target point is selected. Increasing $L_d$ results in smoother trajectories but cuts corners. Decreasing $L_d$ tracks the path tighter but can induce lateral oscillations. |
nLookaheadIndex |
5 |
Fallback index offset when distance-based lookahead search reaches path limits. |
dMaxSpeed |
constants::NAVIGATING_MOTOR_POWER |
Maximum linear velocity ceiling commanded by the controller. |
DriveVectorThe controller returns a PurePursuitController::DriveVector struct:
struct DriveVector
{
double dThetaHeading; // Target absolute compass heading setpoint (degrees)
double dVelocity; // Target forward velocity (-1.0 to 1.0)
};
Calculate() method enforces that the reference path contains at least 2 points (m_vReferencePath.size() < 2). If the reference path has 0 or 1 waypoint, Calculate() logs a warning:
PurePursuitController::Calculate: Reference path has fewer than 2 points. Cannot calculate drive powers.
and returns DriveVector{0.0, 0.0}. This prevents undefined behavior or segmentation faults when evaluating terminal path segments, calculating projection vectors (stLastPoint and stSecondToLastPoint), or computing lookahead intersections on degenerate paths.
// Instantiate with a 2.5 meter lookahead distance
controllers::PurePursuitController controller(2.5, 5);
// Set reference path from GeoPlanner
controller.SetReferencePath(vGeoPlannerPath);
// Execute within state machine loop
geoops::RoverPose stPose = globals::g_pStateMachineHandler->SmartRetrieveRoverPose();
controllers::PurePursuitController::DriveVector stVector = controller.Calculate(stPose, constants::NAVIGATING_MOTOR_POWER);
// Pass setpoints to DriveBoard kinematics
globals::g_pDriveBoard->CalculateMove(stVector.dVelocity, stVector.dThetaHeading, stPose.GetCompassHeading());
globals::g_pDriveBoard->SendDrive();