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
TagDetector Class Reference

Run's Aruco detection & camera pose estimation in a multithreading environment. Given a camera, tag detection using OpenCV's ArUco library and a custom trained model for detecting general tags will be continuously ran on the camera frames. More...

#include <TagDetector.h>

Inheritance diagram for TagDetector:
Collaboration diagram for TagDetector:

Public Member Functions

 TagDetector (std::shared_ptr< BasicCamera > pBasicCam, const int nArucoCornerRefinementMaxIterations=30, const int nArucoCornerRefinementMethod=cv::aruco::CORNER_REFINE_NONE, const int nArucoMarkerBorderBits=1, const bool bArucoDetectInvertedMarkers=false, const bool bUseAruco3Detection=false, const bool bEnableTracking=false, const int nDetectorMaxFPS=30, const bool bEnableRecordingFlag=false, const int nNumDetectedTagsRetrievalThreads=5, const bool bUsingGpuMats=false)
 Construct a new TagDetector object.
 
 TagDetector (std::shared_ptr< ZEDCamera > pZEDCam, const int nArucoCornerRefinementMaxIterations=30, const int nArucoCornerRefinementMethod=cv::aruco::CORNER_REFINE_NONE, const int nArucoMarkerBorderBits=1, const bool bArucoDetectInvertedMarkers=false, const bool bUseAruco3Detection=false, const bool bEnableTracking=false, const int nDetectorMaxFPS=30, const bool bEnableRecordingFlag=false, const int nNumDetectedTagsRetrievalThreads=5, const bool bUsingGpuMats=false)
 Construct a new TagDetector object.
 
 ~TagDetector ()
 Destroy the Tag Detector:: Tag Detector object.
 
std::future< bool > RequestDetectionOverlayFrame (cv::Mat &cvFrame)
 Request a copy of a frame containing the tag detection overlays from the aruco and torch library.
 
std::future< bool > RequestLastGoodOverlayFrame (cv::Mat &cvFrame)
 Request a copy of a frame containing the last good tag detection overlays from the aruco and torch library.
 
std::future< bool > RequestDetectedArucoTags (std::vector< tagdetectutils::ArucoTag > &vArucoTags)
 Request the most up to date vector of detected tags from OpenCV's Aruco algorithm.
 
bool InitTorchDetection (const std::string &szModelPath, yolomodel::pytorch::PyTorchInterpreter::HardwareDevices eDevice=yolomodel::pytorch::PyTorchInterpreter::HardwareDevices::eCUDA)
 Attempt to open the next available Torch hardware and load model at the given path onto the device.
 
void EnableTorchDetection (const float fMinObjectConfidence=0.4f, const float fNMSThreshold=0.6f)
 Turn on torch detection with given parameters.
 
void DisableTorchDetection ()
 Set flag to stop tag detection with the torch model.
 
void SetDetectorMaxFPS (const int nRecordingFPS)
 Mutator for the desired max FPS for this detector.
 
void SetEnableRecordingFlag (const bool bEnableRecordingFlag)
 Mutator for the Enable Recording Flag private member.
 
bool GetIsReady ()
 Accessor for the status of this TagDetector.
 
int GetDetectorMaxFPS () const
 Accessor for the desired max FPS for this detector.
 
bool GetEnableRecordingFlag () const
 Accessor for the Enable Recording Flag private member.
 
std::string GetCameraName ()
 Accessor for the camera name or path that this TagDetector is tied to.
 
cv::Size GetProcessFrameResolution () const
 Accessor for the resolution of the process image used for tag detection.
 
- Public Member Functions inherited from AutonomyThread< void >
 AutonomyThread ()
 Construct a new Autonomy Thread object.
 
virtual ~AutonomyThread ()
 Destroy the Autonomy Thread object. If the parent object or main thread is destroyed or exited while this thread is still running, a race condition will occur. Stopping and joining the thread here insures that the main program can't exit if the user forgot to stop and join the thread.
 
void Start ()
 When this method is called, it starts a new thread that runs the code within the ThreadedContinuousCode method. This is the users main code that will run the important and continuous code for the class.
 
void RequestStop ()
 Signals threads to stop executing user code, terminate. DOES NOT JOIN. This method will not force the thread to exit, if the user code is not written properly and contains WHILE statement or any other long-executing or blocking code, then the thread will not exit until the next iteration.
 
void Join ()
 Waits for thread to finish executing and then closes thread. This method will block the calling code until thread is finished.
 
bool Joinable () const
 Check if the code within the thread and all pools created by it are finished executing and the thread is ready to be closed.
 
AutonomyThreadState GetThreadState () const
 Accessor for the Threads State private member.
 
std::string GetThreadUUID () const
 Accessor for the Thread U U I D private member.
 
IPSGetIPS ()
 Accessor for the Frame I P S private member.
 
void SetMainThreadPriority (AutonomyThreadPriority ePriority)
 Set the OS priority for the main continuous thread.
 
void SetPoolThreadPriority (AutonomyThreadPriority ePriority)
 Set the OS priority for the highly parallelized pool threads.
 

Private Member Functions

void ThreadedContinuousCode () override
 This code will run continuously in a separate thread. New frames from the given camera are grabbed and the tags for the camera image are detected, filtered, and stored. Then any requests for the current tags are fulfilled.
 
void PooledLinearCode () override
 This method holds the code that is ran in the thread pool started by the ThreadedLinearCode() method. It copies the data from the different data objects to references of the same type stored in a queue filled by the Request methods.
 
void UpdateDetectedTags (std::vector< tagdetectutils::ArucoTag > &vNewlyDetectedTags)
 Updates the detected torch tags including tracking the detected tags over time and removing tags that haven't been seen for long enough.
 

Private Attributes

std::shared_ptr< Camera< cv::Mat > > m_pCamera
 
cv::aruco::ArucoDetector m_cvArucoDetector
 
cv::aruco::DetectorParameters m_cvArucoDetectionParams
 
cv::aruco::Dictionary m_cvTagDictionary
 
std::shared_ptr< yolomodel::pytorch::PyTorchInterpreterm_pTorchDetector
 
std::atomic< float > m_fTorchMinObjectConfidence
 
std::atomic< float > m_fTorchNMSThreshold
 
std::atomic_bool m_bTorchInitialized
 
std::atomic_bool m_bTorchEnabled
 
std::shared_ptr< tracking::MultiTrackerm_pMultiTracker
 
bool m_bUsingZedCamera
 
bool m_bUsingGpuMats
 
bool m_bCameraIsOpened
 
bool m_bEnableTracking
 
int m_nNumDetectedTagsRetrievalThreads
 
std::string m_szCameraName
 
std::atomic_bool m_bEnableRecordingFlag
 
std::vector< tagdetectutils::ArucoTagm_vNewlyDetectedTags
 
std::vector< tagdetectutils::ArucoTagm_vDetectedArucoTags
 
geoops::RoverPose m_stRoverPose
 
cv::Mat m_cvFrame
 
cv::cuda::GpuMat m_cvGPUFrame
 
cv::Mat m_cvArucoProcFrame
 
cv::Mat m_cvLastGoodDetectionOverlayFrame
 
cv::Mat m_cvPointCloud
 
cv::cuda::GpuMat m_cvGPUPointCloud
 
