Autonomy Software C++ 24.5.1
Welcome to the Autonomy Software repository of the Mars Rover Design Team (MRDT) at Missouri University of Science and Technology (Missouri S&T)! API reference contains the source code and other resources for the development of the autonomy software for our Mars rover. The Autonomy Software project aims to compete in the University Rover Challenge (URC) by demonstrating advanced autonomous capabilities and robust navigation algorithms.
Loading...
Searching...
No Matches
controllers::PurePursuitController Class Reference

This class implements the Pure Pursuit Controller. This controller is used to follow a path by calculating the steering angle required to reach a lookahead point a set distance down the reference path. More...

#include <PurePursuitController.h>

Classes

struct  DriveVector
 The struct for the drive vector that includes heading and velocity. More...
 

Public Member Functions

 PurePursuitController (const double dLookaheadDistance=2.0, const int nLookaheadIndex=5)
 Construct a new Pure Pursuit Controller:: Pure Pursuit Controller object.
 
DriveVector Calculate (const geoops::RoverPose &stCurrentPose, const double dMaxSpeed)
 Calculate an updated drive vector for the rover based on the current pose using the pure pursuit control law.
 
void SetReferencePath (const std::vector< geoops::Waypoint > &vReferencePath)
 Set the Reference Path object.
 
void SetLookaheadDistance (const double dLookaheadDistance)
 Set the Lookahead Distance object.
 
void SetLookaheadIndex (const int nLookaheadIndex)
 This will allow us to set the max amount of indices to lookahead.
 
std::vector< geoops::WaypointGetReferencePath () const
 Get the Reference Path object.
 
double GetLookaheadDistance () const
 Get the Lookahead Distance object.
 
int GetReferencePathTargetIndex () const
 Get the Reference Path Target Index object.
 

Private Member Functions

int FindClosestWaypointIndex (const geoops::UTMCoordinate &stCurrentPosition)
 Finds the closest waypoint index in the path to the current position, enforcing topological blinders to prevent jumping across overlapping paths.
 
geoops::Waypoint FindLookaheadWaypoint (const geoops::UTMCoordinate &stCurrentPosition)
 Searches forward from the current target index to find a point that is at least the lookahead distance away from the rover.
 

Private Attributes

double m_dLookaheadDistance
 
int m_nLookaheadIndex
 
int m_nCurrentReferencePathTargetIndex
 
std::vector< geoops::Waypointm_vReferencePath
 

Detailed Description

This class implements the Pure Pursuit Controller. This controller is used to follow a path by calculating the steering angle required to reach a lookahead point a set distance down the reference path.

Author
Sam Hajdukiewicz (saman.nosp@m.thah.nosp@m.ajduk.nosp@m.iewi.nosp@m.cz@gm.nosp@m.ail..nosp@m.com)
Date
2026-03-15

Constructor & Destructor Documentation

◆ PurePursuitController()

controllers::PurePursuitController::PurePursuitController ( const double  dLookaheadDistance = 2.0,
const int  nLookaheadIndex = 5 
)

Construct a new Pure Pursuit Controller:: Pure Pursuit Controller object.

Parameters
dLookaheadDistance- The distance in meters to look ahead of current position.
dLookaheadIndex- The number of waypoints the rover will look ahead.
Author
Sam Hajdukiewicz (saman.nosp@m.thah.nosp@m.ajduk.nosp@m.iewi.nosp@m.cz@gm.nosp@m.ail..nosp@m.com)
Date
2026-03-15
41 {
42 // Initialize member variables.
43 m_dLookaheadDistance = dLookaheadDistance;
44 m_nLookaheadIndex = nLookaheadIndex;
45 m_nCurrentReferencePathTargetIndex = 0;
46 }

Member Function Documentation

◆ Calculate()

PurePursuitController::DriveVector controllers::PurePursuitController::Calculate ( const geoops::RoverPose stCurrentPose,
const double  dMaxSpeed 
)

Calculate an updated drive vector for the rover based on the current pose using the pure pursuit control law.

