Run the state machine. Returns the next state.
111 {
112
124 LOG_DEBUG(logging::g_qSharedLogger, "ApproachingMarkerState: Running state-specific behavior.");
125
126
128
129
131 if (stCurrentMeasurement.dDistanceMeters > m_stGoalWaypoint.dRadius)
132 {
133 LOG_WARNING(logging::g_qSharedLogger,
134 "ApproachingMarkerState: Rover broke geofence! Radius threshold is {} m, current distance is {:.2f} m. Triggering MarkerUnseen.",
135 m_stGoalWaypoint.dRadius,
136 stCurrentMeasurement.dDistanceMeters);
137 globals::g_pStateMachineHandler->
HandleEvent(Event::eMarkerUnseen);
138 return;
139 }
140
141
145
146 std::chrono::system_clock::time_point tmCurrentTime = std::chrono::system_clock::now();
147 double dSecondsSinceLastSeen = std::chrono::duration_cast<std::chrono::milliseconds>(tmCurrentTime - m_tmLastSeenTime).count() / 1000.0;
148 bool bMarkerDetected = (stBestArucoTag.nID != -1 || stBestTorchTag.dConfidence != 0.0);
149
150
151 if (dSecondsSinceLastSeen > constants::APPROACH_MARKER_LOST_GIVE_UP_TIME + 5.0)
152 {
153 m_bHasLastGeolocatedPosition = false;
154 }
155
156 std::string szCameraOrigin = "Unknown/None";
157 std::string szTagModelSource = "None";
158 double dCurrentTagYaw = 0.0;
159 double dCurrentTagConf = 0.0;
160
161
162 if (!bMarkerDetected)
163 {
164
165 if (dSecondsSinceLastSeen > constants::APPROACH_MARKER_LOST_GIVE_UP_TIME)
166 {
167 LOG_WARNING(logging::g_qSharedLogger,
168 "ApproachingMarkerState: Tag has been unseen for {:.2f}s (Threshold: {:.2f}s). Giving up and triggering MarkerUnseen.",
169 dSecondsSinceLastSeen,
170 constants::APPROACH_MARKER_LOST_GIVE_UP_TIME);
171 globals::g_pStateMachineHandler->
HandleEvent(Event::eMarkerUnseen);
172 return;
173 }
174
175 if (!m_bAlreadyPrintedLost)
176 {
177 m_bAlreadyPrintedLost = true;
178 LOG_WARNING(logging::g_qSharedLogger, "ApproachingMarkerState: Tracking lost! No valid markers/tags detected across any camera.");
179 }
180
181
182 if (m_bHasLastGeolocatedPosition)
183 {
186 m_dHeadingSetPoint = stLastMeasurement.dStartRelativeBearing;
187 m_dDistanceFromTag = stLastMeasurement.dDistanceMeters;
188
189 if (!m_bAlreadyPrintedVisualLostFallback)
190 {
191 LOG_NOTICE(logging::g_qSharedLogger,
192 "ApproachingMarkerState: [FALLBACK 1] Visual lost. Driving to last known geolocated UTM: [{:.2f}E, {:.2f}N]. Current Dist: {:.2f}m, "
193 "Target Bearing: {:.2f} degrees, Reverse: {}",
196 m_dDistanceFromTag,
197 m_dHeadingSetPoint,
198 m_bDriveBackwards);
199 m_bAlreadyPrintedVisualLostFallback = true;
200 }
201 }
202
203 else if (m_bHasSeenTarget)
204 {
205 if (!m_bAlreadyPrintedVisualLostFallback)
206 {
207 LOG_NOTICE(
208 logging::g_qSharedLogger,
209 "ApproachingMarkerState: [FALLBACK 2] Visual lost & no geolocation history. Coasting along last known heading. Target Bearing: {:.2f} degrees, "
210 "Reverse: {}",
211 m_dHeadingSetPoint,
212 m_bDriveBackwards);
213 m_bAlreadyPrintedVisualLostFallback = true;
214 }
215 }
216
217 else
218 {
220 return;
221 }
222 }
223 else
224 {
225
226 if (m_bAlreadyPrintedLost)
227 {
228 LOG_NOTICE(logging::g_qSharedLogger, "ApproachingMarkerState: Tracking regained! Target marker re-acquired.");
229 }
230
231
232 m_tmLastSeenTime = tmCurrentTime;
233 m_bAlreadyPrintedLost = false;
234 m_bAlreadyPrintedVisualLostFallback = false;
235 m_bHasSeenTarget = true;
236
237 std::shared_ptr<TagDetector> pFrontDetector = globals::g_pTagDetectionHandler->
GetTagDetector(TagDetectionHandler::TagDetectors::eHeadMainCam);
238 std::shared_ptr<TagDetector> pRearDetector = globals::g_pTagDetectionHandler->
GetTagDetector(TagDetectionHandler::TagDetectors::eRearCam);
239
240
241 if (stBestArucoTag.nID != -1)
242 {
243 szTagModelSource = "OpenCV ArUco (ID: " + std::to_string(stBestArucoTag.nID) + ")";
244 dCurrentTagYaw = stBestArucoTag.dYawAngle;
245 dCurrentTagConf = stBestArucoTag.dConfidence;
246
247 if (pFrontDetector != nullptr && stBestArucoTag.szDetectorUUID == pFrontDetector->GetThreadUUID())
248 {
249 m_bDriveBackwards = false;
250 szCameraOrigin = "Head Main Camera";
251 }
252 else if (pRearDetector != nullptr && stBestArucoTag.szDetectorUUID == pRearDetector->GetThreadUUID())
253 {
254 m_bDriveBackwards = true;
255 szCameraOrigin = "Rear Camera";
256 }
257 else
258 {
259 szCameraOrigin = "Unknown Camera";
260 }
261
262 m_dDistanceFromTag = stBestArucoTag.dStraightLineDistance;
263
264
265 if (stBestArucoTag.stGeolocatedPosition.eType != geoops::WaypointType::eUNKNOWN && m_dDistanceFromTag > 0.0)
266 {
269 m_dHeadingSetPoint = stTagMeasurement.dStartRelativeBearing;
270 m_stLastGeolocatedPosition = stBestArucoTag.stGeolocatedPosition;
271 m_bHasLastGeolocatedPosition = true;
272 }
273 else
274 {
275
276 double dVisionYawOffset = stBestArucoTag.dYawAngle;
277 if (m_bDriveBackwards)
278 {
279 dVisionYawOffset += 180.0;
280 }
282 }
283 }
284 else if (stBestTorchTag.dConfidence != 0.0)
285 {
286 szTagModelSource = "ML Torch Model";
287 dCurrentTagYaw = stBestTorchTag.dYawAngle;
288 dCurrentTagConf = stBestTorchTag.dConfidence;
289
290 if (pFrontDetector != nullptr && stBestTorchTag.szDetectorUUID == pFrontDetector->GetThreadUUID())
291 {
292 m_bDriveBackwards = false;
293 szCameraOrigin = "Head Main Camera";
294 }
295 else if (pRearDetector != nullptr && stBestTorchTag.szDetectorUUID == pRearDetector->GetThreadUUID())
296 {
297 m_bDriveBackwards = true;
298 szCameraOrigin = "Rear Camera";
299 }
300 else
301 {
302 szCameraOrigin = "Unknown Camera";
303 }
304
305 m_dDistanceFromTag = stBestTorchTag.dStraightLineDistance;
306
307
308 if (stBestTorchTag.stGeolocatedPosition.eType != geoops::WaypointType::eUNKNOWN && m_dDistanceFromTag > 0.0)
309 {
312 m_dHeadingSetPoint = stTagMeasurement.dStartRelativeBearing;
313 m_stLastGeolocatedPosition = stBestTorchTag.stGeolocatedPosition;
314 m_bHasLastGeolocatedPosition = true;
315 }
316 else
317 {
318
319 double dVisionYawOffset = stBestTorchTag.dYawAngle;
320 if (m_bDriveBackwards)
321 {
322 dVisionYawOffset += 180.0;
323 }
325 }
326 }
327 }
328
329
331 m_dHeadingSetPoint,
333 diffdrive::DifferentialControlMethod::eArcadeDrive,
334 m_bDriveBackwards,
335 false,
336 false,
337 false);
338 globals::g_pDriveBoard->
SendDrive(stDrivePowers);
339
340
341 if (std::chrono::duration_cast<std::chrono::seconds>(tmCurrentTime - m_tmLastLogTime).count() >= 1)
342 {
343 m_tmLastLogTime = tmCurrentTime;
344
345 if (bMarkerDetected)
346 {
347 std::string szTrackingType = (m_bHasLastGeolocatedPosition && m_dDistanceFromTag > 0.0) ? "Absolute (GPS Geolocation)" : "Relative (Vision Yaw Angle)";
348
349 LOG_NOTICE(logging::g_qSharedLogger,
350 "ApproachingMarkerState Status:\n"
351 " >> Detection : Source: {}, Camera: {}\n"
352 " >> Target : Conf: {:.2f}%, Dist: {:.2f}m, Yaw: {:.2f} degrees\n"
353 " >> Tracking : Mode: {}, Reverse: {}\n"
354 " >> Navigation : Curr Hdg: {:.2f} degrees, Tgt Hdg: {:.2f} degrees",
355 szTagModelSource,
356 szCameraOrigin,
357 dCurrentTagConf * 100.0,
358 m_dDistanceFromTag,
359 dCurrentTagYaw,
360 szTrackingType,
361 m_bDriveBackwards ? "TRUE" : "FALSE",
362 stCurrentRoverPose.GetCompassHeading(),
363 m_dHeadingSetPoint);
364 }
365 else
366 {
367 LOG_NOTICE(logging::g_qSharedLogger,
368 "ApproachingMarkerState Status: Marker not visible. Executing fallback logic. Target Hdg: {:.2f} degrees, Reverse: {}, Est. Dist: {:.2f}m",
369 m_dHeadingSetPoint,
370 m_bDriveBackwards,
371 m_dDistanceFromTag);
372 }
373 }
374
375
376 if (m_dDistanceFromTag != 0.0 && m_dDistanceFromTag < constants::APPROACH_MARKER_PROXIMITY_THRESHOLD)
377 {
378 LOG_NOTICE(logging::g_qSharedLogger,
379 "ApproachingMarkerState: SUCCESS! Rover has reached the target marker! (Distance: {:.2f}m < Threshold: {:.2f}m)",
380 m_dDistanceFromTag,
381 constants::APPROACH_MARKER_PROXIMITY_THRESHOLD);
382
383 globals::g_pStateMachineHandler->
HandleEvent(Event::eReachedMarker,
true);
384 return;
385 }
386
387
388 if (constants::APPROACH_MARKER_ENABLE_STUCK_DETECT &&
389 m_StuckDetector.
CheckIfStuck(globals::g_pStateMachineHandler->SmartRetrieveVelocity(),
390 globals::g_pStateMachineHandler->SmartRetrieveAngularVelocity(),
391 constants::APPROACH_MARKER_STUCK_CHECK_VEL_THRESH * globals::g_pDriveBoard->GetMaxDriveEffort(),
392 constants::APPROACH_MARKER_STUCK_CHECK_ROT_THRESH))
393 {
394 LOG_WARNING(logging::g_qSharedLogger,
395 "ApproachingMarkerState: Rover has become stuck! Triggering Stuck event. Curr Vel: {:.2f}, Ang Vel: {:.2f}",
396 globals::g_pStateMachineHandler->SmartRetrieveVelocity(),
397 globals::g_pStateMachineHandler->SmartRetrieveAngularVelocity());
398 globals::g_pStateMachineHandler->
HandleEvent(Event::eStuck,
true);
399 return;
400 }
401 }
void SendDrive(const diffdrive::DrivePowers &stDrivePowers, const bool bEnableVariableDriveEffort=true)
Sets the left and right drive powers of the drive board.
Definition DriveBoard.cpp:166
diffdrive::DrivePowers CalculateMove(const double dGoalSpeed, const double dGoalHeading, const double dActualHeading, const diffdrive::DifferentialControlMethod eKinematicsMethod=diffdrive::DifferentialControlMethod::eArcadeDrive, const bool bDriveBackwards=false, const bool bAlwaysProgressForward=false, const bool bSquareControlInput=false, const bool bCurvatureDriveAllowTurningWhileStopped=true)
This method determines drive powers to make the Rover drive towards a given heading at a given speed.
Definition DriveBoard.cpp:89
void SendStop()
Stop the drivetrain of the Rover.
Definition DriveBoard.cpp:246
geoops::RoverPose SmartRetrieveRoverPose(bool bIMUHeading=true)
This method is used to retrieve the rover's current position and heading. It uses the GPS data from t...
Definition StateMachineHandler.cpp:375
void HandleEvent(statemachine::Event eEvent, const bool bSaveCurrentState=false)
This method Handles Events that are passed to the State Machine Handler. It will check the current st...
Definition StateMachineHandler.cpp:285
bool CheckIfStuck(double dCurrentVelocity, double dCurrentAngularVelocity, double dVelocityThreshold=0.1, double dAngularVelocityThreshold=0.1)
Checks if the rover meets stuck criteria based in the given parameters.
Definition StuckDetection.hpp:96
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
int IdentifyTargetMarker(const std::vector< std::shared_ptr< TagDetector > > &vTagDetectors, tagdetectutils::ArucoTag &stArucoTarget, tagdetectutils::ArucoTag &stTorchTarget, const int nTargetTagID=static_cast< int >(manifest::Autonomy::AUTONOMYWAYPOINTTYPES::ANY))
Identify a target marker in the rover's vision, using OpenCV detection.
Definition TagDetectionChecker.hpp:100
This struct is used to store the left and right drive powers for the robot. Storing these values in a...
Definition DifferentialDrive.hpp:73
This struct is used to store the distance, arc length, and relative bearing for a calculated geodesic...
Definition GeospatialOperations.hpp:83
This struct is used by the WaypointHandler to provide an easy way to store all pose data about the ro...
Definition GeospatialOperations.hpp:708
const geoops::GPSCoordinate & GetGPSCoordinate() const
Accessor for the geoops::GPSCoordinate member variable.
Definition GeospatialOperations.hpp:756
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
const geoops::UTMCoordinate & GetUTMCoordinate() const
Accessor for the geoops::UTMCoordinate member variable.
Definition GeospatialOperations.hpp:508
Represents a single ArUco tag.
Definition TagDetectionUtilty.hpp:57