std::queue< containers::FrameFetchContainer< cv::Mat > > m_qDetectionOverlayFramesCopySchedule
 
std::queue< containers::FrameFetchContainer< cv::Mat > > m_qLastGoodDetectionOverlayFramesCopySchedule
 
std::queue< containers::DataFetchContainer< std::vector< tagdetectutils::ArucoTag > > > m_qDetectedArucoTagCopySchedule
 
std::shared_mutex m_muPoolScheduleMutex
 
std::shared_mutex m_muDetectionOverlayCopyMutex
 
std::shared_mutex m_muLastGoodDetectionOverlayCopyMutex
 
std::shared_mutex m_muArucoDataCopyMutex
 

Additional Inherited Members

- Public Types inherited from AutonomyThread< void >
enum  AutonomyThreadState
 
enum  AutonomyThreadPriority
 
- Protected Member Functions inherited from AutonomyThread< void >
void RunPool (const unsigned int nNumTasksToQueue, const unsigned int nNumThreads=2, const bool bForceStopCurrentThreads=false)
 When this method is called, it starts/adds tasks to a thread pool that runs nNumTasksToQueue copies of the code within the PooledLinearCode() method using nNumThreads number of threads. This is meant to be used as an internal utility of the child class to further improve parallelization. Default value for nNumThreads is 2.
 
void RunDetachedPool (const unsigned int nNumTasksToQueue, const unsigned int nNumThreads=2, const bool bForceStopCurrentThreads=false)
 When this method is called, it starts a thread pool full of threads that don't return std::futures (like a placeholder for the thread return type). This means the thread will not have a return type and there is no way to determine if the thread has finished other than calling the Join() method. Only use this if you want to 'set and forget'. It will be faster as it doesn't return futures. Runs PooledLinearCode() method code. This is meant to be used as an internal utility of the child class to further improve parallelization.
 
void ParallelizeLoop (const int nNumThreads, const N tTotalIterations, F &&tLoopFunction)
 Given a ref-qualified looping function and an arbitrary number of iterations, this method will divide up the loop and run each section in a thread pool. This function must not return anything. This method will block until the loop has completed.
 
void ClearPoolQueue ()
 Clears any tasks waiting to be ran in the queue, tasks currently running will remain running.
 
void JoinPool ()
 Waits for pool to finish executing tasks. This method will block the calling code until thread is finished.
 
bool PoolJoinable () const
 Check if the internal pool threads are done executing code and the queue is empty.
 
void SetMainThreadIPSLimit (int nMaxIterationsPerSecond=0)
 Mutator for the Main Thread Max I P S private member.
 
int GetPoolNumOfThreads ()
 Accessor for the Pool Num Of Threads private member.
 
int GetPoolQueueLength ()
 Accessor for the Pool Queue Size private member.
 
std::vector< void > GetPoolResults ()
 Accessor for the Pool Results private member. The action of getting results will destroy and remove them from this object. This method blocks if the thread is not finished, so no need to call JoinPool() before getting results.
 
int GetMainThreadMaxIPS () const
 Accessor for the Main Thread Max I P S private member.
 
- Protected Attributes inherited from AutonomyThread< void >
IPS m_IPS
 

Detailed Description

Run's Aruco detection & camera pose estimation in a multithreading environment. Given a camera, tag detection using OpenCV's ArUco library and a custom trained model for detecting general tags will be continuously ran on the camera frames.

What are the threads doing? Continuous Thread: In this thread we are constantly getting images and depth maps from the necessary cameras. We then detect the tags in the image and estimate their location with respect to the rover. Pooled Threads: Copy the vector of detected tags to all of the threads requesting it through the RequestDetectedArucoTags(...) function.

Author
jspencerpittman (jspen.nosp@m.cerp.nosp@m.ittma.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om), clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-09-30

Constructor & Destructor Documentation

◆ TagDetector() [1/2]

TagDetector::TagDetector ( std::shared_ptr< BasicCamera pBasicCam,
const int  nArucoCornerRefinementMaxIterations = 30,
const int  nArucoCornerRefinementMethod = cv::aruco::CORNER_REFINE_NONE,
const int  nArucoMarkerBorderBits = 1,
const bool  bArucoDetectInvertedMarkers = false,
const bool  bUseAruco3Detection = false,
const bool  bEnableTracking = false,
const int  nDetectorMaxFPS = 30,
const bool  bEnableRecordingFlag = false,
const int  nNumDetectedTagsRetrievalThreads = 5,
const bool  bUsingGpuMats = false 
)

Construct a new TagDetector object.

Parameters
pBasicCam- A pointer to the BasicCam camera to get frames from for detection.
nArucoCornerRefinementMaxIterations- The number of iterations to use when refining marker corners.
nArucoCornerRefinementMethod- The refinement method to use.
nArucoMarkerBorderBits- The number of border unit squares around the marker.
bArucoDetectInvertedMarkers- Enable or disable upside-down marker detection.
bUseAruco3Detection- Whether or not to use the newer/faster method of detection. Experimental.
bEnableTracking- Whether or not to enable tracking of detected tags.
nDetectorMaxFPS- The max FPS limit the detector can run at.
bEnableRecordingFlag- Whether or not this TagDetector's overlay output should be recorded.
nNumDetectedTagsRetrievalThreads- The number of threads to use when fulfilling requests for the detected aruco tags. Default is 5.
bUsingGpuMats- Whether or not the given camera name will be using GpuMats.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-10-10
48{
49 // Initialize member variables.
50 m_pCamera = pBasicCam;
51 m_bTorchInitialized = false;
52 m_bTorchEnabled = false;
53 m_bEnableTracking = bEnableTracking;
54 m_bUsingZedCamera = false; // Toggle ZED functions off.
55 m_bUsingGpuMats = bUsingGpuMats;
56 m_bCameraIsOpened = false;
57 m_nNumDetectedTagsRetrievalThreads = nNumDetectedTagsRetrievalThreads;
58 m_szCameraName = pBasicCam->GetCameraLocation();
59 m_bEnableRecordingFlag = bEnableRecordingFlag;
60 m_stRoverPose = geoops::RoverPose();
61
62 // Setup aruco detector params.
63 m_cvArucoDetectionParams = cv::aruco::DetectorParameters();
64 m_cvArucoDetectionParams.cornerRefinementMaxIterations = nArucoCornerRefinementMaxIterations;
65 m_cvArucoDetectionParams.cornerRefinementMethod = nArucoCornerRefinementMethod;
66 m_cvArucoDetectionParams.markerBorderBits = nArucoMarkerBorderBits;
67 m_cvArucoDetectionParams.detectInvertedMarker = bArucoDetectInvertedMarkers;
68 m_cvArucoDetectionParams.useAruco3Detection = bUseAruco3Detection;
69 // Get aruco dictionary and initialize aruco detector.
70 m_cvTagDictionary = cv::aruco::getPredefinedDictionary(constants::ARUCO_DICTIONARY);
71 m_cvArucoDetector = cv::aruco::ArucoDetector(m_cvTagDictionary, m_cvArucoDetectionParams);
72
73 // Create a multi-tracker for tracking multiple tags from the torch detectors.
74 m_pMultiTracker = std::make_shared<tracking::MultiTracker>(constants::BBOX_TRACKER_LOST_TIMEOUT,
75 constants::BBOX_TRACKER_MAX_TRACK_TIME,
76 constants::BBOX_TRACKER_IOU_MATCH_THRESHOLD);
77
78 // Set max IPS of main thread.
79 this->SetMainThreadIPSLimit(nDetectorMaxFPS);
80
81 // Submit logger message.
82 LOG_INFO(logging::g_qSharedLogger, "TagDetector created for camera at path/index: {}", m_szCameraName);
83}
void SetMainThreadIPSLimit(int nMaxIterationsPerSecond=0)
Mutator for the Main Thread Max I P S private member.
Definition AutonomyThread.hpp:530
Dictionary getPredefinedDictionary(PredefinedDictionaryType name)
CornerRefineMethod cornerRefinementMethod
This struct is used by the WaypointHandler to provide an easy way to store all pose data about the ro...
Definition GeospatialOperations.hpp:708
Here is the call graph for this function:

◆ TagDetector() [2/2]

TagDetector::TagDetector ( std::shared_ptr< ZEDCamera pZEDCam,
const int  nArucoCornerRefinementMaxIterations = 30,
const int  nArucoCornerRefinementMethod = cv::aruco::CORNER_REFINE_NONE,
const int  nArucoMarkerBorderBits = 1,
const bool  bArucoDetectInvertedMarkers = false,
const bool  bUseAruco3Detection = false,
const bool  bEnableTracking = false,
const int  nDetectorMaxFPS = 30,
const bool  bEnableRecordingFlag = false,
const int  nNumDetectedTagsRetrievalThreads = 5,
const bool  bUsingGpuMats = false 
)

Construct a new TagDetector object.

Parameters
pZEDCam- A pointer to the ZEDCam camera to get frames from for detection. Override for ZED camera.
nArucoCornerRefinementMaxIterations- The number of iterations to use when refining marker corners.
nArucoCornerRefinementMethod- The refinement method to use.
nArucoMarkerBorderBits- The number of border unit squares around the marker.
bArucoDetectInvertedMarkers- Enable or disable upside-down marker detection.
bUseAruco3Detection- Whether or not to use the newer/faster method of detection. Experimental.
bEnableTracking- Whether or not to enable tracking of detected tags.
nDetectorMaxFPS- The max FPS limit the detector can run at.
bEnableRecordingFlag- Whether or not this TagDetector's overlay output should be recorded.
nNumDetectedTagsRetrievalThreads- The number of threads to use when fulfilling requests for the detected aruco tags. Default is 5.
bUsingGpuMats- Whether or not the given camera name will be using GpuMats.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-10-07
115{
116 // Initialize member variables.
117 m_pCamera = pZEDCam;
118 m_bTorchInitialized = false;
119 m_bTorchEnabled = false;
120 m_bUsingZedCamera = true; // Toggle ZED functions on.
121 m_bEnableTracking = bEnableTracking;
122 m_bUsingGpuMats = bUsingGpuMats;
123 m_bCameraIsOpened = false;
124 m_nNumDetectedTagsRetrievalThreads = nNumDetectedTagsRetrievalThreads;
125 m_szCameraName = pZEDCam->GetCameraModel() + "_" + std::to_string(pZEDCam->GetCameraSerial());
126 m_bEnableRecordingFlag = bEnableRecordingFlag;
127 m_IPS = IPS();
128 m_stRoverPose = geoops::RoverPose();
129
130 // Setup aruco detector params.
131 m_cvArucoDetectionParams = cv::aruco::DetectorParameters();
132 m_cvArucoDetectionParams.cornerRefinementMaxIterations = nArucoCornerRefinementMaxIterations;
133 m_cvArucoDetectionParams.cornerRefinementMethod = nArucoCornerRefinementMethod;
134 m_cvArucoDetectionParams.markerBorderBits = nArucoMarkerBorderBits;
135 m_cvArucoDetectionParams.detectInvertedMarker = bArucoDetectInvertedMarkers;
136 m_cvArucoDetectionParams.useAruco3Detection = bUseAruco3Detection;
137
138 // Create a multi-tracker for tracking multiple tags from the torch detectors.
139 m_pMultiTracker = std::make_shared<tracking::MultiTracker>(constants::BBOX_TRACKER_LOST_TIMEOUT,
140 constants::BBOX_TRACKER_IOU_MATCH_THRESHOLD,
141 constants::BBOX_TRACKER_IOU_MATCH_THRESHOLD);
142
143 // Set max IPS of main thread.
144 this->SetMainThreadIPSLimit(nDetectorMaxFPS);
145
146 // Submit logger message.
147 LOG_INFO(logging::g_qSharedLogger, "TagDetector created for camera: {}", m_szCameraName);
148}
This util class provides an easy way to keep track of iterations per second for any body of code.
Definition IPS.hpp:30
Here is the call graph for this function:

◆ ~TagDetector()

TagDetector::~TagDetector ( )

Destroy the Tag Detector:: Tag Detector object.

Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2024-01-09
158{
159 // Stop threaded code.
160 this->RequestStop();
161 this->Join();
162
163 // Submit logger message.
164 LOG_INFO(logging::g_qSharedLogger, "TagDetector for camera {} has been successfully destroyed.", this->GetCameraName());
165}
void Join()
Waits for thread to finish executing and then closes thread. This method will block the calling code ...
Definition AutonomyThread.hpp:203
void RequestStop()
Signals threads to stop executing user code, terminate. DOES NOT JOIN. This method will not force the...
Definition AutonomyThread.hpp:187
std::string GetCameraName()
Accessor for the camera name or path that this TagDetector is tied to.
Definition TagDetector.cpp:750
Here is the call graph for this function:

Member Function Documentation

◆ RequestDetectionOverlayFrame()

std::future< bool > TagDetector::RequestDetectionOverlayFrame ( cv::Mat cvFrame)

Request a copy of a frame containing the tag detection overlays from the aruco and torch library.

Parameters
cvFrame- The frame to copy the detection overlay image to.
Returns
std::future<bool> - The future that should be waited on before using the passed in frame. Future will be true or false based on whether or not the frame was successfully retrieved.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-10-11
497{
498 // Assemble the DataFetchContainer.
499 containers::FrameFetchContainer<cv::Mat> stContainer(cvFrame, PIXEL_FORMATS::eArucoDetection);
500
501 // Acquire lock on pool copy queue.
502 std::unique_lock<std::shared_mutex> lkScheduler(m_muPoolScheduleMutex);
503 // Append frame fetch container to the schedule queue.
504 m_qDetectionOverlayFramesCopySchedule.push(stContainer);
505 // Release lock on the frame schedule queue.
506 lkScheduler.unlock();
507
508 // Return the future from the promise stored in the container.
509 return stContainer.pCopiedFrameStatus->get_future();
510}
This struct is used to carry references to camera frames for scheduling and copying....
Definition FetchContainers.hpp:88

◆ RequestLastGoodOverlayFrame()

std::future< bool > TagDetector::RequestLastGoodOverlayFrame ( cv::Mat cvFrame)

Request a copy of a frame containing the last good tag detection overlays from the aruco and torch library.