Parameters
stCurrentPose- The current pose of the rover.
dMaxSpeed- The maximum speed the rover can travel.
Returns
DriveVector - The new output heading and speed for the rover.
Author
Sam Hajdukiewicz (saman.nosp@m.thah.nosp@m.ajduk.nosp@m.iewi.nosp@m.cz@gm.nosp@m.ail..nosp@m.com)
Date
2026-03-15
60 {
61 // Check if the reference path is empty.
62 if (m_vReferencePath.empty())
63 {
64 // Submit logger message.
65 LOG_WARNING(logging::g_qSharedLogger, "PurePursuitController::Calculate: Reference path is empty. Cannot calculate drive powers.");
66
67 return DriveVector{0.0, 0.0};
68 }
69
70 // Evaluate the rover position to properly advance the path index based on physical location.
71 m_nCurrentReferencePathTargetIndex = FindClosestWaypointIndex(stCurrentPose.GetUTMCoordinate());
72
73 // Check if we are at the end of the path.
74 if (m_nCurrentReferencePathTargetIndex >= static_cast<int>(m_vReferencePath.size()) - 2)
75 {
76 // Get the start and end points of the last segment.
77 geoops::UTMCoordinate stLastPoint = m_vReferencePath.back().GetUTMCoordinate();
78 geoops::UTMCoordinate stSecondToLastPoint = m_vReferencePath[m_vReferencePath.size() - 2].GetUTMCoordinate();
79 geoops::UTMCoordinate stRoverPos = stCurrentPose.GetUTMCoordinate();
80
81 // Calculate vector of the last segment (Start -> End).
82 double dSegmentX = stLastPoint.dEasting - stSecondToLastPoint.dEasting;
83 double dSegmentY = stLastPoint.dNorthing - stSecondToLastPoint.dNorthing;
84 double dSegmentLenSq = dSegmentX * dSegmentX + dSegmentY * dSegmentY;
85
86 // Calculate vector from Segment Start -> Rover.
87 double dRoverVectorX = stRoverPos.dEasting - stSecondToLastPoint.dEasting;
88 double dRoverVectorY = stRoverPos.dNorthing - stSecondToLastPoint.dNorthing;
89
90 // Project rover onto the segment vector.
91 double dNormalDistance = 0.0;
92 if (dSegmentLenSq > 1e-6)
93 {
94 dNormalDistance = (dRoverVectorX * dSegmentX + dRoverVectorY * dSegmentY) / dSegmentLenSq;
95 }
96
97 // Check if we have passed the end or are very close to it.
98 if (dNormalDistance >= 1.0 || std::hypot(dRoverVectorX, dRoverVectorY) < 0.5)
99 {
100 // We have reached or passed the end. Command the rover to stop.
101 double dHeadingToLastWaypoint = geoops::CalculateGeoMeasurement(stRoverPos, stLastPoint).dStartRelativeBearing;
102 return DriveVector{dHeadingToLastWaypoint, 0.0};
103 }
104 }
105
106 // Find the lookahead point on the path ahead of the rover.
107 geoops::Waypoint stLookaheadWaypoint = FindLookaheadWaypoint(stCurrentPose.GetUTMCoordinate());
108
109 // Calculate the required heading to reach the lookahead point.
110 double dDeltaX = stLookaheadWaypoint.GetUTMCoordinate().dEasting - stCurrentPose.GetUTMCoordinate().dEasting;
111 double dDeltaY = stLookaheadWaypoint.GetUTMCoordinate().dNorthing - stCurrentPose.GetUTMCoordinate().dNorthing;
112
113 // Math angle of segment (atan2 gives angle from +X (East), CCW positive) in degrees.
114 double dTargetMathDeg = std::atan2(dDeltaY, dDeltaX) * (180.0 / M_PI);
115
116 // Convert math angle to COMPASS heading (0 = North, clockwise positive): compass = (90 - math) mod 360.
117 double dAbsoluteHeadingGoal = std::fmod(90.0 - dTargetMathDeg + 360.0, 360.0);
118
119 return DriveVector{dAbsoluteHeadingGoal, dMaxSpeed};
120 }
geoops::Waypoint FindLookaheadWaypoint(const geoops::UTMCoordinate &stCurrentPosition)
Searches forward from the current target index to find a point that is at least the lookahead distanc...
Definition PurePursuitController.cpp:266
int FindClosestWaypointIndex(const geoops::UTMCoordinate &stCurrentPosition)
Finds the closest waypoint index in the path to the current position, enforcing topological blinders ...
Definition PurePursuitController.cpp:212
GeoMeasurement CalculateGeoMeasurement(const GPSCoordinate &stCoord1, const GPSCoordinate &stCoord2)
The shortest path between two points on an ellipsoid at (lat1, lon1) and (lat2, lon2) is called the g...
Definition GeospatialOperations.hpp:553
const geoops::UTMCoordinate & GetUTMCoordinate() const
Accessor for the geoops::UTMCoordinate member variable.
Definition GeospatialOperations.hpp:767
This struct stores/contains information about a UTM coordinate.
Definition GeospatialOperations.hpp:211
This struct is used by the WaypointHandler class to store location, size, and type information about ...
Definition GeospatialOperations.hpp:423
const geoops::UTMCoordinate & GetUTMCoordinate() const
Accessor for the geoops::UTMCoordinate member variable.
Definition GeospatialOperations.hpp:508
Here is the call graph for this function:

