27 #undef OSVR_UVBI_DUMP_CALIB_LOG
36 #include <boost/assert.hpp>
40 #ifdef OSVR_UVBI_DUMP_CALIB_LOG
49 #ifdef OSVR_UVBI_DUMP_CALIB_LOG
55 namespace filters = util::filters;
65 static const auto LINEAR_VELOCITY_CUTOFF = 0.75;
66 static const auto ANGULAR_VELOCITY_CUTOFF = .75;
67 static const std::size_t REQUIRED_SAMPLES = 15;
72 static const auto NEAR_MESSAGE_CUTOFF = 0.4;
74 RoomCalibration::RoomCalibration(Eigen::Vector3d
const &camPosition,
76 : m_lastVideoData(util::time::
getNow()),
77 m_suppliedCamPosition(camPosition),
78 m_cameraIsForward(cameraIsForward) {}
81 BodyTargetId
const &target)
const {
90 if (!haveVideoData()) {
93 return target.first == m_imuBody;
98 return m_videoTarget == target;
104 Eigen::Quaterniond
const &quat) {
105 if (!wantVideoData(sys, target)) {
110 if (!xlate.array().allFinite() || !quat.coeffs().array().allFinite()) {
114 if (!haveVideoData()) {
115 msg() <<
"Got first video report from target " << target
118 bool firstData = !haveVideoData();
119 m_videoTarget = target;
120 auto dt =
duration(timestamp, m_lastVideoData);
121 m_lastVideoData = timestamp;
126 Eigen::Quaterniond prevRot = m_poseFilter.getOrientation();
127 Eigen::Vector3d prevXlate = m_poseFilter.getPosition();
131 m_poseFilter.filter(dt, xlate, quat);
139 Eigen::Quaterniond rTc =
140 m_imuOrientation * m_poseFilter.getOrientation().inverse();
141 Eigen::Vector3d rTcln = util::quat_ln(rTc);
150 m_linVel = XlateDeriv::computeMagnitude(
151 XlateDeriv::compute(prevXlate, m_poseFilter.getPosition(), dt));
152 m_angVel = QuatDeriv::computeMagnitude(
153 QuatDeriv::compute(prevRot, m_poseFilter.getOrientation(), dt));
156 #ifdef OSVR_UVBI_DUMP_CALIB_LOG
159 static bool output =
false;
164 m_poseFilter.getPosition())
166 m_poseFilter.getOrientation())
170 }
else if (!output) {
172 std::ofstream os(
"calib.csv");
174 msg() <<
"Done writing calibration log data." << std::endl;
177 #endif // OSVR_UVBI_DUMP_CALIB_LOG
180 if (m_linVel < LINEAR_VELOCITY_CUTOFF &&
181 m_angVel < ANGULAR_VELOCITY_CUTOFF) {
183 if (m_steadyVideoReports == 0) {
184 msg() <<
"Hold still, performing room calibration";
186 msgStream() <<
"." << std::flush;
187 m_rTc_ln_accum += rTcln;
188 ++m_steadyVideoReports;
191 msgStream() <<
"\n" << std::endl;
194 handleExcessVelocity(xlate.z());
197 void RoomCalibration::handleExcessVelocity(
double zTranslation) {
199 if (m_steadyVideoReports > 0) {
201 msgStream() << std::endl;
203 msg() <<
"Restarting ";
204 if (m_linVel >= LINEAR_VELOCITY_CUTOFF) {
205 msgStream() <<
" - Linear velocity too high (" << m_linVel
208 if (m_angVel >= ANGULAR_VELOCITY_CUTOFF) {
209 msgStream() <<
" - Angular velocity too high (" << m_angVel
214 m_steadyVideoReports = 0;
215 m_rTc_ln_accum = Eigen::Vector3d::Zero();
217 switch (m_instructionState) {
218 case InstructionState::Uninstructed:
219 if (zTranslation > NEAR_MESSAGE_CUTOFF) {
221 <<
"NOTE: For best results, during tracker/server "
222 "startup, hold your head/HMD still closer than "
223 << NEAR_MESSAGE_CUTOFF
224 <<
" meters from the tracking camera for a few "
225 "seconds, then rotate slowly in all directions.";
227 m_instructionState = InstructionState::ToldToMoveCloser;
230 case InstructionState::ToldToMoveCloser:
231 if (zTranslation < (0.9 * NEAR_MESSAGE_CUTOFF)) {
232 instructions() <<
"That distance looks good, rotate the device "
233 "gently until you get a 'Hold still' "
236 m_instructionState = InstructionState::ToldDistanceIsGood;
239 case InstructionState::ToldDistanceIsGood:
244 void RoomCalibration::processIMUData(TrackingSystem
const &sys,
247 Eigen::Quaterniond
const &quat) {
248 if (haveIMUData() && m_imuBody != body) {
250 #ifdef OSVR_UVBI_ASSUME_SINGLE_IMU
251 throw std::logic_error(
252 "RoomCalibration just received data from a second IMU, but the "
253 "single IMU assumption define is still in "
259 if (!quat.coeffs().array().allFinite()) {
261 msg() <<
"Non-finite quat" << std::endl;
265 if (!haveIMUData()) {
267 if (!sys.isValidBodyId(body)) {
268 msg() <<
"Invalid body ID" << std::endl;
271 auto &trackedBody = sys.getBody(body);
272 if (trackedBody.getNumTargets() == 0) {
278 msg() <<
"Got first IMU report from body " << body.value()
283 BOOST_ASSERT_MSG(m_imuBody == body,
"BodyID for incoming data and "
284 "known IMU must be identical at "
290 quat.w() >= 0. ? quat : Eigen::Quaterniond(-quat.coeffs());
295 util::flipQuatSignToMatch(m_imuOrientation, quat);
303 msg() <<
"Room calibration process complete." << std::endl;
329 Eigen::Quaterniond iRc =
330 util::quat_exp(m_rTc_ln_accum / m_steadyVideoReports);
332 if (m_cameraIsForward) {
334 iRc = Eigen::AngleAxisd(-yaw, Eigen::Vector3d::UnitY()) * iRc;
336 m_imuYaw = -yaw * util::radians;
341 msg() <<
"camera pose AKA rTc: translation: "
342 << m_cameraPose.translation().transpose() <<
" rotation: ";
343 Eigen::AngleAxisd rot(m_cameraPose.rotation());
344 msgStream() << rot.angle() <<
" radians about "
345 << rot.axis().transpose() << std::endl;
347 m_calibComplete =
true;
350 sys.setCameraPose(getCameraPose());
352 auto yaw = getCalibrationYaw(imu.getBody().
getId());
354 imu.setCalibrationYaw(*yaw);
360 boost::optional<util::Angle>
361 RoomCalibration::getCalibrationYaw(
BodyId const &body)
const {
362 BOOST_ASSERT_MSG(calibrationComplete(),
"Not valid to call "
363 "getCalibrationYaw() unless "
364 "calibration is complete!");
365 if (m_imuBody == body) {
372 BOOST_ASSERT_MSG(calibrationComplete(),
"Not valid to call "
373 "getCameraPose() unless "
374 "calibration is complete!");
378 bool RoomCalibration::finished()
const {
379 return m_steadyVideoReports >= REQUIRED_SAMPLES;
382 std::ostream &RoomCalibration::msgStream()
const {
return std::cout; }
383 std::ostream &RoomCalibration::msg()
const {
384 return msgStream() <<
"[Unified Tracker: Room Calibration] ";
387 std::ostream &RoomCalibration::instructions()
const {
388 msgStream() <<
"\n\n";
391 void RoomCalibration::endInstructions()
const {
392 msgStream() <<
"\n\n" << std::endl;
394 bool isRoomCalibrationComplete(TrackingSystem
const &sys) {
396 if (!sys.haveCameraPose()) {
401 auto numIMUs = std::size_t{0};
402 bool complete =
true;
403 forEachIMU(sys, [&](TrackedBodyIMU
const &imu) {
404 complete = complete && imu.calibrationYawKnown();
408 #ifdef OSVR_UVBI_ASSUME_SINGLE_IMU
410 throw std::logic_error(
"More than one IMU system wide, but the "
411 "single IMU assumption define is still in "
void getNow(TimeValue &tv)
Set the given TimeValue to the current time.
auto extractYaw(T const &quat) -> decltype(std::atan(quat.w()))
BodyId getId() const
Gets the body ID within the tracking system.
::OSVR_TimeValue TimeValue
C++-friendly typedef for the OSVR_TimeValue structure.
double duration(TimeValue const &a, TimeValue const &b)
Get a double containing seconds between the time points.
void output(std::ostream &os) const
detail::CellGroup< T > cellGroup(const char *groupHeader, T const &data)
Helper function to create a cell group with a group header prefix.
Filters for use with Eigen datatypes.
bool wantVideoData(TrackingSystem const &sys, BodyTargetId const &target) const
Header containing preprocessor defines for various simplifying assumptions made for the sake of itera...
void processVideoData(TrackingSystem const &sys, BodyTargetId const &target, util::time::TimeValue const ×tamp, Eigen::Vector3d const &xlate, Eigen::Quaterniond const &quat)
std::size_t numDataRows() const
Isometry3< typename Derived1::Scalar > makeIsometry(Eigen::MatrixBase< Derived1 > const &translation, Eigen::RotationBase< Derived2, 3 > const &rotation)
A simpler, functional-style alternative to .fromPositionOrientationScale when no scaling is performed...
Standardized, portable parallel to struct timeval for representing both absolute times and time inter...
detail::Cell< T > cell(const char *header, T const &data)
Helper free function to make a CSV cell.
Eigen::Isometry3d getCameraPose() const
bool postCalibrationUpdate(TrackingSystem &sys)