Parameters
cvFrame- The frame to copy the detection overlay image to.
Returns
std::future<bool> - The future that should be waited on before using the passed in frame. Future will be true or false based on whether or not the frame was successfully retrieved.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-10-11
524{
525 // Assemble the DataFetchContainer.
526 containers::FrameFetchContainer<cv::Mat> stContainer(cvFrame, PIXEL_FORMATS::eArucoDetection);
527
528 // Acquire lock on pool copy queue.
529 std::unique_lock<std::shared_mutex> lkScheduler(m_muPoolScheduleMutex);
530 // Append frame fetch container to the schedule queue.
531 m_qLastGoodDetectionOverlayFramesCopySchedule.push(stContainer);
532 // Release lock on the frame schedule queue.
533 lkScheduler.unlock();
534
535 // Return the future from the promise stored in the container.
536 return stContainer.pCopiedFrameStatus->get_future();
537}

◆ RequestDetectedArucoTags()

std::future< bool > TagDetector::RequestDetectedArucoTags ( std::vector< tagdetectutils::ArucoTag > &  vArucoTags)

Request the most up to date vector of detected tags from OpenCV's Aruco algorithm.

Parameters
vArucoTags- The vector the detected aruco tags will be saved to.
Returns
std::future<bool> - The future that should be waited on before using the passed in tag vector. Future will be true or false based on whether or not the tags were successfully retrieved.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-10-07
551{
552 // Assemble the DataFetchContainer.
554
555 // Acquire lock on pool copy queue.
556 std::unique_lock<std::shared_mutex> lkScheduler(m_muPoolScheduleMutex);
557 // Append detected tag fetch container to the schedule queue.
558 m_qDetectedArucoTagCopySchedule.push(stContainer);
559 // Release lock on the frame schedule queue.
560 lkScheduler.unlock();
561
562 // Return the future from the promise stored in the container.
563 return stContainer.pCopiedDataStatus->get_future();
564}
This struct is used to carry references to any datatype for scheduling and copying....
Definition FetchContainers.hpp:164

◆ InitTorchDetection()

bool TagDetector::InitTorchDetection ( const std::string &  szModelPath,
yolomodel::pytorch::PyTorchInterpreter::HardwareDevices  eDevice = yolomodel::pytorch::PyTorchInterpreter::HardwareDevices::eCUDA 
)

Attempt to open the next available Torch hardware and load model at the given path onto the device.

Parameters
szModelPath- The absolute path to the model to open.
eDevice- The hardware device to launch the Torch model on.
Returns
true - Model was opened and loaded successfully onto the Torch device.
false - Something went wrong, model/device not opened.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-03-06
579{
580 // Initialize a new YOLOModel object.
581 m_pTorchDetector = std::make_shared<yolomodel::pytorch::PyTorchInterpreter>(szModelPath, eDevice);
582
583 // Check if device/model was opened without issue.
584 if (m_pTorchDetector->IsReadyForInference())
585 {
586 // Update member variable.
587 m_bTorchInitialized = true;
588 // Return status.
589 return true;
590 }
591 else
592 {
593 // Submit logger message.
594 LOG_ERROR(logging::g_qSharedLogger, "Unable to initialize Torch detection for TagDetector.");
595 // Update member variable.
596 m_bTorchInitialized = false;
597 // Return status.
598 return false;
599 }
600}

◆ EnableTorchDetection()

void TagDetector::EnableTorchDetection ( const float  fMinObjectConfidence = 0.4f,
const float  fNMSThreshold = 0.6f 
)

Turn on torch detection with given parameters.

Parameters
fMinObjectConfidence- The lower limit of detection confidence.
fNMSThreshold- The overlap thresh for NMS algorithm.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-03-06
612{
613 // Update member variables.
614 m_fTorchMinObjectConfidence = fMinObjectConfidence;
615 m_fTorchNMSThreshold = fNMSThreshold;
616
617 // Check if torch model has been initialized.
618 if (m_bTorchInitialized)
619 {
620 // Update member variable.
621 m_bTorchEnabled = true;
622 }
623 else
624 {
625 // Submit logger message.
626 LOG_WARNING(logging::g_qSharedLogger, "Tried to enable torch detection for TagDetector but it has not been initialized yet!");
627 // Update member variable.
628 m_bTorchEnabled = false;
629 }
630}

◆ DisableTorchDetection()

void TagDetector::DisableTorchDetection ( )

Set flag to stop tag detection with the torch model.

Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-03-06
640{
641 // Update member variables.
642 m_bTorchEnabled = false;
643}

◆ SetDetectorMaxFPS()

void TagDetector::SetDetectorMaxFPS ( const int  nRecordingFPS)

Mutator for the desired max FPS for this detector.

Parameters
nRecordingFPS- The max frames per second to detect tags at.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2024-01-22
654{
655 // Set the max iterations per second of the recording handler.
656 this->SetMainThreadIPSLimit(nRecordingFPS);
657}
Here is the call graph for this function:

◆ SetEnableRecordingFlag()

void TagDetector::SetEnableRecordingFlag ( const bool  bEnableRecordingFlag)

Mutator for the Enable Recording Flag private member.

Parameters
bEnableRecordingFlag- Whether or not recording should be enabled for this detector.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-12-31
668{
669 m_bEnableRecordingFlag = bEnableRecordingFlag;
670}

◆ GetIsReady()

bool TagDetector::GetIsReady ( )

Accessor for the status of this TagDetector.

Returns
true - The detector is running and detecting tags from the camera.
false - The detector thread and/or camera is not running/opened.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2024-01-04
682{
683 // Create instance variables.
684 bool bDetectorIsReady = false;
685
686 // Check if this detectors thread is currently running.
687 if (this->GetThreadState() == AutonomyThreadState::eRunning)
688 {
689 // Check if using ZEDCam or BasicCam.
690 if (m_bUsingZedCamera)
691 {
692 // Check if camera is NOT open.
693 if (std::dynamic_pointer_cast<ZEDCamera>(m_pCamera)->GetCameraIsOpen())
694 {
695 // Set camera opened toggle.
696 bDetectorIsReady = true;
697 }
698 }
699 else
700 {
701 // Check if camera is NOT open.
702 if (std::dynamic_pointer_cast<BasicCamera>(m_pCamera)->GetCameraIsOpen())
703 {
704 // Set camera opened toggle.
705 bDetectorIsReady = true;
706 }
707 }
708 }
709
710 // Return if this detector is ready or not.
711 return bDetectorIsReady;
712}
AutonomyThreadState GetThreadState() const
Accessor for the Threads State private member.
Definition AutonomyThread.hpp:237
Here is the call graph for this function:

◆ GetDetectorMaxFPS()

int TagDetector::GetDetectorMaxFPS ( ) const

Accessor for the desired max FPS for this detector.

Returns
int - The max frames per second the detector can run at.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2024-01-22
723{
724 // Return member variable value.
725 return this->GetMainThreadMaxIPS();
726}
int GetMainThreadMaxIPS() const
Accessor for the Main Thread Max I P S private member.
Definition AutonomyThread.hpp:594
Here is the call graph for this function:

◆ GetEnableRecordingFlag()

bool TagDetector::GetEnableRecordingFlag ( ) const

Accessor for the Enable Recording Flag private member.