◆ SetReferencePath()

void controllers::PurePursuitController::SetReferencePath ( const std::vector< geoops::Waypoint > &  vReferencePath)

Set the Reference Path object.

Parameters
vReferencePath- The new reference path for the controller to follow.
Author
Sam Hajdukiewicz (saman.nosp@m.thah.nosp@m.ajduk.nosp@m.iewi.nosp@m.cz@gm.nosp@m.ail..nosp@m.com)
Date
2026-03-15
131 {
132 // Reset the index and store the new path.
133 m_nCurrentReferencePathTargetIndex = 0;
134 m_vReferencePath = vReferencePath;
135 }

◆ SetLookaheadDistance()

void controllers::PurePursuitController::SetLookaheadDistance ( const double  dLookaheadDistance)

Set the Lookahead Distance object.

Parameters
dLookaheadDistance- The distance ahead of the rover to place the target point.
Author
Sam Hajdukiewicz (saman.nosp@m.thah.nosp@m.ajduk.nosp@m.iewi.nosp@m.cz@gm.nosp@m.ail..nosp@m.com)
Date
2026-03-15
146 {
147 m_dLookaheadDistance = dLookaheadDistance;
148 }

◆ SetLookaheadIndex()

void controllers::PurePursuitController::SetLookaheadIndex ( const int  nLookAheadIndex)

This will allow us to set the max amount of indices to lookahead.

Parameters
nLookAheadIndex- How many waypoints/indices we can look ahead.
Author
Sam Hajdukiewicz (saman.nosp@m.thah.nosp@m.ajduk.nosp@m.iewi.nosp@m.cz@gm.nosp@m.ail..nosp@m.com)
Date
2026-04-14
159 {
160 m_nLookaheadIndex = nLookAheadIndex;
161 }

◆ GetReferencePath()

std::vector< geoops::Waypoint > controllers::PurePursuitController::GetReferencePath ( ) const

Get the Reference Path object.

Returns
std::vector<geoops::Waypoint> - The current reference path.
Author
Sam Hajdukiewicz (saman.nosp@m.thah.nosp@m.ajduk.nosp@m.iewi.nosp@m.cz@gm.nosp@m.ail..nosp@m.com)
Date
2026-03-15
172 {
173 return m_vReferencePath;
174 }

◆ GetLookaheadDistance()

double controllers::PurePursuitController::GetLookaheadDistance ( ) const

Get the Lookahead Distance object.

Returns
double - The current lookahead distance.
Author
Sam Hajdukiewicz (saman.nosp@m.thah.nosp@m.ajduk.nosp@m.iewi.nosp@m.cz@gm.nosp@m.ail..nosp@m.com)
Date
2026-03-15
185 {
186 return m_dLookaheadDistance;
187 }

◆ GetReferencePathTargetIndex()

int controllers::PurePursuitController::GetReferencePathTargetIndex ( ) const

Get the Reference Path Target Index object.

Returns
int - The current index of the reference path.
Author
Sam Hajdukiewicz (saman.nosp@m.thah.nosp@m.ajduk.nosp@m.iewi.nosp@m.cz@gm.nosp@m.ail..nosp@m.com)
Date
2026-03-15
198 {
199 return m_nCurrentReferencePathTargetIndex;
200 }

◆ FindClosestWaypointIndex()

int controllers::PurePursuitController::FindClosestWaypointIndex ( const geoops::UTMCoordinate stCurrentPosition)
private

Finds the closest waypoint index in the path to the current position, enforcing topological blinders to prevent jumping across overlapping paths.

