OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
BeaconBasedPoseEstimator_Kalman.cpp
Go to the documentation of this file.
1 
12 // Copyright 2015 Sensics, Inc.
13 //
14 // Licensed under the Apache License, Version 2.0 (the "License");
15 // you may not use this file except in compliance with the License.
16 // You may obtain a copy of the License at
17 //
18 // http://www.apache.org/licenses/LICENSE-2.0
19 //
20 // Unless required by applicable law or agreed to in writing, software
21 // distributed under the License is distributed on an "AS IS" BASIS,
22 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 // See the License for the specific language governing permissions and
24 // limitations under the License.
25 
26 #include <iostream>
28 #if 0
29 template <typename T>
30 inline void dumpKalmanDebugOuput(const char name[], const char expr[],
31  T const &value) {
32  std::cout << "\n(Kalman Debug Output) " << name << " [" << expr << "]:\n"
33  << value << std::endl;
34 }
35 #endif
36 
37 // Internal Includes
39 #include "ImagePointMeasurement.h"
40 #include "cvToEigen.h"
41 
42 // Library/third-party includes
47 
48 #include <osvr/Util/EigenInterop.h>
49 
50 #include <opencv2/core/eigen.hpp>
51 
52 // Standard includes
53 // - none
54 
55 namespace osvr {
56 namespace vbtracker {
57  static const auto LOW_BEACON_CUTOFF = 5;
58 
59  static const auto DIM_BEACON_CUTOFF_TO_SKIP_BRIGHTS = 4;
60  static const auto BRIGHT_PENALTY = 8.;
61  bool BeaconBasedPoseEstimator::m_kalmanAutocalibEstimator(LedGroup &leds,
62  double dt) {
63  int const beaconsSize = m_beacons.size();
64  // Default measurement variance (for now, factor) per axis.
65  double varianceFactor = 1;
66 
67  auto maxBoxRatio = m_params.boundingBoxFilterRatio;
68  auto minBoxRatio = 1.f / m_params.boundingBoxFilterRatio;
69 
70  auto inBoundsID = std::size_t{0};
71  // Default to using all the measurements we can
72  auto skipBright = false;
73  {
74  auto totalLeds = leds.size();
75  auto identified = std::size_t{0};
76  auto inBoundsBright = std::size_t{0};
77  auto inBoundsRound = std::size_t{0};
78  for (auto const &led : leds) {
79  if (!led.identified()) {
80  continue;
81  }
82  identified++;
83  auto id = led.getID();
84  if (id >= beaconsSize) {
85  continue;
86  }
87  inBoundsID++;
88  if (led.isBright()) {
89  inBoundsBright++;
90  }
91 
92  if (led.getMeasurement().knowBoundingBox()) {
93  auto boundingBoxRatio =
94  led.getMeasurement().boundingBoxSize().height /
95  led.getMeasurement().boundingBoxSize().width;
96  if (boundingBoxRatio > minBoxRatio &&
97  boundingBoxRatio < maxBoxRatio) {
98  inBoundsRound++;
99  }
100  }
101  }
102 
105 #if 0
106  // Now we decide if we want to cut the variance artificially to
107  // reduce latency in low-beacon situations
108  if (inBoundsID < LOW_BEACON_CUTOFF) {
109  varianceFactor = 0.5;
110  }
111 #endif
112 
113  if (inBoundsID - inBoundsBright >
114  DIM_BEACON_CUTOFF_TO_SKIP_BRIGHTS &&
115  m_params.shouldSkipBrightLeds) {
116  skipBright = true;
117  }
118  if (0 == inBoundsID) {
119  m_framesWithoutIdentifiedBlobs++;
120  } else {
121  m_framesWithoutIdentifiedBlobs = 0;
122  }
123  }
124 
125  CameraModel cam;
126  cam.focalLength = m_camParams.focalLength();
127  cam.principalPoint = cvToVector(m_camParams.principalPoint());
128  ImagePointMeasurement meas{cam};
129 
130  kalman::ConstantProcess<kalman::PureVectorState<>> beaconProcess;
131 
132  const auto maxSquaredResidual =
133  m_params.maxResidual * m_params.maxResidual;
134  const auto maxZComponent = m_params.maxZComponent;
135  kalman::predict(m_state, m_model, dt);
136 
139  Eigen::Matrix3d rotate =
140  Eigen::Matrix3d(m_state.getCombinedQuaternion());
141  auto numBad = std::size_t{0};
142  auto numGood = std::size_t{0};
143  for (auto &led : leds) {
144  if (!led.identified()) {
145  continue;
146  }
147  auto id = led.getID();
148  if (id >= beaconsSize) {
149  continue;
150  }
151 
152  auto &debug = m_beaconDebugData[id];
153  debug.seen = true;
154  debug.measurement = led.getLocation();
155  if (skipBright && led.isBright()) {
156  continue;
157  }
158 
159  // Angle of emission checking
160  // If we transform the body-local emission vector, an LED pointed
161  // right at the camera will be -Z. Anything with a 0 or positive z
162  // component is clearly out, and realistically, anything with a z
163  // component over -0.5 is probably fairly oblique. We don't want to
164  // use such beacons since they can easily introduce substantial
165  // error.
166  double zComponent =
167  (rotate * cvToVector(m_beaconEmissionDirection[id])).z();
168  if (zComponent > 0.) {
169  if (m_params.extraVerbose) {
170  std::cout << "Rejecting an LED at " << led.getLocation()
171  << " claiming ID " << led.getOneBasedID()
172  << std::endl;
173  }
176  led.markMisidentified();
177 
181  numBad++;
182  continue;
183  } else if (zComponent > maxZComponent) {
186  continue;
187  }
188 
189 #if 0
190  if (led.getMeasurement().knowBoundingBox) {
195  auto boundingBoxRatio =
196  led.getMeasurement().boundingBox.height /
197  led.getMeasurement().boundingBox.width;
198  if (boundingBoxRatio < minBoxRatio ||
199  boundingBoxRatio > maxBoxRatio) {
201  numBad++;
202  continue;
203  }
204  }
205 #endif
206 
207  auto localVarianceFactor = varianceFactor;
208  auto newIdentificationVariancePenalty =
209  std::pow(2.0, led.novelty());
210 
213  if (m_beaconFixed[id]) {
214  beaconProcess.setNoiseAutocorrelation(0);
215  } else {
216  beaconProcess.setNoiseAutocorrelation(
217  m_params.beaconProcessNoise);
218  kalman::predict(*(m_beacons[id]), beaconProcess, dt);
219  }
220 
221  meas.setMeasurement(
222  Eigen::Vector2d(led.getLocation().x, led.getLocation().y));
223  led.markAsUsed();
224  auto state = kalman::makeAugmentedState(m_state, *(m_beacons[id]));
225  meas.updateFromState(state);
226  Eigen::Vector2d residual = meas.getResidual(state);
227  if (residual.squaredNorm() > maxSquaredResidual) {
228  // probably bad
229  numBad++;
230  localVarianceFactor *= m_params.highResidualVariancePenalty;
231  } else {
232  numGood++;
233  }
234  debug.residual.x = residual.x();
235  debug.residual.y = residual.y();
236  auto effectiveVariance =
237  localVarianceFactor * m_params.measurementVarianceScaleFactor *
238  newIdentificationVariancePenalty *
239  (led.isBright() ? BRIGHT_PENALTY : 1.) *
240  m_beaconMeasurementVariance[id] / led.getMeasurement().area;
241  debug.variance = effectiveVariance;
242  meas.setVariance(effectiveVariance);
243 
245  auto model =
246  kalman::makeAugmentedProcessModel(m_model, beaconProcess);
247  kalman::correct(state, model, meas);
248  m_gotMeasurement = true;
249  }
250 
252  bool incrementProbation = false;
253  if (0 == m_framesInProbation) {
254  // Let's try to keep a 3:2 ratio of good to bad when not "in
255  // probation"
256  incrementProbation = (numBad * 3 > numGood * 2);
257 
258  } else {
259  // Already in trouble, add a bit of hysteresis and raising the bar
260  // so we don't hop out easily.
261  incrementProbation = numBad * 2 > numGood;
262  if (!incrementProbation) {
263  // OK, we're good again
264  m_framesInProbation = 0;
265  }
266  }
267  if (incrementProbation) {
268  m_framesInProbation++;
269  }
270 
272  if (m_gotMeasurement) {
273  m_framesWithoutUtilizedMeasurements = 0;
274  } else {
275  if (inBoundsID > 0) {
278  m_framesWithoutUtilizedMeasurements++;
279  }
280  }
281 
284  m_rvec = eiQuatToRotVec(m_state.getQuaternion());
285  cv::eigen2cv(m_state.position().eval(), m_tvec);
286  return true;
287  }
288 
290  BeaconBasedPoseEstimator::GetPredictedState(double dt) const {
291  auto state = m_state;
292  auto model = m_model;
293  kalman::predict(state, model, dt);
294  state.postCorrect();
295  OSVR_PoseState ret;
296  util::eigen_interop::map(ret).rotation() = state.getQuaternion();
297  util::eigen_interop::map(ret).translation() =
298  m_convertInternalPositionRepToExternal(state.position());
299  return ret;
300  }
301 
302 } // namespace vbtracker
303 } // namespace osvr
DeducedAugmentedProcessModel< ModelA, ModelB > makeAugmentedProcessModel(ModelA &a, ModelB &b)
OSVR_Quaternion rotation
Orientation as a unit quaternion.
Definition: Pose3C.h:58
Header wrapping include of and for warning quieting.
Eigen::Quaterniond getCombinedQuaternion() const
Definition: PoseState.h:238
bool extraVerbose
Extra verbose developer debugging messages.
Definition: ConfigParams.h:247
Header file for class that tracks and identifies LEDs.
Header.
A structure defining a 3D (6DOF) rigid body pose: translation and rotation.
Definition: Pose3C.h:54
Header for interoperation between the Eigen math library, the internal mini math library, and VRPN's quatlib.
bool correct(StateType &state, ProcessModelType &processModel, MeasurementType &meas, bool cancelIfNotFinite=true)
DeducedAugmentedState< StateA, StateB > makeAugmentedState(StateA &a, StateB &b)
Factory function, akin to std::tie(), to make an augmented state.