Returns
true - Recording for this detector has been requested/flagged.
false - This detector should not be recorded.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-12-31
738{
739 return m_bEnableRecordingFlag;
740}

◆ GetCameraName()

std::string TagDetector::GetCameraName ( )

Accessor for the camera name or path that this TagDetector is tied to.

Returns
std::string - The name/path/index of the camera used by this TagDetector.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2024-01-01
751{
752 return m_szCameraName;
753}
Here is the caller graph for this function:

◆ GetProcessFrameResolution()

cv::Size TagDetector::GetProcessFrameResolution ( ) const

Accessor for the resolution of the process image used for tag detection.

Returns
cv::Size - The resolution stored in an OpenCV cv::Size.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2024-01-01
764{
765 // Check if using a ZED camera.
766 if (m_bUsingZedCamera)
767 {
768 // Concatenate camera model name and serial number.
769 return std::dynamic_pointer_cast<ZEDCamera>(m_pCamera)->GetPropResolution();
770 }
771 else
772 {
773 // Concatenate camera path or index.
774 return std::dynamic_pointer_cast<BasicCamera>(m_pCamera)->GetPropResolution();
775 }
776}

◆ ThreadedContinuousCode()

void TagDetector::ThreadedContinuousCode ( )
overrideprivatevirtual

This code will run continuously in a separate thread. New frames from the given camera are grabbed and the tags for the camera image are detected, filtered, and stored. Then any requests for the current tags are fulfilled.

Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-10-07

Implements AutonomyThread< void >.

177{
178 // Check if using ZEDCam or BasicCam.
179 if (m_bUsingZedCamera)
180 {
181 // Check if camera is NOT open.
182 if (!std::dynamic_pointer_cast<ZEDCamera>(m_pCamera)->GetCameraIsOpen())
183 {
184 // Set camera opened toggle.
185 m_bCameraIsOpened = false;
186
187 // If camera's not open on first iteration of thread, it's probably not present, so stop.
188 if (this->GetThreadState() == AutonomyThreadState::eStarting)
189 {
190 // Shutdown threads for this ZEDCam.
191 this->RequestStop();
192
193 // Submit logger message.
194 LOG_CRITICAL(logging::g_qSharedLogger,
195 "TagDetector start was attempted for ZED camera with serial number {}, but camera never properly opened or it has been closed/rebooted! "
196 "This tag detector will now stop.",
197 std::dynamic_pointer_cast<ZEDCamera>(m_pCamera)->GetCameraSerial());
198 }
199 }
200 else
201 {
202 // Set camera opened toggle.
203 m_bCameraIsOpened = true;
204 }
205 }
206 else
207 {
208 // Check if camera is NOT open.
209 if (!std::dynamic_pointer_cast<BasicCamera>(m_pCamera)->GetCameraIsOpen())
210 {
211 // Set camera opened toggle.
212 m_bCameraIsOpened = false;
213
214 // If camera's not open on first iteration of thread, it's probably not present, so stop.
215 if (this->GetThreadState() == AutonomyThreadState::eStarting)
216 {
217 // Shutdown threads for this BasicCam.
218 this->RequestStop();
219
220 // Submit logger message.
221 LOG_CRITICAL(logging::g_qSharedLogger,
222 "TagDetector start was attempted for BasicCam at {}, but camera never properly opened or it has become disconnected!",
223 std::dynamic_pointer_cast<BasicCamera>(m_pCamera)->GetCameraLocation());
224 }
225 }
226 else
227 {
228 // Set camera opened toggle.
229 m_bCameraIsOpened = true;
230 }
231 }
232
233 // Check if camera is opened.
234 if (m_bCameraIsOpened)
235 {
236 // Create future for indicating when the frame has been copied.
237 std::future<bool> fuPointCloudCopyStatus;
238 std::future<bool> fuRegularFrameCopyStatus;
239 bool bRequestingPointCloud = false;
240
241 // Check if the camera is setup to use CPU or GPU mats.
242 if (m_bUsingZedCamera)
243 {
244 bRequestingPointCloud = true;
245 // Check if the ZED camera is returning cv::cuda::GpuMat or cv:Mat.
246 if (m_bUsingGpuMats)
247 {
248 // Grabs point cloud from ZEDCam. Dynamic casts Camera to ZEDCamera* so we can use ZEDCam methods.
249 fuPointCloudCopyStatus = std::dynamic_pointer_cast<ZEDCamera>(m_pCamera)->RequestPointCloudCopy(m_cvGPUPointCloud);
250 // Get the regular RGB image from the camera.
251 fuRegularFrameCopyStatus = std::dynamic_pointer_cast<ZEDCamera>(m_pCamera)->RequestFrameCopy(m_cvGPUFrame);
252 }
253 else
254 {
255 // Grabs point cloud from ZEDCam.
256 fuPointCloudCopyStatus = std::dynamic_pointer_cast<ZEDCamera>(m_pCamera)->RequestPointCloudCopy(m_cvPointCloud);
257 fuRegularFrameCopyStatus = std::dynamic_pointer_cast<ZEDCamera>(m_pCamera)->RequestFrameCopy(m_cvFrame);
258 }
259 }
260 else
261 {
262 // Grab frames from camera.
263 fuRegularFrameCopyStatus = std::dynamic_pointer_cast<BasicCamera>(m_pCamera)->RequestFrameCopy(m_cvFrame);
264 }
265
266 // Safe polling wrapper to prevent deadlocks.
267 bool bCloudReady = !bRequestingPointCloud; // True by default if we don't need a point cloud
268 bool bFrameReady = false;
269
270 // Keep polling as long as the thread hasn't been asked to stop
271 while (this->GetThreadState() == AutonomyThreadState::eRunning)
272 {
273 if (!bCloudReady && fuPointCloudCopyStatus.wait_for(std::chrono::milliseconds(10)) == std::future_status::ready)
274 bCloudReady = true;
275
276 if (!bFrameReady && fuRegularFrameCopyStatus.wait_for(std::chrono::milliseconds(10)) == std::future_status::ready)
277 bFrameReady = true;
278
279 if (bCloudReady && bFrameReady)
280 break;
281 }
282
283 // If the thread is shutting down, break out of the loop gracefully
284 if (this->GetThreadState() != AutonomyThreadState::eRunning)
285 {
286 return;
287 }
288
289 // Process the retrieved frames
290 if (m_bUsingZedCamera)
291 {
292 if (m_bUsingGpuMats)
293 {
294 if (fuPointCloudCopyStatus.get() && fuRegularFrameCopyStatus.get())
295 {
296 // Download mat from GPU memory.
297 m_cvGPUPointCloud.download(m_cvPointCloud);
298 m_cvGPUFrame.download(m_cvFrame);
299 // Drop alpha channel.
300 cv::cvtColor(m_cvFrame, m_cvFrame, cv::COLOR_BGRA2BGR);
301 }
302 else
303 {
304 LOG_WARNING(logging::g_qSharedLogger, "TagDetector unable to get point cloud or frame from ZEDCam!");
305 }
306 }
307 else
308 {
309 if (!fuPointCloudCopyStatus.get())
310 LOG_WARNING(logging::g_qSharedLogger, "TagDetector unable to get point cloud from ZEDCam!");
311 if (!fuRegularFrameCopyStatus.get())
312 LOG_WARNING(logging::g_qSharedLogger, "TagDetector unable to get regular frame from ZEDCam!");
313 }
314 }
315 else
316 {
317 if (!fuRegularFrameCopyStatus.get())
318 {
319 LOG_WARNING(logging::g_qSharedLogger, "TagDetector unable to get RGB image from BasicCam!");
320 }
321 }
322
324 // Actual detection logic goes here.
326 // Check if the frame is empty.
327 if (m_cvFrame.empty())
328 {
329 // Submit logger message.
330 LOG_WARNING(logging::g_qSharedLogger, "Frame from camera is empty!");
331 return;
332 }
333
334 // Clear the list of newly detected tags.
335 m_vNewlyDetectedTags.clear();
336 // Clone frames.
337 m_cvArucoProcFrame = m_cvFrame.clone();
338 // Detect tags in the image
339 std::vector<tagdetectutils::ArucoTag> vNewOpenCVTags = arucotag::Detect(m_cvArucoProcFrame, m_cvArucoDetector);
340 // Loop through the newly detected OpenCV tags and set their detector UUID to this TagDetector's camera name so we can associate them with this detector.
341 for (tagdetectutils::ArucoTag& stTag : vNewOpenCVTags)
342 {
343 stTag.szDetectorUUID = this->GetThreadUUID();
344 }
345 // Add OpenCV tags to the list of newly detected tags.
346 m_vNewlyDetectedTags.insert(m_vNewlyDetectedTags.end(), vNewOpenCVTags.begin(), vNewOpenCVTags.end());
347
348 // Check if torch detection if turned on.
349 if (m_bTorchEnabled)
350 {
351 // Detect tags in the image.
352 std::vector<tagdetectutils::ArucoTag> vNewTorchTags =
353 torchtag::Detect(m_cvArucoProcFrame, *m_pTorchDetector, m_fTorchMinObjectConfidence, m_fTorchNMSThreshold);
354
355 // Add Torch tags to the list of newly detected tags.
356 m_vNewlyDetectedTags.insert(m_vNewlyDetectedTags.end(), vNewTorchTags.begin(), vNewTorchTags.end());
357 }
358
359 // Set the FOV of the camera in the tag structs for this detector's camera.
360 for (tagdetectutils::ArucoTag& stTag : m_vNewlyDetectedTags)
361 {
362 // Set the UUID of the detector that detected this tag to this TagDetector's camera name so we can associate it with this detector.
363 stTag.szDetectorUUID = this->GetThreadUUID();
364 // Set tag FOV parameter to this tag detectors camera's FOV.
365 stTag.dHorizontalFOV = m_pCamera->GetPropHorizontalFOV();
366 }
367
368 // Merge the newly detected tags with the pre-existing detected tags.
369 this->UpdateDetectedTags(m_vNewlyDetectedTags);
370
371 // Draw tag overlays onto normal image.
372 arucotag::DrawDetections(m_cvArucoProcFrame, m_vDetectedArucoTags);
373 torchtag::DrawDetections(m_cvArucoProcFrame, m_vDetectedArucoTags);
374
375 // Check if the detected tags vector is empty. If not, set the last good detection overlay frame to the current one with detections drawn on it.
376 if (!m_vDetectedArucoTags.empty())
377 {
378 m_cvLastGoodDetectionOverlayFrame = m_cvArucoProcFrame.clone();
379 }
381 }
382
383 // Acquire a shared_lock on the detected tags copy queue.
384 std::shared_lock<std::shared_mutex> lkSchedulers(m_muPoolScheduleMutex);
385 // Check if the detected tag copy queue is empty.
386 if (!m_qDetectionOverlayFramesCopySchedule.empty() || !m_qDetectedArucoTagCopySchedule.empty())
387 {
388 size_t siQueueLength = m_qDetectionOverlayFramesCopySchedule.size() + m_qDetectedArucoTagCopySchedule.size();
389 // Start the thread pool to store multiple copies of the detected tags to the requesting threads
390 this->RunDetachedPool(siQueueLength, m_nNumDetectedTagsRetrievalThreads);
391 // Wait for thread pool to finish.
392 this->JoinPool();
393 // Release lock on frame copy queue.
394 lkSchedulers.unlock();
395 }
396}
std::string GetThreadUUID() const
Accessor for the Thread U U I D private member.
Definition AutonomyThread.hpp:247
void RunDetachedPool(const unsigned int nNumTasksToQueue, const unsigned int nNumThreads=2, const bool bForceStopCurrentThreads=false)
When this method is called, it starts a thread pool full of threads that don't return std::futures (l...
Definition AutonomyThread.hpp:399
void JoinPool()
Waits for pool to finish executing tasks. This method will block the calling code until thread is fin...
Definition AutonomyThread.hpp:502
void UpdateDetectedTags(std::vector< tagdetectutils::ArucoTag > &vNewlyDetectedTags)
Updates the detected torch tags including tracking the detected tags over time and removing tags that...
Definition TagDetector.cpp:787
CV_NODISCARD_STD Mat clone() const
bool empty() const
void download(OutputArray dst) const
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0)
COLOR_BGRA2BGR
std::vector< tagdetectutils::ArucoTag > Detect(const cv::Mat &cvFrame, const cv::aruco::ArucoDetector &cvArucoDetector)
Detect ArUco tags in the provided image.
Definition ArucoDetection.hpp:93
void DrawDetections(cv::Mat &cvDetectionsFrame, const std::vector< tagdetectutils::ArucoTag > &vDetectedTags)
Given a vector of tagdetectutils::ArucoTag structs draw each tag corner and ID onto the given image.
Definition ArucoDetection.hpp:138
void DrawDetections(cv::Mat &cvDetectionsFrame, const std::vector< tagdetectutils::ArucoTag > &vDetectedTags)
Given a vector of tagdetectutils::ArucoTag structs draw each tag corner and confidence onto the given...
Definition TorchTagDetection.hpp:103
std::vector< tagdetectutils::ArucoTag > Detect(const cv::Mat &cvFrame, yolomodel::pytorch::PyTorchInterpreter &trPyTorchDetector, const float fMinObjectConfidence=0.40f, const float fNMSThreshold=0.60f)
Detect ArUco tags in the provided image using a YOLO DNN model.
Definition TorchTagDetection.hpp:45
Represents a single ArUco tag.
Definition TagDetectionUtilty.hpp:57
Here is the call graph for this function:

◆ PooledLinearCode()

void TagDetector::PooledLinearCode ( )
overrideprivatevirtual

This method holds the code that is ran in the thread pool started by the ThreadedLinearCode() method. It copies the data from the different data objects to references of the same type stored in a queue filled by the Request methods.

Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-10-08

Implements AutonomyThread< void >.