Parameters
stCurrentPosition- The current UTM coordinate of the rover.
Returns
int - The index of the closest waypoint.
Author
Sam Hajdukiewicz (saman.nosp@m.thah.nosp@m.ajduk.nosp@m.iewi.nosp@m.cz@gm.nosp@m.ail..nosp@m.com)
Date
2026-03-15
213 {
214 if (m_vReferencePath.empty())
215 {
216 return 0;
217 }
218
219 int nBestSegmentIndex = m_nCurrentReferencePathTargetIndex;
220 double dClosestDistanceSq = std::numeric_limits<double>::max();
221 int nConsecutiveIncreases = 0;
222
223 // Search forward starting from the current known index
224 for (size_t siIter = static_cast<size_t>(m_nCurrentReferencePathTargetIndex); siIter < m_vReferencePath.size(); ++siIter)
225 {
226 const geoops::UTMCoordinate& stA = m_vReferencePath[siIter].GetUTMCoordinate();
227
228 double dDeltaX = stCurrentPosition.dEasting - stA.dEasting;
229 double dDeltaY = stCurrentPosition.dNorthing - stA.dNorthing;
230 double dDistSq = dDeltaX * dDeltaX + dDeltaY * dDeltaY;
231
232 if (dDistSq < dClosestDistanceSq)
233 {
234 // We found a closer point. Update best and reset the blinder counter.
235 dClosestDistanceSq = dDistSq;
236 nBestSegmentIndex = static_cast<int>(siIter);
237 nConsecutiveIncreases = 0;
238 }
239 else
240 {
241 nConsecutiveIncreases++;
242
243 // TOPOLOGICAL BLINDER:
244 // If the distance increases for N consecutive points, we have found the local minimum.
245 // This stops the search so we don't accidentally jump to the adjacent ring of a spiral.
246 if (nConsecutiveIncreases >= m_nLookaheadIndex)
247 {
248 break;
249 }
250 }
251 }
252
253 return nBestSegmentIndex;
254 }
Here is the caller graph for this function:

◆ FindLookaheadWaypoint()

geoops::Waypoint controllers::PurePursuitController::FindLookaheadWaypoint ( const geoops::UTMCoordinate stCurrentPosition)
private

Searches forward from the current target index to find a point that is at least the lookahead distance away from the rover.

Parameters
stCurrentPosition- The current UTM coordinate of the rover.
Returns
geoops::Waypoint - The target waypoint to drive towards.
Author
Sam Hajdukiewicz (saman.nosp@m.thah.nosp@m.ajduk.nosp@m.iewi.nosp@m.cz@gm.nosp@m.ail..nosp@m.com)
Date
2026-03-15
267 {
268 const size_t siWaypoints = m_vReferencePath.size();
269
270 // Calculate the distance from the rover to the TRUE closest point on the path
271 const geoops::UTMCoordinate& stClosest = m_vReferencePath[m_nCurrentReferencePathTargetIndex].GetUTMCoordinate();
272 double dClosestDist = std::hypot(stClosest.dEasting - stCurrentPosition.dEasting, stClosest.dNorthing - stCurrentPosition.dNorthing);
273
274 // OFF-PATH RECOVERY:
275 // If the closest point on the path is further away than your lookahead circle,
276 // no point ahead will be strictly < L. To prevent returning a point behind the rover,
277 // we must target the closest point to pull the rover back onto the path.
278 if (dClosestDist > m_dLookaheadDistance)
279 {
280 return m_vReferencePath[m_nCurrentReferencePathTargetIndex];
281 }
282
283 // FIND INTERSECTION AHEAD:
284 // Because the closest point is guaranteed to be < L (from the check above),
285 // searching forward guarantees we start INSIDE the circle.
286 // The first point we find that is >= L represents exiting the circle AHEAD of the rover.
287 for (size_t siIter = m_nCurrentReferencePathTargetIndex; siIter < siWaypoints; ++siIter)
288 {
289 const geoops::UTMCoordinate& stTarget = m_vReferencePath[siIter].GetUTMCoordinate();
290
291 double dDeltaX = stTarget.dEasting - stCurrentPosition.dEasting;
292 double dDeltaY = stTarget.dNorthing - stCurrentPosition.dNorthing;
293 double dDistance = std::hypot(dDeltaX, dDeltaY);
294
295 // The moment we cross the lookahead radius boundary, return that point.
296 if (dDistance >= m_dLookaheadDistance)
297 {
298 return m_vReferencePath[siIter];
299 }
300 }
301
302 // If no point is far enough ahead (we are at the end of the path), return the very last point.
303 return m_vReferencePath.back();
304 }
Here is the caller graph for this function:

The documentation for this class was generated from the following files: