OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
TrackerThread.cpp
Go to the documentation of this file.
1 
11 // Copyright 2016 Sensics, Inc.
12 //
13 // Licensed under the Apache License, Version 2.0 (the "License");
14 // you may not use this file except in compliance with the License.
15 // You may obtain a copy of the License at
16 //
17 // http://www.apache.org/licenses/LICENSE-2.0
18 //
19 // Unless required by applicable law or agreed to in writing, software
20 // distributed under the License is distributed on an "AS IS" BASIS,
21 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 // See the License for the specific language governing permissions and
23 // limitations under the License.
24 
25 // Internal Includes
26 #include "TrackerThread.h"
27 #include "AdditionalReports.h"
28 #include "ImageProcessingThread.h"
29 #include "ProcessIMUMessage.h"
30 #include "SpaceTransformations.h"
31 #include "TrackedBody.h"
32 #include "TrackedBodyIMU.h"
33 #include "TrackedBodyTarget.h"
34 
35 // Library/third-party includes
36 #include <osvr/Util/EigenInterop.h>
37 #include <osvr/Util/Finally.h>
38 
39 // Standard includes
40 #include <future>
41 #include <iostream>
42 #include <type_traits>
43 
44 #define OSVR_TRACKER_THREAD_WRAP_WITH_TRY
45 
46 namespace osvr {
47 namespace vbtracker {
48  // 16 and even 32 was too small - we were dropping messages.
49  static const uint32_t IMU_MESSAGE_QUEUE_SIZE = 64 + 1;
50 
51  TrackerThread::TrackerThread(TrackingSystem &trackingSystem,
52  ImageSource &imageSource,
53  BodyReportingVector &reportingVec,
54  CameraParameters const &camParams,
55  std::int32_t cameraUsecOffset, bool bufferImu,
56  bool debugData)
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;
63  }
64 
65  TrackerThread::~TrackerThread() {
66  if (m_imageThread.joinable()) {
67  m_imageThread.join();
68  }
69  }
70 
71  void TrackerThread::permitStart() { m_startupSignal.set_value(); }
72 
79 
80  msg() << "Tracker thread object invoked, waiting for permitStart()."
81  << std::endl;
82  m_startupSignal.get_future().wait();
85  std::this_thread::sleep_for(std::chrono::milliseconds(500));
86 
88  m_numBodies = m_trackingSystem.getNumBodies();
89  setupReportingVectorProcessModels();
90 
92 
93  ImageProcessingThread imageProcThreadObj{
94  m_trackingSystem, m_cam, *this, m_camParams, m_cameraUsecOffset};
95  imageProcThreadObj_ = &imageProcThreadObj;
96  m_imageThread = std::thread{[&] { imageProcThreadObj.threadAction(); }};
97 
98  msg() << "Tracker thread object entering its main execution loop."
99  << std::endl;
100 
101 #ifdef OSVR_TRACKER_THREAD_WRAP_WITH_TRY
102  try {
103 #endif
104  bool keepGoing = true;
105  while (keepGoing) {
108  doFrame();
109 
110  {
112  std::lock_guard<std::mutex> lock(m_runMutex);
113  keepGoing = m_run;
114  }
115  if (!keepGoing) {
116  msg() << "Tracker thread object: Just checked our run flag "
117  "and noticed it turned false..."
118  << std::endl;
119  }
120  }
121 #ifdef OSVR_TRACKER_THREAD_WRAP_WITH_TRY
122  } catch (std::exception const &e) {
123  warn() << "Tracker thread object: exiting because of caught "
124  "exception: "
125  << e.what() << std::endl;
126  m_run = false;
127  }
128 #endif
129  msg() << "Tracker thread object: functor exiting." << std::endl;
130 
131  if (!imageProcThreadObj.exiting()) {
132  msg() << "Telling image processing thread to exit." << std::endl;
133  imageProcThreadObj.signalExit();
134  }
135  imageProcThreadObj_ = nullptr;
136  if (m_imageThread.joinable()) {
137  m_imageThread.join();
138  }
139  }
140 
143  msg() << "Tracker thread object: triggerStop() called" << std::endl;
144  std::lock_guard<std::mutex> lock(m_runMutex);
145  m_run = false;
146  }
147 
149  util::time::TimeValue const &tv,
150  OSVR_OrientationReport const &report) {
152  if (!m_imuMessages.write(makeImuReport(imu, tv, report))) {
153  // no room for IMU message!
154  // msg() << "Dropped IMU orientation message!\n";
155  return false;
156  }
157  m_messageCondVar.notify_one();
158  return true;
159  }
160 
161  bool
163  util::time::TimeValue const &tv,
164  OSVR_AngularVelocityReport const &report) {
166  if (!m_imuMessages.write(makeImuReport(imu, tv, report))) {
167  // no room for IMU message!
168  return false;
169  }
170  m_messageCondVar.notify_one();
171  return true;
172  }
173 
174  bool TrackerThread::checkForDebugData(DebugArray &data) {
175  return m_debugDataMessages.read(data);
176  }
177 
178  void
179  TrackerThread::signalImageProcessingComplete(ImageOutputDataPtr &&imageData,
180  cv::Mat const &frame,
181  cv::Mat const &frameGray) {
182  m_imageData = std::move(imageData);
183  m_frame = frame;
184  m_frameGray = frameGray;
185  {
186  std::lock_guard<std::mutex> lock{m_messageMutex};
187  m_timeConsumingImageStepComplete = true;
188  }
189  m_messageCondVar.notify_one();
190  }
191 
192  std::ostream &TrackerThread::msg() const {
193  return std::cout << "[UnifiedTracker] ";
194  }
195 
196  std::ostream &TrackerThread::warn() const { return msg() << "Warning: "; }
197 
198  void TrackerThread::doFrame() {
199  // Check camera status.
200  if (!m_cam.ok()) {
201  // Hmm, camera seems bad. Might regain it? Skip for now...
202  warn() << "Camera is reporting it is not OK." << std::endl;
203  return;
204  }
205  // Trigger a grab.
206  if (!m_cam.grab()) {
207  // Again failing without quitting, in hopes we get better luck
208  // next time...
209  warn() << "Camera grab failed." << std::endl;
210  return;
211  }
212  // When we triggered the grab was a good guess of the time
213  // for the image before that got moved upstream into the ImageSource
214  // library.
215 
218  launchTimeConsumingImageStep();
219  if (m_bufferImu) {
220  setImuOverrideClock();
221  }
223  UpdatedBodyIndices imuIndices;
224 
225  bool finishedImage = false;
226  do {
227 
228  {
230  std::unique_lock<std::mutex> lock(m_messageMutex);
231  m_messageCondVar.wait(lock, [&] {
232  return m_timeConsumingImageStepComplete ||
233  !m_imuMessages.isEmpty();
234  });
235  if (m_timeConsumingImageStepComplete) {
239  finishedImage = true;
240  }
241  // Otherwise we have some IMU reports to keep us busy in the
242  // meantime.
243  } // unlock
244 
245  if (!finishedImage) {
248 
249  IMUMessage message = boost::none;
250  if (!m_imuMessages.read(message) || message.empty()) {
251  // couldn't read a message, or read an empty message
252  continue;
253  }
254 
255  // process it.
256  BodyId id;
257  ImuMessageCategory cat;
258  std::tie(id, cat) = processIMUMessage(message);
259  if (id.empty()) {
260  // processed but got an empty body ID
261  continue;
262  }
263 
264  if (m_bufferImu) {
265  // insert index into the list
266  imuIndices.insert(id);
267 
268  // if it's time, send a report even if we haven't gotten a
269  // video frame with useful things in it yet.
270  if (shouldSendImuReport()) {
271  updateReportingVector(imuIndices);
272  imuIndices.clear();
273  }
274  } else {
275 
277  updateReportingVector(id);
278  }
279  }
280  } while (!finishedImage);
281 
282  // OK, once we get here, we know the timeConsumingImageStep is complete.
283  if (!m_frame.data || !m_frameGray.data) {
284  // but it ended early due to error.
285  warn() << "Camera retrieve appeared to fail: frames had null "
286  "pointers!"
287  << std::endl;
288  return;
289  }
290 
291  if (!m_imageData) {
292  // but it failed to set the pointer? this is very strange...
293  warn() << "Initial image processing failed somehow!" << std::endl;
294  return;
295  }
296 
297  // Submit initial image data to the tracking system.
298  auto bodyIds =
299  m_trackingSystem.updateBodiesFromVideoData(std::move(m_imageData));
300  m_imageData.reset();
301 
302  // Sort those body IDs so we can merge them with the body IDs from any
303  // IMU messages we're about to process.
304  UpdatedBodyIndices sortedBodyIds{begin(bodyIds), end(bodyIds)};
305  if (m_bufferImu) {
306  for (auto &id : imuIndices) {
307  sortedBodyIds.insert(id);
308  }
309  imuIndices.clear();
310  }
311 
312  // process those IMU messages and add any unique IDs to the vector
313  // returned by the image tracker.
314  // We only want to process a fixed number of messages so we don't get
315  // stuck here in a loop without servicing the camera.
316  std::size_t numMessages = m_imuMessages.sizeGuess();
317  {
318  IMUMessage message = boost::none;
319  for (std::size_t i = 0; i < numMessages; ++i) {
320  if (!m_imuMessages.read(message)) {
321  // ran out of messages earlier than expected.
322  break;
323  }
324 
325  auto id = processIMUMessage(message).first;
326  if (!id.empty()) {
327  sortedBodyIds.insert(id);
328  }
329  }
330  }
331 
332  updateReportingVector(sortedBodyIds);
333  }
334 
335  std::pair<BodyId, ImuMessageCategory>
336  TrackerThread::processIMUMessage(IMUMessage const &m) {
337  return osvr::vbtracker::processImuMessage(m);
338  }
339 
340  BodyReporting *TrackerThread::getCamPoseReporting() const {
341 #ifdef OSVR_OUTPUT_CAMERA_POSE
342  return m_reportingVec[m_numBodies + extra_outputs::outputCamIndex]
343  .get();
344 #else
345  return nullptr;
346 #endif
347  }
348 
349  BodyReporting *TrackerThread::getIMUReporting() const {
350 #ifdef OSVR_OUTPUT_IMU
351  return m_reportingVec[m_numBodies + extra_outputs::outputImuIndex]
352  .get();
353 #else
354  return nullptr;
355 #endif
356  }
357 
358  BodyReporting *TrackerThread::getIMUCamReporting() const {
359 #ifdef OSVR_OUTPUT_IMU_CAM
360  return m_reportingVec[m_numBodies + extra_outputs::outputImuCamIndex]
361  .get();
362 #else
363  return nullptr;
364 #endif
365  }
366 
367  BodyReporting *TrackerThread::getHMDCamReporting() const {
368 
369 #ifdef OSVR_OUTPUT_HMD_CAM
370  return m_reportingVec[m_numBodies + extra_outputs::outputHMDCamIndex]
371  .get();
372 #else
373  return nullptr;
374 #endif
375  }
376 
377  void TrackerThread::updateExtraCameraReport() {
378 #ifndef OSVR_OUTPUT_CAMERA_POSE
379  return;
380 #endif
381 
382  using namespace Eigen;
383  using namespace std::chrono;
385  auto rightNow = our_clock::now();
386  if (!m_nextCameraPoseReport || rightNow > *m_nextCameraPoseReport) {
387  m_nextCameraPoseReport = rightNow + seconds(1);
388  BodyState state;
389  state.position() = m_trackingSystem.getCameraPose().translation();
390  state.setQuaternion(
391  Quaterniond(m_trackingSystem.getCameraPose().rotation()));
392  getCamPoseReporting()->updateState(util::time::getNow(), state);
393  }
394  }
395 
396  void TrackerThread::updateExtraIMUReports() {
397 
398 #if !defined(OSVR_OUTPUT_IMU) && !defined(OSVR_OUTPUT_IMU_CAM)
399  // Not reporting these special reports, do nothing.
400  return;
401 #endif
402 
403  if (!m_trackingSystem.getBody(BodyId(0)).hasIMU()) {
404  // no IMU, nothing to report.
405  return;
406  }
407  auto &imu = m_trackingSystem.getBody(BodyId(0)).getIMU();
408  if (!imu.hasPoseEstimate()) {
409  return;
410  }
411  Eigen::Quaterniond imuQuat = imu.getPoseEstimate();
412 
413 #ifdef OSVR_OUTPUT_IMU
414  {
415  BodyState state;
416  state.setQuaternion(imuQuat);
417  getIMUReporting()->updateState(imu.getLastUpdate(), state);
418  }
419 #endif
420 
421 #ifdef OSVR_OUTPUT_IMU_CAM
422 
423  {
424  BodyState state;
426  state.setQuaternion(getCameraRotation(m_trackingSystem) * imuQuat);
427  // Put this one up in the air a little so we can tell the
428  // difference.
429  state.position() = Eigen::Vector3d(0, 0.5, 0);
430  getIMUCamReporting()->updateState(imu.getLastUpdate(), state);
431  }
432 #endif
433  }
434 
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());
443  }
444 #endif
445  }
446  }
447 
448  bool TrackerThread::setupReportingVectorRoomTransforms() {
449  if (!m_trackingSystem.haveCameraPose()) {
451  return false;
452  }
453  if (m_setCameraPose) {
455  return true;
456  }
457 
459  m_setCameraPose = true;
460 
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) {
469 
472  auto reportingRaw = reporting.get();
473  if (reportingRaw == cameraPoseReporting ||
474  reportingRaw == imuAlignedReporting ||
475  reportingRaw == imuCameraSpaceReporting ||
476  reportingRaw == hmdCameraSpaceReporting) {
477  continue;
478  }
479 
482  reporting->setTrackerToRoomTransform(trackerToRoomXform);
483  }
484  return true;
485  }
486 
487  void
488  TrackerThread::updateReportingVector(UpdatedBodyIndices const &bodyIds) {
489  if (!setupReportingVectorRoomTransforms()) {
490  // false return means that we don't have calibration data yet, so no
491  // sense in reporting the other things.
492  return;
493  }
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(),
500  body.getState());
501  }
502 #endif
503  }
504 
506  updateExtraCameraReport();
507  updateExtraIMUReports();
508  }
509 
510  void TrackerThread::updateReportingVector(BodyId const bodyId) {
511  auto &body = m_trackingSystem.getBody(bodyId);
512  m_reportingVec[bodyId.value()]->updateState(body.getStateTime(),
513  body.getState());
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;
526  }
527  m_debugDataMessages.write(newDebugArray);
528  }
529  }
530 
531  void TrackerThread::launchTimeConsumingImageStep() {
534  m_timeConsumingImageStepComplete = false;
535 
537  imageProcThreadObj_->signalDoFrame();
538  }
539 } // namespace vbtracker
540 } // namespace osvr
Header declaring a C++11 finally or "scope-guard" construct.
void setQuaternion(Eigen::Quaterniond const &quaternion)
Intended for startup use.
Definition: PoseState.h:189
virtual bool ok() const =0
void getNow(TimeValue &tv)
Set the given TimeValue to the current time.
Definition: TimeValue.h:51
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...
Definition: TimeValueC.h:81
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)