409{
411 // Detection Overlay Frame queue.
413 // Acquire sole writing access to the detectedTagCopySchedule.
414 std::unique_lock<std::shared_mutex> lkTagOverlayFrameQueue(m_muDetectionOverlayCopyMutex);
415 // Check if there are unfulfilled requests.
416 if (!m_qDetectionOverlayFramesCopySchedule.empty())
417 {
418 // Get frame container out of queue.
419 containers::FrameFetchContainer<cv::Mat> stContainer = m_qDetectionOverlayFramesCopySchedule.front();
420 // Pop out of queue.
421 m_qDetectionOverlayFramesCopySchedule.pop();
422 // Release lock.
423 lkTagOverlayFrameQueue.unlock();
424
425 // Check which frame we should copy.
426 switch (stContainer.eFrameType)
427 {
428 case PIXEL_FORMATS::eArucoDetection: *stContainer.pFrame = m_cvArucoProcFrame.clone(); break;
429 default: *stContainer.pFrame = m_cvArucoProcFrame.clone(); break;
430 }
431
432 // Signal future that the frame has been successfully retrieved.
433 stContainer.pCopiedFrameStatus->set_value(true);
434 }
435
437 // Last Good Detection Overlay Frame queue.
439 // Acquire sole writing access to the detectedTagCopySchedule.
440 std::unique_lock<std::shared_mutex> lkLastGoodDetectionOverlayFrameQueue(m_muLastGoodDetectionOverlayCopyMutex);
441 // Check if there are unfulfilled requests.
442 if (!m_qLastGoodDetectionOverlayFramesCopySchedule.empty())
443 {
444 // Get frame container out of queue.
445 containers::FrameFetchContainer<cv::Mat> stContainer = m_qLastGoodDetectionOverlayFramesCopySchedule.front();
446 // Pop out of queue.
447 m_qLastGoodDetectionOverlayFramesCopySchedule.pop();
448 // Release lock.
449 lkLastGoodDetectionOverlayFrameQueue.unlock();
450
451 // Check which frame we should copy.
452 switch (stContainer.eFrameType)
453 {
454 case PIXEL_FORMATS::eArucoDetection: *stContainer.pFrame = m_cvLastGoodDetectionOverlayFrame.clone(); break;
455 default: *stContainer.pFrame = m_cvLastGoodDetectionOverlayFrame.clone(); break;
456 }
457
458 // Signal future that the frame has been successfully retrieved.
459 stContainer.pCopiedFrameStatus->set_value(true);
460 }
461
463 // ArucoTag queue.
465 // Acquire sole writing access to the detectedTagCopySchedule.
466 std::unique_lock<std::shared_mutex> lkArucoTagQueue(m_muArucoDataCopyMutex);
467 // Check if there are unfulfilled requests.
468 if (!m_qDetectedArucoTagCopySchedule.empty())
469 {
470 // Get frame container out of queue.
471 containers::DataFetchContainer<std::vector<tagdetectutils::ArucoTag>> stContainer = m_qDetectedArucoTagCopySchedule.front();
472 // Pop out of queue.
473 m_qDetectedArucoTagCopySchedule.pop();
474 // Release lock.
475 lkArucoTagQueue.unlock();
476
477 // Copy the detected tags to the target location
478 *stContainer.pData = m_vDetectedArucoTags;
479
480 // Signal future that the frame has been successfully retrieved.
481 stContainer.pCopiedDataStatus->set_value(true);
482 }
483}
Here is the call graph for this function:

◆ UpdateDetectedTags()

void TagDetector::UpdateDetectedTags ( std::vector< tagdetectutils::ArucoTag > &  vNewlyDetectedTags)
private

Updates the detected torch tags including tracking the detected tags over time and removing tags that haven't been seen for long enough.

