Calculate an updated steering angle for the rover based on the current pose using the predictive stanley controller.
79 {
80
81 double dSteeringAngle = 0.0;
82 std::vector<UnicycleModel::Prediction> vPredictions;
83
84
85 if (m_vReferencePath.empty())
86 {
87
88 LOG_WARNING(logging::g_qSharedLogger, "PredictiveStanleyController::Calculate: Reference path is empty. Cannot calculate drive powers.");
89
90 return DriveVector{0.0, 0.0};
91 }
92
93
94
95
96 if (m_nCurrentReferencePathTargetIndex >= static_cast<int>(m_vReferencePath.size()) - 2)
97 {
98
100 geoops::UTMCoordinate stSecondToLastPoint = m_vReferencePath[m_vReferencePath.size() - 2].GetUTMCoordinate();
102
103
104 double dSegmentX = stLastPoint.dEasting - stSecondToLastPoint.dEasting;
105 double dSegmentY = stLastPoint.dNorthing - stSecondToLastPoint.dNorthing;
106 double dSegmentLenSq = dSegmentX * dSegmentX + dSegmentY * dSegmentY;
107
108
109 double dRoverVectorX = stRoverPos.dEasting - stSecondToLastPoint.dEasting;
110 double dRoverVectorY = stRoverPos.dNorthing - stSecondToLastPoint.dNorthing;
111
112
113
114 double dNormalDistance = 0.0;
115 if (dSegmentLenSq > 1e-6)
116 {
117 dNormalDistance = (dRoverVectorX * dSegmentX + dRoverVectorY * dSegmentY) / dSegmentLenSq;
118 }
119
120
121
122 if (dNormalDistance >= 1.0)
123 {
124
125
127 return DriveVector{dHeadingToLastWaypoint, 1.0};
128 }
129 }
130
131
133
134 m_UnicycleModel.
Predict(m_dPredictionTimeStep, m_nPredictionHorizon, vPredictions);
135
136
137 for (size_t nIter = 0; nIter < vPredictions.size(); ++nIter)
138 {
139
140 double dPredictedXPosition = vPredictions[nIter].dXPosition;
141 double dPredictedYPosition = vPredictions[nIter].dYPosition;
142 double dPredictedTheta = vPredictions[nIter].dTheta;
143
144
146 stPredictedPosition.dEasting = dPredictedXPosition;
147 stPredictedPosition.dNorthing = dPredictedYPosition;
148
150
151
152 int nIdx = m_nCurrentReferencePathTargetIndex;
153 int nNextIdx = std::min(nIdx + 1, static_cast<int>(m_vReferencePath.size() - 1));
156
157
158 double dForwardVectorX = stSegmentEnd.dEasting - stSegmentStart.dEasting;
159 double dForwardVectorY = stSegmentEnd.dNorthing - stSegmentStart.dNorthing;
160 double dForwardNorm = std::hypot(dForwardVectorX, dForwardVectorY);
161
162 if (dForwardNorm < 1e-9)
163 {
164 continue;
165 }
166
167
168 double dFwdUnitX = dForwardVectorX / dForwardNorm;
169 double dFwdUnitY = dForwardVectorY / dForwardNorm;
170
171 double dSegmentMathDeg = std::atan2(dForwardVectorY, dForwardVectorX) * (180.0 / M_PI);
172
173
174 double dPathHeadingDeg = std::fmod(90.0 - dSegmentMathDeg + 360.0, 360.0);
175
176 double dPredThetaDeg = dPredictedTheta;
177
178 std::function<double(double, double)> AngleDiffDeg = [](double targetDeg, double sourceDeg) -> double
179 {
180 double diff = std::fmod(targetDeg - sourceDeg + 540.0, 360.0) - 180.0;
181 return diff;
182 };
183
184 double dHeadingError = AngleDiffDeg(dPathHeadingDeg, dPredThetaDeg);
185
186
187 double dVehicleVectorX = dPredictedXPosition - stClosestWaypoint.
GetUTMCoordinate().dEasting;
188 double dVehicleVectorY = dPredictedYPosition - stClosestWaypoint.
GetUTMCoordinate().dNorthing;
189
190 double dLongitudinal = dVehicleVectorX * dFwdUnitX + dVehicleVectorY * dFwdUnitY;
191 double dLateralX = dVehicleVectorX - dLongitudinal * dFwdUnitX;
192 double dLateralY = dVehicleVectorY - dLongitudinal * dFwdUnitY;
193 double dLateralDistance = std::hypot(dLateralX, dLateralY);
194
195 double dCross = dFwdUnitX * dVehicleVectorY - dFwdUnitY * dVehicleVectorX;
196 double dCrossTrackError = (dCross > 0.0 ? 1.0 : -1.0) * dLateralDistance;
197
198
199 double dTimeWeight = std::exp(-1.5 * static_cast<double>(nIter));
200
201
202 double dModelSpeed = m_UnicycleModel.
GetVelocity();
203 double dSpeed = (dModelSpeed > 0.0) ? dModelSpeed : dMaxSpeed;
204 dSpeed = std::max(dSpeed, 1e-4);
205
206
207 double dStanleyTermRad = std::atan2(m_dControlGain * dCrossTrackError, dSpeed);
208 double dStanleyTermDeg = dStanleyTermRad * (180.0 / M_PI);
209
210 dSteeringAngle += (dStanleyTermDeg + dHeadingError) * dTimeWeight;
211
212
213 dSteeringAngle = std::clamp(dSteeringAngle, -m_dAngularVelocityLimit, m_dAngularVelocityLimit);
214 }
215
216
218
219 return DriveVector{dAbsoluteHeadingGoal, dMaxSpeed};
220 }
void UpdateState(const double dXPosition, const double dYPosition, const double dTheta)
Update the state of the model, given a new position and heading. This method will automatically calcu...
Definition UnicycleModel.hpp:151
double GetVelocity() const
Accessor for the Velocity private member.
Definition UnicycleModel.hpp:300
void Predict(const double dTimeStep, const int nNumPredictions, std::vector< Prediction > &vPredictions)
Accessor for the State private member.
Definition UnicycleModel.hpp:187
geoops::Waypoint FindClosestWaypointInPath(const geoops::UTMCoordinate &stCurrentPosition)
Given the current position of the rover, find the point on the reference path that is closest to the ...
Definition PredictiveStanleyController.cpp:375
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
constexpr T InputAngleModulus(T tValue, T tMinValue, T tMaxValue)
Calculates the modulus of an input angle.
Definition NumberOperations.hpp:165
double GetCompassHeading() const
Accessor for the Compass Heading private member.
Definition GeospatialOperations.hpp:787
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