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
95 if (stClosestPointResult.second >= 0)
96 {
97 m_nCurrentReferencePathTargetIndex = stClosestPointResult.second;
98 }
99
100
101
102
103 if (m_nCurrentReferencePathTargetIndex >= static_cast<int>(m_vReferencePath.size()) - 2)
104 {
105
107 geoops::UTMCoordinate stSecondToLastPoint = m_vReferencePath[m_vReferencePath.size() - 2].GetUTMCoordinate();
109
110
111 double dSegmentX = stLastPoint.dEasting - stSecondToLastPoint.dEasting;
112 double dSegmentY = stLastPoint.dNorthing - stSecondToLastPoint.dNorthing;
113 double dSegmentLenSq = dSegmentX * dSegmentX + dSegmentY * dSegmentY;
114
115
116 double dRoverVectorX = stRoverPos.dEasting - stSecondToLastPoint.dEasting;
117 double dRoverVectorY = stRoverPos.dNorthing - stSecondToLastPoint.dNorthing;
118
119
120
121 double dNormalDistance = 0.0;
122 if (dSegmentLenSq > 1e-6)
123 {
124 dNormalDistance = (dRoverVectorX * dSegmentX + dRoverVectorY * dSegmentY) / dSegmentLenSq;
125 }
126
127
128
129 if (dNormalDistance >= 1.0)
130 {
131
132
134 return DriveVector{dHeadingToLastWaypoint, 1.0};
135 }
136 }
137
138
140
141 m_UnicycleModel.
Predict(m_dPredictionTimeStep, m_nPredictionHorizon, vPredictions);
142
143
144 int nPredSearchIndex = m_nCurrentReferencePathTargetIndex;
145
146
147 for (size_t nIter = 0; nIter < vPredictions.size(); ++nIter)
148 {
149
150 double dPredictedXPosition = vPredictions[nIter].dXPosition;
151 double dPredictedYPosition = vPredictions[nIter].dYPosition;
152 double dPredictedTheta = vPredictions[nIter].dTheta;
153
154
156 stPredictedPosition.dEasting = dPredictedXPosition;
157 stPredictedPosition.dNorthing = dPredictedYPosition;
158
159
160 std::pair<geoops::Waypoint, int> stdClosestPointResult =
FindClosestWaypointInPath(stPredictedPosition, nPredSearchIndex);
162 int nBestIndex = stdClosestPointResult.second;
163 if (nBestIndex >= 0)
164 {
165 nPredSearchIndex = nBestIndex;
166 }
167
168
169 int nIdx = nPredSearchIndex;
170 int nNextIdx = std::min(nIdx + 1, static_cast<int>(m_vReferencePath.size() - 1));
173
174
175 double dForwardVectorX = stSegmentEnd.dEasting - stSegmentStart.dEasting;
176 double dForwardVectorY = stSegmentEnd.dNorthing - stSegmentStart.dNorthing;
177 double dForwardNorm = std::hypot(dForwardVectorX, dForwardVectorY);
178
179 if (dForwardNorm < 1e-9)
180 {
181 continue;
182 }
183
184
185 double dFwdUnitX = dForwardVectorX / dForwardNorm;
186 double dFwdUnitY = dForwardVectorY / dForwardNorm;
187
188 double dSegmentMathDeg = std::atan2(dForwardVectorY, dForwardVectorX) * (180.0 / M_PI);
189
190
191 double dPathHeadingDeg = std::fmod(90.0 - dSegmentMathDeg + 360.0, 360.0);
192
193 double dPredThetaDeg = dPredictedTheta;
194
195 std::function<double(double, double)> AngleDiffDeg = [](double targetDeg, double sourceDeg) -> double
196 {
197 double diff = std::fmod(targetDeg - sourceDeg + 540.0, 360.0) - 180.0;
198 return diff;
199 };
200
201 double dHeadingError = AngleDiffDeg(dPathHeadingDeg, dPredThetaDeg);
202
203
204 double dVehicleVectorX = dPredictedXPosition - stClosestWaypoint.
GetUTMCoordinate().dEasting;
205 double dVehicleVectorY = dPredictedYPosition - stClosestWaypoint.
GetUTMCoordinate().dNorthing;
206
207 double dLongitudinal = dVehicleVectorX * dFwdUnitX + dVehicleVectorY * dFwdUnitY;
208 double dLateralX = dVehicleVectorX - dLongitudinal * dFwdUnitX;
209 double dLateralY = dVehicleVectorY - dLongitudinal * dFwdUnitY;
210 double dLateralDistance = std::hypot(dLateralX, dLateralY);
211
212 double dCross = dFwdUnitX * dVehicleVectorY - dFwdUnitY * dVehicleVectorX;
213 double dCrossTrackError = (dCross > 0.0 ? 1.0 : -1.0) * dLateralDistance;
214
215
216 double dTimeWeight = std::exp(-1.5 * static_cast<double>(nIter));
217
218
219 double dModelSpeed = m_UnicycleModel.
GetVelocity();
220 double dSpeed = (dModelSpeed > 0.0) ? dModelSpeed : dMaxSpeed;
221 dSpeed = std::max(dSpeed, 1e-4);
222
223
224
225 double dEffectiveSpeed = std::max(dSpeed, constants::STANLEY_MIN_STABLE_SPEED);
226
227 double dStanleyTermRad = std::atan2(m_dControlGain * dCrossTrackError, dEffectiveSpeed);
228 double dStanleyTermDeg = dStanleyTermRad * (180.0 / M_PI);
229
230 dSteeringAngle += (dStanleyTermDeg + dHeadingError) * dTimeWeight;
231
232
233 dSteeringAngle = std::clamp(dSteeringAngle, -m_dAngularVelocityLimit, m_dAngularVelocityLimit);
234 }
235
236
238
239 return DriveVector{dAbsoluteHeadingGoal, dMaxSpeed};
240 }
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
std::pair< geoops::Waypoint, int > FindClosestWaypointInPath(const geoops::UTMCoordinate &stCurrentPosition, const int nStartIndex) const
Given a position, find the point on the reference path that is closest. Returns both the mapped waypo...
Definition PredictiveStanleyController.cpp:396
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:162
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