42 #include <type_traits>
44 #define OSVR_TRACKER_THREAD_WRAP_WITH_TRY
49 static const uint32_t IMU_MESSAGE_QUEUE_SIZE = 64 + 1;
51 TrackerThread::TrackerThread(TrackingSystem &trackingSystem,
52 ImageSource &imageSource,
53 BodyReportingVector &reportingVec,
54 CameraParameters
const &camParams,
55 std::int32_t cameraUsecOffset,
bool bufferImu,
57 : m_trackingSystem(trackingSystem), m_cam(imageSource),
58 m_reportingVec(reportingVec), m_camParams(camParams),
59 m_cameraUsecOffset(cameraUsecOffset), m_bufferImu(bufferImu),
60 m_debugData(debugData), m_imuMessages(IMU_MESSAGE_QUEUE_SIZE),
61 m_debugDataMessages(32) {
62 msg() <<
"Tracker thread object created." << std::endl;
65 TrackerThread::~TrackerThread() {
66 if (m_imageThread.joinable()) {
71 void TrackerThread::permitStart() { m_startupSignal.set_value(); }
80 msg() <<
"Tracker thread object invoked, waiting for permitStart()."
82 m_startupSignal.get_future().wait();
85 std::this_thread::sleep_for(std::chrono::milliseconds(500));
88 m_numBodies = m_trackingSystem.getNumBodies();
89 setupReportingVectorProcessModels();
94 m_trackingSystem, m_cam, *
this, m_camParams, m_cameraUsecOffset};
95 imageProcThreadObj_ = &imageProcThreadObj;
96 m_imageThread = std::thread{[&] { imageProcThreadObj.threadAction(); }};
98 msg() <<
"Tracker thread object entering its main execution loop."
101 #ifdef OSVR_TRACKER_THREAD_WRAP_WITH_TRY
104 bool keepGoing =
true;
112 std::lock_guard<std::mutex> lock(m_runMutex);
116 msg() <<
"Tracker thread object: Just checked our run flag "
117 "and noticed it turned false..."
121 #ifdef OSVR_TRACKER_THREAD_WRAP_WITH_TRY
122 }
catch (std::exception
const &e) {
123 warn() <<
"Tracker thread object: exiting because of caught "
125 << e.what() << std::endl;
129 msg() <<
"Tracker thread object: functor exiting." << std::endl;
131 if (!imageProcThreadObj.exiting()) {
132 msg() <<
"Telling image processing thread to exit." << std::endl;
133 imageProcThreadObj.signalExit();
135 imageProcThreadObj_ =
nullptr;
136 if (m_imageThread.joinable()) {
137 m_imageThread.join();
143 msg() <<
"Tracker thread object: triggerStop() called" << std::endl;
144 std::lock_guard<std::mutex> lock(m_runMutex);
152 if (!m_imuMessages.write(makeImuReport(imu, tv, report))) {
157 m_messageCondVar.notify_one();
166 if (!m_imuMessages.write(makeImuReport(imu, tv, report))) {
170 m_messageCondVar.notify_one();
174 bool TrackerThread::checkForDebugData(DebugArray &data) {
175 return m_debugDataMessages.read(data);
180 cv::Mat
const &frame,
181 cv::Mat
const &frameGray) {
182 m_imageData = std::move(imageData);
184 m_frameGray = frameGray;
186 std::lock_guard<std::mutex> lock{m_messageMutex};
187 m_timeConsumingImageStepComplete =
true;
189 m_messageCondVar.notify_one();
192 std::ostream &TrackerThread::msg()
const {
193 return std::cout <<
"[UnifiedTracker] ";
196 std::ostream &TrackerThread::warn()
const {
return msg() <<
"Warning: "; }
198 void TrackerThread::doFrame() {
202 warn() <<
"Camera is reporting it is not OK." << std::endl;
209 warn() <<
"Camera grab failed." << std::endl;
218 launchTimeConsumingImageStep();
220 setImuOverrideClock();
223 UpdatedBodyIndices imuIndices;
225 bool finishedImage =
false;
230 std::unique_lock<std::mutex> lock(m_messageMutex);
231 m_messageCondVar.wait(lock, [&] {
232 return m_timeConsumingImageStepComplete ||
233 !m_imuMessages.isEmpty();
235 if (m_timeConsumingImageStepComplete) {
239 finishedImage =
true;
245 if (!finishedImage) {
249 IMUMessage message = boost::none;
250 if (!m_imuMessages.read(message) || message.empty()) {
257 ImuMessageCategory cat;
258 std::tie(
id, cat) = processIMUMessage(message);
266 imuIndices.insert(
id);
270 if (shouldSendImuReport()) {
271 updateReportingVector(imuIndices);
277 updateReportingVector(
id);
280 }
while (!finishedImage);
283 if (!m_frame.data || !m_frameGray.data) {
285 warn() <<
"Camera retrieve appeared to fail: frames had null "
293 warn() <<
"Initial image processing failed somehow!" << std::endl;
304 UpdatedBodyIndices sortedBodyIds{begin(bodyIds), end(bodyIds)};
306 for (
auto &
id : imuIndices) {
307 sortedBodyIds.insert(
id);
316 std::size_t numMessages = m_imuMessages.sizeGuess();
318 IMUMessage message = boost::none;
319 for (std::size_t i = 0; i < numMessages; ++i) {
320 if (!m_imuMessages.read(message)) {
325 auto id = processIMUMessage(message).first;
327 sortedBodyIds.insert(
id);
332 updateReportingVector(sortedBodyIds);
335 std::pair<BodyId, ImuMessageCategory>
336 TrackerThread::processIMUMessage(IMUMessage
const &m) {
337 return osvr::vbtracker::processImuMessage(m);
340 BodyReporting *TrackerThread::getCamPoseReporting()
const {
341 #ifdef OSVR_OUTPUT_CAMERA_POSE
342 return m_reportingVec[m_numBodies + extra_outputs::outputCamIndex]
349 BodyReporting *TrackerThread::getIMUReporting()
const {
350 #ifdef OSVR_OUTPUT_IMU
351 return m_reportingVec[m_numBodies + extra_outputs::outputImuIndex]
358 BodyReporting *TrackerThread::getIMUCamReporting()
const {
359 #ifdef OSVR_OUTPUT_IMU_CAM
360 return m_reportingVec[m_numBodies + extra_outputs::outputImuCamIndex]
367 BodyReporting *TrackerThread::getHMDCamReporting()
const {
369 #ifdef OSVR_OUTPUT_HMD_CAM
370 return m_reportingVec[m_numBodies + extra_outputs::outputHMDCamIndex]
377 void TrackerThread::updateExtraCameraReport() {
378 #ifndef OSVR_OUTPUT_CAMERA_POSE
382 using namespace Eigen;
385 auto rightNow = our_clock::now();
386 if (!m_nextCameraPoseReport || rightNow > *m_nextCameraPoseReport) {
387 m_nextCameraPoseReport = rightNow + seconds(1);
389 state.position() = m_trackingSystem.getCameraPose().translation();
391 Quaterniond(m_trackingSystem.getCameraPose().rotation()));
396 void TrackerThread::updateExtraIMUReports() {
398 #if !defined(OSVR_OUTPUT_IMU) && !defined(OSVR_OUTPUT_IMU_CAM)
403 if (!m_trackingSystem.getBody(BodyId(0)).hasIMU()) {
407 auto &imu = m_trackingSystem.getBody(BodyId(0)).getIMU();
408 if (!imu.hasPoseEstimate()) {
411 Eigen::Quaterniond imuQuat = imu.getPoseEstimate();
413 #ifdef OSVR_OUTPUT_IMU
417 getIMUReporting()->updateState(imu.getLastUpdate(), state);
421 #ifdef OSVR_OUTPUT_IMU_CAM
426 state.
setQuaternion(getCameraRotation(m_trackingSystem) * imuQuat);
429 state.position() = Eigen::Vector3d(0, 0.5, 0);
430 getIMUCamReporting()->updateState(imu.getLastUpdate(), state);
435 void TrackerThread::setupReportingVectorProcessModels() {
436 for (BodyId::wrapped_type i = 0; i < m_numBodies; ++i) {
437 auto bodyId = BodyId{i};
438 auto &body = m_trackingSystem.getBody(bodyId);
439 m_reportingVec[i]->initProcessModel(body.getProcessModel());
440 #ifdef OSVR_OUTPUT_HMD_CAM
441 if (bodyId == BodyId(0)) {
442 getHMDCamReporting()->initProcessModel(body.getProcessModel());
448 bool TrackerThread::setupReportingVectorRoomTransforms() {
449 if (!m_trackingSystem.haveCameraPose()) {
453 if (m_setCameraPose) {
459 m_setCameraPose =
true;
463 auto cameraPoseReporting = getCamPoseReporting();
464 auto imuAlignedReporting = getIMUReporting();
465 auto imuCameraSpaceReporting = getIMUCamReporting();
466 auto hmdCameraSpaceReporting = getHMDCamReporting();
467 Eigen::Isometry3d trackerToRoomXform = m_trackingSystem.getCameraPose();
468 for (
auto &reporting : m_reportingVec) {
472 auto reportingRaw = reporting.get();
473 if (reportingRaw == cameraPoseReporting ||
474 reportingRaw == imuAlignedReporting ||
475 reportingRaw == imuCameraSpaceReporting ||
476 reportingRaw == hmdCameraSpaceReporting) {
482 reporting->setTrackerToRoomTransform(trackerToRoomXform);
488 TrackerThread::updateReportingVector(UpdatedBodyIndices
const &bodyIds) {
489 if (!setupReportingVectorRoomTransforms()) {
494 for (
auto const &bodyId : bodyIds) {
495 updateReportingVector(bodyId);
496 #ifdef OSVR_OUTPUT_HMD_CAM
497 if (bodyId == BodyId(0)) {
498 auto &body = m_trackingSystem.getBody(bodyId);
499 getHMDCamReporting()->updateState(body.getStateTime(),
506 updateExtraCameraReport();
507 updateExtraIMUReports();
510 void TrackerThread::updateReportingVector(BodyId
const bodyId) {
511 auto &body = m_trackingSystem.getBody(bodyId);
512 m_reportingVec[bodyId.value()]->updateState(body.getStateTime(),
514 if (m_debugData && bodyId == BodyId(0)) {
515 DebugArray newDebugArray = {};
516 auto &target = *body.getTarget(TargetId(0));
517 auto &debugData = target.getBeaconDebugData();
518 for (
int i = 0; i < MAX_DEBUG_BEACONS; ++i) {
519 auto baseIdx = i * DEBUG_ANALOGS_PER_BEACON;
520 auto &thisBeacon = debugData[i];
521 newDebugArray[baseIdx] = thisBeacon.variance;
522 newDebugArray[baseIdx + 1] = thisBeacon.measurement.x;
523 newDebugArray[baseIdx + 2] = thisBeacon.measurement.y;
524 newDebugArray[baseIdx + 3] = thisBeacon.residual.x;
525 newDebugArray[baseIdx + 4] = thisBeacon.residual.y;
527 m_debugDataMessages.write(newDebugArray);
531 void TrackerThread::launchTimeConsumingImageStep() {
534 m_timeConsumingImageStepComplete =
false;
537 imageProcThreadObj_->signalDoFrame();
Header declaring a C++11 finally or "scope-guard" construct.
void setQuaternion(Eigen::Quaterniond const &quaternion)
Intended for startup use.
virtual bool ok() const =0
void getNow(TimeValue &tv)
Set the given TimeValue to the current time.
BodyIndices const & updateBodiesFromVideoData(ImageOutputDataPtr &&imageData)
Report type for an orientation callback on a tracker interface.
Header for interoperation between the Eigen math library, the internal mini math library, and VRPN's quatlib.
Report type for an angular velocity callback on a tracker interface.
Standardized, portable parallel to struct timeval for representing both absolute times and time inter...
void signalImageProcessingComplete(ImageOutputDataPtr &&imageData, cv::Mat const &frame, cv::Mat const &frameGray)
bool submitIMUReport(TrackedBodyIMU &imu, util::time::TimeValue const &tv, OSVR_OrientationReport const &report)