27 #include <util/Stride.h>
28 static ::util::Stride debugStride{401};
32 inline void dumpKalmanDebugOuput(
const char name[],
const char expr[],
35 std::cout <<
"\n(Kalman Debug Output) " << name <<
" [" << expr
36 <<
"]:\n" << value << std::endl;
39 #define OSVR_KALMAN_DEBUG_OUTPUT(Name, Value) \
40 dumpKalmanDebugOuput(Name, #Value, Value)
57 #include <util/Stride.h>
65 #undef OSVR_DEBUG_VELOCITY
66 #undef OSVR_VARIANCE_PENALTY_FOR_FIXED_BEACONS
67 #undef OSVR_CHECK_BOUNDING_BOXES
68 #undef OSVR_TRY_LIMITING_ANGULAR_VELOCITY_CHANGE
69 #undef OSVR_DEBUG_EMISSION_DIRECTION
73 static const auto DIM_BEACON_CUTOFF_TO_SKIP_BRIGHTS = 4;
79 static const auto MISIDENTIFIED_BEACON_CUTOFF = 3;
83 static const auto SQUARED_MAX_RESIDUAL_FACTOR_FOR_ID_REJECT = 4;
84 SCAATKalmanPoseEstimator::SCAATKalmanPoseEstimator(
85 ConfigParams
const ¶ms)
86 : m_shouldSkipBright(params.shouldSkipBrightLeds),
87 m_maxResidual(params.maxResidual),
88 m_maxSquaredResidual(params.maxResidual * params.maxResidual),
89 m_maxZComponent(params.maxZComponent),
90 m_highResidualVariancePenalty(params.highResidualVariancePenalty),
91 m_beaconProcessNoise(params.beaconProcessNoise),
92 m_noveltyPenaltyBase(params.tuning.noveltyPenaltyBase),
93 m_noBeaconLinearVelocityDecayCoefficient(
94 params.noBeaconLinearVelocityDecayCoefficient),
95 m_brightLedVariancePenalty(params.brightLedVariancePenalty),
96 m_measurementVarianceScaleFactor(
97 params.measurementVarianceScaleFactor),
98 m_distanceMeasVarianceBase(params.tuning.distanceMeasVarianceBase),
99 m_distanceMeasVarianceIntercept(
100 params.tuning.distanceMeasVarianceIntercept),
101 m_extraVerbose(params.extraVerbose),
102 m_randEngine(
std::random_device()()) {
103 std::tie(m_minBoxRatio, m_maxBoxRatio) =
104 std::minmax({params.boundingBoxFilterRatio,
105 1.f / params.boundingBoxFilterRatio});
107 const auto maxSquaredResidual = params.maxResidual * params.maxResidual;
110 inline double xyDistanceFromMetersToPixels(
double xyDistance,
111 double depthInMeters,
112 CameraModel
const &cam) {
113 return (xyDistance / depthInMeters) * cam.focalLength;
115 inline double squaredXyDistanceFromMetersToPixels(
double xyDistance,
116 double depthInMeters,
117 CameraModel
const &cam) {
118 return (xyDistance * xyDistance / (depthInMeters * depthInMeters)) *
119 cam.focalLength * cam.focalLength;
125 bool gotMeasurement =
false;
126 double varianceFactor = 1;
128 const auto inBoundsID = leds.size();
130 auto skipBright =
false;
132 auto skipAll =
false;
133 auto numBeaconsToUse = std::size_t{0};
135 auto inBoundsBright = std::size_t{0};
136 auto inBoundsRound = std::size_t{0};
139 for (
auto const &ledPtr : leds) {
141 if (led.isBright()) {
145 auto boundingBoxRatioResult = inBoundingBoxRatioRange(led);
146 if (TriBool::True == boundingBoxRatioResult) {
152 if (inBoundsID - inBoundsBright >
153 DIM_BEACON_CUTOFF_TO_SKIP_BRIGHTS &&
154 m_shouldSkipBright) {
157 if (0 == inBoundsID) {
158 m_framesWithoutIdentifiedBlobs++;
160 m_framesWithoutIdentifiedBlobs = 0;
163 skipBright ? (inBoundsID - inBoundsBright) : inBoundsID;
164 if (m_lastUsableBeaconsSeen > 0 && numBeaconsToUse == 1) {
170 m_lastUsableBeaconsSeen = numBeaconsToUse;
179 kalman::predict(p.state, p.processModel, dt);
180 p.state.externalizeRotation();
183 auto numBad = std::size_t{0};
184 auto numGood = std::size_t{0};
185 auto goodLeds =
filterLeds(leds, skipBright, skipAll, numBad, p);
189 m_lastUsableBeaconsSeen = goodLeds.size();
196 auto atten = std::pow(m_noBeaconLinearVelocityDecayCoefficient, dt);
197 p.state.velocity() *= atten;
201 std::shuffle(begin(goodLeds), end(goodLeds), m_randEngine);
204 static ::util::Stride varianceStride{ 809 };
205 if (++varianceStride) {
212 cam.focalLength = p.camParams.focalLength();
213 cam.principalPoint = p.camParams.eiPrincipalPoint();
218 for (
auto &ledPtr : goodLeds) {
221 auto id = led.getID();
222 auto index = asIndex(
id);
224 auto &debug = p.beaconDebug[index];
226 auto localVarianceFactor = varianceFactor;
227 auto newIdentificationVariancePenalty =
228 std::pow(m_noveltyPenaltyBase, led.novelty());
232 if (p.beaconFixed[index]) {
233 beaconProcess.setNoiseAutocorrelation(0);
234 #ifdef OSVR_VARIANCE_PENALTY_FOR_FIXED_BEACONS
235 localVarianceFactor *= 2;
241 beaconProcess.setNoiseAutocorrelation(m_beaconProcessNoise);
242 kalman::predict(*(p.beacons[index]), beaconProcess, videoDt);
248 cvToVector(led.getLocationForTracking()).cast<double>());
252 meas.updateFromState(state);
263 Eigen::Vector2d residual = meas.getResidual(state);
264 auto squaredResidual = residual.squaredNorm();
268 auto depth = meas.getBeaconInCameraSpace().z();
269 auto maxSquaredResidual =
270 squaredXyDistanceFromMetersToPixels(m_maxResidual, depth, cam);
271 if (squaredResidual > maxSquaredResidual) {
281 if (squaredResidual >
282 squaredXyDistanceFromMetersToPixels(
283 SQUARED_MAX_RESIDUAL_FACTOR_FOR_ID_REJECT *
287 markAsPossiblyMisidentified(led);
293 localVarianceFactor *= m_highResidualVariancePenalty;
303 debug.residual.x = residual.x();
304 debug.residual.y = residual.y();
306 auto effectiveVariance =
309 localVarianceFactor * m_measurementVarianceScaleFactor *
310 newIdentificationVariancePenalty *
311 (led.isBright() ? m_brightLedVariancePenalty : 1.) *
312 p.beaconMeasurementVariance[index] / led.getMeasurement().area;
314 auto effectiveVariance = localVarianceFactor *
320 m_measurementVarianceScaleFactor *
321 newIdentificationVariancePenalty *
322 p.beaconMeasurementVariance[index] *
323 getVarianceFromBeaconDepth(depth);
325 debug.variance = effectiveVariance;
326 meas.setVariance(effectiveVariance);
333 if (!correction.stateCorrectionFinite) {
334 std::cout <<
"Non-finite state correction processing beacon "
335 << led.getOneBasedID().value() << std::endl;
338 #ifdef OSVR_TRY_LIMITING_ANGULAR_VELOCITY_CHANGE
346 static const auto MaxAngVelChangeFromOneBeacon = 3 * M_PI;
347 static const auto MaxAnglVelChangeSquared =
348 MaxAngVelChangeFromOneBeacon * MaxAngVelChangeFromOneBeacon;
349 auto angVelChangeSquared =
350 correction.stateCorrection.segment<3>(9).squaredNorm();
351 if (angVelChangeSquared > MaxAnglVelChangeSquared) {
352 std::cout <<
"Got too high of a angular velocity correction "
354 << led.getOneBasedID().value() <<
": magnitude "
355 << std::sqrt(angVelChangeSquared) << std::endl;
359 correction.finishCorrection();
361 gotMeasurement =
true;
364 handlePossiblyMisidentifiedLeds();
366 if (gotMeasurement) {
373 #ifdef OSVR_DEBUG_VELOCITY
375 static ::util::Stride s(77);
377 if (p.state.velocity().squaredNorm() > 0.01) {
380 <<
"Velocity: " << p.state.velocity().transpose()
383 double angSpeed = p.state.angularVelocity().norm();
384 if (angSpeed > 0.1) {
385 std::cout <<
"AngVel: " << angSpeed <<
" about "
386 << (p.state.angularVelocity() / angSpeed)
396 bool incrementProbation =
false;
397 if (0 == m_framesInProbation) {
400 incrementProbation = (numBad * 3 > numGood * 2);
405 incrementProbation = numBad * 2 > numGood;
406 if (!incrementProbation) {
408 m_framesInProbation = 0;
411 if (incrementProbation) {
412 m_framesInProbation++;
416 if (gotMeasurement) {
417 m_framesWithoutUtilizedMeasurements = 0;
419 if (inBoundsID > 0) {
422 m_framesWithoutUtilizedMeasurements++;
426 if (gotMeasurement && p.state.position().z() < 0) {
427 if (m_extraVerbose) {
428 std::cout <<
"Kalman detected wrong side of the looking glass! "
429 "We can't be behind the camera and be seen, but "
430 "our position is apparently "
431 << p.state.position().transpose() << std::endl;
436 Eigen::Quaterniond quat = p.state.getQuaternion();
437 pinholeCameraFlipPose(p.state.position(), quat);
440 pinholeCameraFlipVelocities(p.state.velocity(),
441 p.state.angularVelocity());
443 p.state.position() *= -1;
445 p.state.velocity() *= -1;
448 p.state.angularVelocity() =
449 get180aboutZ() * p.state.angularVelocity();
450 p.state.
setQuaternion(get180aboutZ() * p.state.getQuaternion());
457 LedPtrList
const &leds,
const bool skipBright,
const bool skipAll,
463 const Eigen::Matrix3d rotate =
469 begin(leds), end(leds), std::back_inserter(ret), [&](
Led *ledPtr) {
472 auto id = led.
getID();
473 auto index = asIndex(
id);
475 auto &debug = p.beaconDebug[index];
477 debug.measurement = led.getLocationForTracking();
490 (rotate * cvToVector(p.beaconEmissionDirection[index])).z();
491 #if OSVR_DEBUG_EMISSION_DIRECTION
502 if (makeOneBased(
id) == beaconInspected) {
503 static ::util::Stride s(20);
507 cvToVector(p.beaconEmissionDirection[index]);
509 <<
"Beacon " << beaconInspected.value()
510 <<
": alternate z component is " << altZ
511 <<
" and beacon emission direction is "
513 cvToVector(p.beaconEmissionDirection[index]))
520 if (zComponent > 0.) {
521 if (m_extraVerbose) {
523 <<
"Rejecting an LED at "
524 << led.getLocationForTracking() <<
" claiming ID "
525 << led.getOneBasedID().value()
526 <<
" thus emission vec "
528 cvToVector(p.beaconEmissionDirection[index]))
534 markAsPossiblyMisidentified(led);
542 if (zComponent > m_maxZComponent) {
545 if (m_extraVerbose) {
546 std::cout <<
"Skipping " << led.getOneBasedID().value()
547 <<
" with z component " << zComponent
553 if (skipBright && led.isBright()) {
557 #ifdef OSVR_CHECK_BOUNDING_BOXES
558 auto boundingBoxRatioResult = inBoundingBoxRatioRange(led);
563 if (TriBool::False == boundingBoxRatioResult) {
574 SCAATKalmanPoseEstimator::TriBool
575 SCAATKalmanPoseEstimator::inBoundingBoxRatioRange(
Led const &led) {
577 auto boundingBoxRatio =
580 if (boundingBoxRatio > m_minBoxRatio &&
581 boundingBoxRatio < m_maxBoxRatio) {
582 return TriBool::True;
584 return TriBool::False;
586 return TriBool::Unknown;
589 void SCAATKalmanPoseEstimator::markAsPossiblyMisidentified(Led &led) {
590 m_possiblyMisidentified.push_back(&led);
593 void SCAATKalmanPoseEstimator::markAsUsed(Led &led) {
598 double SCAATKalmanPoseEstimator::getVarianceFromBeaconDepth(
double depth) {
599 return std::pow(m_distanceMeasVarianceBase, depth * 100.) *
600 m_distanceMeasVarianceIntercept;
603 void SCAATKalmanPoseEstimator::handlePossiblyMisidentifiedLeds() {
604 m_ledsConsideredMisidentifiedLastFrame = m_possiblyMisidentified.size();
605 if (m_ledsConsideredMisidentifiedLastFrame > m_ledsUsed &&
606 m_ledsConsideredMisidentifiedLastFrame >
607 MISIDENTIFIED_BEACON_CUTOFF) {
608 std::cout <<
"Think our model is wrong: considered "
609 << m_ledsConsideredMisidentifiedLastFrame
610 <<
" beacons misidentified, while only used "
611 << m_ledsUsed << std::endl;
612 m_misIDConsideredOurFault =
true;
614 m_misIDConsideredOurFault =
false;
615 for (
auto &ledPtr : m_possiblyMisidentified) {
616 ledPtr->markMisidentified();
621 m_ledsUsedLastFrame = m_ledsUsed;
623 m_possiblyMisidentified.clear();
628 static const std::size_t MAX_PROBATION_FRAMES = 10;
632 static const std::size_t MAX_FRAMES_WITHOUT_ID_BLOBS = 25;
634 SCAATKalmanPoseEstimator::TrackingHealth
638 if (m_misIDConsideredOurFault) {
639 return TrackingHealth::NeedsHardResetNow;
642 auto needsReset = (m_framesInProbation > MAX_PROBATION_FRAMES);
645 (m_framesWithoutUtilizedMeasurements >
646 MAX_FRAMES_WITHOUT_MEASUREMENTS);
649 return TrackingHealth::NeedsHardResetNow;
652 if (m_framesWithoutIdentifiedBlobs > MAX_FRAMES_WITHOUT_ID_BLOBS) {
654 return TrackingHealth::SoftResetWhenBeaconsSeen;
657 return TrackingHealth::Functioning;
Helper class to keep track of the state of a blob over time. This is used to help determine the ident...
DeducedAugmentedProcessModel< ModelA, ModelB > makeAugmentedProcessModel(ModelA &a, ModelB &b)
bool knowBoundingBox() const
void setQuaternion(Eigen::Quaterniond const &quaternion)
Intended for startup use.
Header wrapping include of and for warning quieting.
Eigen::Quaterniond getCombinedQuaternion() const
StateSquareMatrix const & errorCovariance() const
P.
double duration(TimeValue const &a, TimeValue const &b)
Get a double containing seconds between the time points.
SquareMatrix< Dimension< T >::value > DimSquareMatrix
A square matrix, n x n, where n is the dimension of T.
Header file for class that tracks and identifies LEDs.
CorrectionInProgress< StateType, MeasurementType > beginCorrection(StateType &state, ProcessModelType &processModel, MeasurementType &meas)
ZeroBasedBeaconId getID() const
Tells which LED I am.
cv::Size2f boundingBoxSize() const
bool operator()(EstimatorInOutParams const &p, LedPtrList const &leds, osvr::util::time::TimeValue const &frameTime, double videoDt)
Standardized, portable parallel to struct timeval for representing both absolute times and time inter...
LedPtrList filterLeds(LedPtrList const &leds, const bool skipBright, const bool skipAll, std::size_t &numBad, EstimatorInOutParams const &p)
TrackingHealth getTrackingHealth()
DeducedAugmentedState< StateA, StateB > makeAugmentedState(StateA &a, StateB &b)
Factory function, akin to std::tie(), to make an augmented state.
osvr::util::time::TimeValue const & startingTime
Time that the state is coming in at.