Parameters
vNewlyDetectedTags- Input vector of TorchTag structs containing the tag info.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-03-15
788{
789 // Check if tracking is enabled.
790 if (m_bEnableTracking)
791 {
792 // Check if the given tag vector is empty
793 if (vNewlyDetectedTags.empty())
794 {
795 // Since the tags are empty that means the detector has not detected any new ground truth tags.
796 // In this case we will fallback to relying on the multi-tracker to track the tags and just update the tags
797 // stored in the m_vDetectedArucoTags vector.
798 // This is necessary because the torch detector is not perfect and may not detect all tags in the frame
799 // and it doesn't have the ability to track tags over time.
800 // We will use the multi-tracker to track the tags over time and update the bounding box data for the tags.
801
802 // Update the multi-tracker with the current frame.
803 m_pMultiTracker->Update(m_cvFrame);
804 }
805 else
806 {
807 // Loop through the newly detected tags.
808 for (tagdetectutils::ArucoTag& stTag : vNewlyDetectedTags)
809 {
810 // Add the newly detected tags to the multi-tracker.
811 bool bMatchedTagToExistingTracker = m_pMultiTracker->InitTracker(m_cvFrame, stTag.pBoundingBox, constants::BBOX_TRACKER_TYPE);
812 // Check if the tag was matched to an existing tracker.
813 if (!bMatchedTagToExistingTracker)
814 {
815 // Add the new tag to the member variable list.
816 m_vDetectedArucoTags.emplace_back(stTag);
817 }
818 else
819 {
820 // Find the tag with the same bounding box pointer and update the ID and confidence.
821 for (tagdetectutils::ArucoTag& stExistingTag : m_vDetectedArucoTags)
822 {
823 // Check if the bounding box pointers are the same.
824 if (stTag.pBoundingBox == stExistingTag.pBoundingBox)
825 {
826 // Update the ID and confidence of the existing tag.
827 stExistingTag.nID = stTag.nID;
828 stExistingTag.dConfidence = stTag.dConfidence;
829 }
830 }
831 }
832 }
833
834 // Update the multi-tracker with the current frame.
835 m_pMultiTracker->Update(m_cvFrame);
836 }
837
838 // Loop through the detected tags and check if there are any we need to remove, and also update the time last seen.
839 for (std::vector<tagdetectutils::ArucoTag>::iterator itTag = m_vDetectedArucoTags.begin(); itTag != m_vDetectedArucoTags.end();)
840 {
841 // Check if the bounding box is 0,0,0,0.
842 if (itTag->pBoundingBox->x == 0 && itTag->pBoundingBox->y == 0 && itTag->pBoundingBox->width == 0 && itTag->pBoundingBox->height == 0)
843 {
844 // Remove the tag from the vector.
845 itTag = m_vDetectedArucoTags.erase(itTag);
846 }
847 else
848 {
849 ++itTag;
850 }
851 }
852 }
853 else
854 {
855 // If tracking is not enabled, we will just clear the detected tags and add the new ones.
856 m_vDetectedArucoTags.clear();
857 // Loop through the newly detected tags and add them to the detected tags vector.
858 for (tagdetectutils::ArucoTag& stTag : vNewlyDetectedTags)
859 {
860 // Set the tag creation time to 0. The tags aren't being tracked, so we can't really tell their age.
861 stTag.tmCreation = std::chrono::system_clock::time_point::min();
862
863 // Add the new tag to the member variable list.
864 m_vDetectedArucoTags.emplace_back(stTag);
865 }
866 }
867
868 // Check if we are using a ZED camera.
869 if (m_bUsingZedCamera)
870 {
871 // Check if the point cloud is empty.
872 if (!m_cvPointCloud.empty())
873 {
874 // Get the rover pose from the waypoint handler.
875 m_stRoverPose = globals::g_pStateMachineHandler->SmartRetrieveRoverPose();
876
877 // Find the camera's position in UTM coordinates by applying the camera offset to the rover pose.
878 geoops::UTMCoordinate stCamera = m_stRoverPose.GetUTMCoordinate();
879
880 // Translate the camera's local offsets into global Easting/Northing.
881 double dRoverHeadingRad = m_stRoverPose.GetCompassHeading() * (CV_PI / 180.0);
882 double dRotatedX = (m_pCamera->GetCameraPoseOffset().dPosY * sin(dRoverHeadingRad)) + (m_pCamera->GetCameraPoseOffset().dPosX * cos(dRoverHeadingRad));
883 double dRotatedY = (m_pCamera->GetCameraPoseOffset().dPosY * cos(dRoverHeadingRad)) - (m_pCamera->GetCameraPoseOffset().dPosX * sin(dRoverHeadingRad));
884
885 // Apply the rotated offsets to the global coordinates
886 stCamera.dEasting += dRotatedX;
887 stCamera.dNorthing += dRotatedY;
888 stCamera.dAltitude += m_pCamera->GetCameraPoseOffset().dPosZ;
889
890 // Creating variables for the quaternion values.
891 double dQW = m_pCamera->GetCameraPoseOffset().dQW;
892 double dQX = m_pCamera->GetCameraPoseOffset().dQX;
893 double dQY = m_pCamera->GetCameraPoseOffset().dQY;
894 double dQZ = m_pCamera->GetCameraPoseOffset().dQZ;
895 // Update the rover pose's heading to match the camera's heading. We will need to calculate the camera's heading using the quaternion.
896 double dSinYCosP = 2.0 * (dQW * dQY + dQX * dQZ);
897 double dCosYCosP = 1.0 - 2.0 * (dQX * dQX + dQY * dQY);
898 double dCameraHeading = std::atan2(dSinYCosP, dCosYCosP) * (180.0 / CV_PI);
899
900 // Add the relative camera heading to the absolute rover heading
901 double dAbsoluteCameraHeading = numops::InputAngleModulus<double>(m_stRoverPose.GetCompassHeading() + dCameraHeading, 0.0, 360.0);
902
903 // Recreate the rover pose with the camera's adjusted absolute position and absolute heading
904 geoops::RoverPose stCameraPose = geoops::RoverPose(stCamera, dAbsoluteCameraHeading);
905
906 // LOG_NOTICE(logging::g_qSharedLogger,
907 // "RoverPose GPS: {} {} | RoverPose Heading: {}",
908 // stCameraPose.GetGPSCoordinate().dLatitude,
909 // stCameraPose.GetGPSCoordinate().dLongitude,
910 // stCameraPose.GetCompassHeading());
911 // // Recreate the rover pose with the camera's adjusted position and heading.
912 // geoops::RoverPose stCameraPose = geoops::RoverPose(stCamera, dCameraHeading);
913
914 // Loop through the tags and use their center point to lookup their distance in the point cloud.
915 for (tagdetectutils::ArucoTag& stTag : m_vDetectedArucoTags)
916 {
917 // Use either width of height for the neighborhood size.
918 int nNeighborhoodSize = std::min(stTag.pBoundingBox->width, stTag.pBoundingBox->height);
919 // Geolocate the tag in the point cloud.
920 geoops::Waypoint stGeolocation =
921 geoloc::GeolocateBox(m_cvPointCloud,
922 stCameraPose,
923 cv::Point(stTag.pBoundingBox->x + stTag.pBoundingBox->width / 2, stTag.pBoundingBox->y + stTag.pBoundingBox->height / 2),
924 nNeighborhoodSize);
925
926 // Calculate the yaw angle to the tag using the center point of the tag and the camera's field of view.
927 // This is a fallback in case the geolocation fails for some reason, we can still provide a relative angle to the tag.
928 // Get the center X pixel coordinate of the tag's bounding box.
929 double dTagCenterX = stTag.pBoundingBox->x + (stTag.pBoundingBox->width / 2.0);
930 // Get the center X pixel coordinate of the camera frame.
931 double dFrameCenterX = m_cvFrame.cols / 2.0;
932 // Calculate the offset in pixels from the center of the camera frame.
933 // (Positive offset = target is to the right, Negative = target is to the left)
934 double dPixelOffsetX = dTagCenterX - dFrameCenterX;
935 // Calculate how many real-world degrees each pixel represents.
936 double dDegreesPerPixel = stTag.dHorizontalFOV / static_cast<double>(m_cvFrame.cols);
937 // Multiply the pixel offset by the degrees per pixel to get the relative yaw angle.
938 stTag.dYawAngle = dPixelOffsetX * dDegreesPerPixel;
939 // Explicitly set distance to 0.0 so the autonomy state machines know the depth map failed
940 // and will properly fall back to using this calculated dYawAngle.
941 stTag.dStraightLineDistance = 0.0;
942
943 // Check if the geolocation is valid. If it is overwrite the yaw angle and distance with the geolocation data.
944 if (stGeolocation != geoops::Waypoint())
945 {
946 // Since this is a tag detection, set the tag's waypoint type appropriately.
947 stGeolocation.eType = geoops::WaypointType::eTagWaypoint;
948 // Calculate the geo measurement and print the distance to the tag.
949 geoops::GeoMeasurement stMeasurement = geoops::CalculateGeoMeasurement(m_stRoverPose.GetUTMCoordinate(), stGeolocation.GetUTMCoordinate());
950
951 // Check that the distance is in a reasonable range.
952 if (stMeasurement.dDistanceMeters > 0.0 && stMeasurement.dDistanceMeters < 25.0)
953 {
954 // Set the tag's geolocation.
955 stTag.stGeolocatedPosition = stGeolocation;
956 // Use the rover heading and the azimuth angle to calculate the relative heading to the tag.
957 stTag.dYawAngle = numops::AngularDifference(m_stRoverPose.GetCompassHeading(), stMeasurement.dStartRelativeBearing);
958 // Set the straight line distance to the tag.
959 stTag.dStraightLineDistance = stMeasurement.dDistanceMeters;
960 }
961 }
962 }
963 }
964 }
965 else
966 {
967 // Estimate the positions of the tags using some basic trig.
968 for (tagdetectutils::ArucoTag& stTag : m_vDetectedArucoTags)
969 {
970 // Use some trig to get the location of the tag.
972 }
973 }
974}
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
#define CV_PI
__device__ __forceinline__ float1 cos(const uchar1 &a)
__device__ __forceinline__ float4 sin(const uchar4 &a)
geoops::Waypoint GeolocateBox(const cv::Mat &cvPointcloud, const geoops::RoverPose &stCameraPose, const cv::Point &cvPixel, int nNeighborhoodSize=5, float fDepthPercentileTarget=0.20f, float fDepthToleranceMeters=0.5f)
Converts pixel coordinates in a ZED2i pointcloud into global UTM coordinates, given the camera's curr...
Definition Geolocate.hpp:57
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 AngularDifference(T tFirstValue, T tSecondValue)
Calculates the distance in degrees between two angles. This function accounts for wrap around so that...
Definition NumberOperations.hpp:201
void EstimatePoseFromCameraFrame(ArucoTag &stTag)
Estimate the pose of a tag from a camera frame.
Definition TagDetectionUtilty.hpp:214
This struct is used to store the distance, arc length, and relative bearing for a calculated geodesic...
Definition GeospatialOperations.hpp:83
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
Here is the call graph for this function:
Here is the caller graph for this function:

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