2 ******************************************************************************
5 * @author Jonathan Brandmeyer <jonathan.brandmeyer@gmail.com>
7 * @addtogroup GCSPlugins GCS Plugins
9 * @addtogroup Config plugin
11 * @brief Implements low-level calibration algorithms
12 *****************************************************************************/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; Version 3 of the License, and no other
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include <calibration.h>
30 #include <assertions.h>
31 #include <Eigen/Eigen2Support>
35 #undef PRINTF_DEBUGGING
40 #if defined(__APPLE__) || defined(_WIN32)
43 extern "C" int isnan(double);
46 extern "C" int isinf(double);
51 * Alas, this implementation is a fairly direct copy of the contents of the
52 * following papers. You don't have a chance at understanding it without
53 * reading these first. Any bugs should be presumed to be JB's fault rather
54 * than Alonso and Shuster until proven otherwise.
56 * Reference [1]: "A New Algorithm for Attitude-independent magnetometer
57 * calibration", Robert Alonso and Malcolmn D. Shuster, Flight Mechanics/
58 * Estimation Theory Symposium, NASA Goddard, 1994, pp 513-527
60 * Reference [2]: Roberto Alonso and Malcolm D. Shuster, "Complete Linear
61 * Attitude-Independent Magnetometer Calibration", Journal of the Astronautical
62 * Sciences, Vol 50, No 4, 2002, pp 477-490
67 Vector3f
center(const Vector3f samples
[],
70 Vector3f
summation(Vector3f::Zero());
72 for (size_t i
= 0; i
< n_samples
; ++i
) {
73 summation
+= samples
[i
];
75 return summation
/ float(n_samples
);
78 void inv_fisher_information_matrix(Matrix3f
& fisherInv
,
80 const Vector3f samples
[],
84 MatrixXf
column_samples(3, n_samples
);
86 for (size_t i
= 0; i
< n_samples
; ++i
) {
87 column_samples
.col(i
) = samples
[i
];
90 column_samples
* column_samples
.transpose();
91 // Compute the inverse by taking advantage of the results symmetricness
92 fisher
.ldlt().solve(Matrix3f::Identity(), &fisherInv
);
94 // Eq 14 of the original reference. Computes the gradiant of the cost function
95 // with respect to the bias offset.
96 // @param samples: The vector of measurement samples
97 // @param mags: The vector of sample delta magnitudes
98 // @param b: The estimate of the bias
99 // @param mu: The trace of the noise matrix (3*noise)
100 // @return the negative gradiant of the cost function with respect to the
101 // current value of the estimate, b
102 Vector3f
neg_dJdb(const Vector3f samples
[],
109 Vector3f
summation(Vector3f::Zero());
110 float b_norm2
= b
.squaredNorm();
112 for (size_t i
= 0; i
< n_samples
; ++i
) {
113 summation
+= (mags
[i
] - 2 * samples
[i
].dot(b
) + b_norm2
- mu
)
114 * 2 * (samples
[i
] - b
);
117 return (1.0 / noise
) * summation
;
119 } // !namespace (anon)
121 Vector3f
twostep_bias_only(const Vector3f samples
[],
123 const Vector3f
& referenceField
,
127 Vector3f
*centeredSamples
= new Vector3f
[n_samples
];
129 float sampleDeltaMag
[n_samples
];
130 // eq 7 and 8 applied to samples
131 Vector3f avg
= center(samples
, n_samples
);
132 float refSquaredNorm
= referenceField
.squaredNorm();
133 float sampleDeltaMagCenter
= 0;
135 for (size_t i
= 0; i
< n_samples
; ++i
) {
136 // eq 9 applied to samples
137 centeredSamples
[i
] = samples
[i
] - avg
;
139 sampleDeltaMag
[i
] = samples
[i
].squaredNorm() - refSquaredNorm
;
140 sampleDeltaMagCenter
+= sampleDeltaMag
[i
];
142 sampleDeltaMagCenter
/= n_samples
;
147 inv_fisher_information_matrix(P_bb
, P_bb_inv
, centeredSamples
, n_samples
, noise
);
148 // Compute centered magnitudes
149 float sampleDeltaMagCentered
[n_samples
];
150 for (size_t i
= 0; i
< n_samples
; ++i
) {
151 sampleDeltaMagCentered
[i
] = sampleDeltaMag
[i
] - sampleDeltaMagCenter
;
155 Vector3f
estimate(Vector3f::Zero());
156 for (size_t i
= 0; i
< n_samples
; ++i
) {
157 estimate
+= sampleDeltaMagCentered
[i
] * centeredSamples
[i
];
159 estimate
= P_bb
* ((2 / noise
) * estimate
);
161 // Newton-Raphson gradient descent to the optimal solution
163 float mu
= -3 * noise
;
164 for (int i
= 0; i
< 6; ++i
) {
165 Vector3f neg_gradiant
= neg_dJdb(samples
, sampleDeltaMag
, n_samples
,
166 estimate
, mu
, noise
);
167 Matrix3f scale
= P_bb_inv
+ 4 / noise
* (avg
- estimate
) * (avg
- estimate
).transpose();
168 Vector3f neg_increment
;
169 scale
.ldlt().solve(neg_gradiant
, &neg_increment
);
170 // Note that the negative has been done twice
171 estimate
+= neg_increment
;
173 delete[] centeredSamples
;
178 // Private utility functions for the version of TWOSTEP that computes bias and
179 // scale factor vectors alone.
181 /** Compute the gradiant of the norm of b with respect to the estimate vector.
184 dnormb_dtheta(const Matrix
<double, 6, 1> & theta
)
186 return (Matrix
<double, 6, 1>() << 2 * theta
.coeff(0) / (1 + theta
.coeff(3)),
187 2 * theta
.coeff(1) / (1 + theta
.coeff(4)),
188 2 * theta
.coeff(2) / (1 + theta
.coeff(5)),
189 -pow(theta
.coeff(0), 2) / pow(1 + theta
.coeff(3), 2),
190 -pow(theta
.coeff(1), 2) / pow(1 + theta
.coeff(4), 2),
191 -pow(theta
.coeff(2), 2) / pow(1 + theta
.coeff(5), 2)).finished();
195 * Compute the gradiant of the cost function with respect to the optimal
199 dJdb(const Matrix
<double, 6, 1> & centerL
,
201 const Matrix
<double, 6, 1> & theta
,
202 const Matrix
<double, 6, 1> & dbdtheta
,
206 // \frac{\delta}{\delta \theta'} |b(\theta')|^2
207 Matrix
<double, 6, 1> vectorPart
= dbdtheta
- centerL
;
210 double normbthetaSquared
= 0;
211 for (unsigned i
= 0; i
< 3; ++i
) {
212 normbthetaSquared
+= theta
.coeff(i
) * theta
.coeff(i
) / (1 + theta
.coeff(3 + i
));
214 double scalarPart
= (centerMag
- centerL
.dot(theta
) + normbthetaSquared
216 return scalarPart
* vectorPart
;
219 /** Reconstruct the scale factor and bias offset vectors from the transformed
223 theta_to_sane(const Matrix
<double, 6, 1> & theta
)
225 return (Matrix
<double, 6, 1>() <<
226 theta
.coeff(0) / sqrt(1 + theta
.coeff(3)),
227 theta
.coeff(1) / sqrt(1 + theta
.coeff(4)),
228 theta
.coeff(2) / sqrt(1 + theta
.coeff(5)),
229 -1 + sqrt(1 + theta
.coeff(3)),
230 -1 + sqrt(1 + theta
.coeff(4)),
231 -1 + sqrt(1 + theta
.coeff(5))).finished();
233 } // !namespace (anon)
236 * Compute the scale factor and bias associated with a vector observer
237 * according to the equation:
238 * B_k = (I_{3x3} + D)^{-1} \times (O^T A_k H_k + b + \epsilon_k)
239 * where k is the sample index,
240 * B_k is the kth measurement
241 * I_{3x3} is a 3x3 identity matrix
242 * D is a 3x3 diagonal matrix of scale factors
243 * O is the orthogonal alignment matrix
244 * A_k is the attitude at the kth sample
245 * b is the bias in the global reference frame
246 * \epsilon_k is the noise at the kth sample
247 * This implementation makes the assumption that \epsilon is a constant white,
248 * gaussian noise source that is common to all k. The misalignment matrix O
249 * is not computed, and the off-diagonal elements of D, corresponding to the
250 * misalignment scale factors are not either.
252 * @param bias[out] The computed bias, in the global frame
253 * @param scale[out] The computed scale factor, in the sensor frame
254 * @param samples[in] An array of measurement samples.
255 * @param n_samples The number of samples in the array.
256 * @param referenceField The magnetic field vector at this location.
257 * @param noise The one-sigma squared standard deviation of the observed noise
260 void twostep_bias_scale(Vector3f
& bias
,
262 const Vector3f samples
[],
263 const size_t n_samples
,
264 const Vector3f
& referenceField
,
267 // Initial estimate for gradiant descent starts at eq 37a of ref 2.
269 // Define L_k by eq 30 and 28 for k = 1 .. n_samples
270 Matrix
<double, Dynamic
, 6> fullSamples(n_samples
, 6);
271 // \hbar{L} by eq 33, simplified by obesrving that the
272 Matrix
<double, 1, 6> centerSample
= Matrix
<double, 1, 6>::Zero();
273 // Define the sample differences z_k by eq 23 a)
274 double sampleDeltaMag
[n_samples
];
275 // The center value \hbar{z}
276 double sampleDeltaMagCenter
= 0;
277 double refSquaredNorm
= referenceField
.squaredNorm();
279 for (size_t i
= 0; i
< n_samples
; ++i
) {
280 fullSamples
.row(i
) << 2 * samples
[i
].transpose().cast
<double>(),
281 -(samples
[i
][0] * samples
[i
][0]),
282 -(samples
[i
][1] * samples
[i
][1]),
283 -(samples
[i
][2] * samples
[i
][2]);
284 centerSample
+= fullSamples
.row(i
);
286 sampleDeltaMag
[i
] = samples
[i
].squaredNorm() - refSquaredNorm
;
287 sampleDeltaMagCenter
+= sampleDeltaMag
[i
];
289 sampleDeltaMagCenter
/= n_samples
;
290 centerSample
/= n_samples
;
293 // double mu = -3*noise;
294 // The center value of mu, \bar{mu}
295 // double centerMu = mu;
296 // The center value of mu, \tilde{mu}
297 // double centeredMu = 0;
299 // Define \tilde{L}_k for k = 0 .. n_samples
300 Matrix
<double, Dynamic
, 6> centeredSamples(n_samples
, 6);
301 // Define \tilde{z}_k for k = 0 .. n_samples
302 double centeredMags
[n_samples
];
303 // Compute the term under the summation of eq 37a
304 Matrix
<double, 6, 1> estimateSummation
= Matrix
<double, 6, 1>::Zero();
305 for (size_t i
= 0; i
< n_samples
; ++i
) {
306 centeredSamples
.row(i
) = fullSamples
.row(i
) - centerSample
;
307 centeredMags
[i
] = sampleDeltaMag
[i
] - sampleDeltaMagCenter
;
308 estimateSummation
+= centeredMags
[i
] * centeredSamples
.row(i
).transpose();
310 estimateSummation
/= noise
; // note: paper supplies 1/noise
312 // By eq 37 b). Note, paper supplies 1/noise here
313 Matrix
<double, 6, 6> P_theta_theta_inv
= (1.0f
/ noise
) *
314 centeredSamples
.transpose() * centeredSamples
;
315 #ifdef PRINTF_DEBUGGING
316 SelfAdjointEigenSolver
<Matrix
<double, 6, 6> > eig(P_theta_theta_inv
);
317 std::cout
<< "P_theta_theta_inverse: \n" << P_theta_theta_inv
<< "\n\n";
318 std::cout
<< "P_\\tt^-1 eigenvalues: " << eig
.eigenvalues().transpose()
320 std::cout
<< "P_\\tt^-1 eigenvectors:\n" << eig
.eigenvectors() << "\n";
323 // The Fisher information matrix has a small eigenvalue, with a
324 // corresponding eigenvector of about [1e-2 1e-2 1e-2 0.55, 0.55,
325 // 0.55]. This means that there is relatively little information
326 // about the common gain on the scale factor, which has a
327 // corresponding effect on the bias, too. The fixup is performed by
328 // the first iteration of the second stage of TWOSTEP, as documented in
330 Matrix
<double, 6, 1> estimate
;
331 // By eq 37 a), \tilde{Fisher}^-1
332 P_theta_theta_inv
.ldlt().solve(estimateSummation
, &estimate
);
334 #ifdef PRINTF_DEBUGGING
335 Matrix
<double, 6, 6> P_theta_theta
;
336 P_theta_theta_inv
.ldlt().solve(Matrix
<double, 6, 6>::Identity(), &P_theta_theta
);
337 SelfAdjointEigenSolver
<Matrix
<double, 6, 6> > eig2(P_theta_theta
);
338 std::cout
<< "eigenvalues: " << eig2
.eigenvalues().transpose() << "\n";
339 std::cout
<< "eigenvectors: \n" << eig2
.eigenvectors() << "\n";
340 std::cout
<< "estimate summation: \n" << estimateSummation
.normalized()
344 // estimate i+1 = estimate_i - Fisher^{-1}(at estimate_i)*gradiant(theta)
345 // Fisher^{-1} = \tilde{Fisher}^-1 + \hbar{Fisher}^{-1}
347 while (count
-- > 0) {
349 Matrix
<double, 1, 6> db_dtheta
= dnormb_dtheta(estimate
);
351 Matrix
<double, 6, 1> dJ_dtheta
= dJdb(centerSample
,
352 sampleDeltaMagCenter
,
359 // Eq 39 (with double-negation on term inside parens)
360 db_dtheta
= centerSample
- db_dtheta
;
361 Matrix
<double, 6, 6> scale
= P_theta_theta_inv
+
362 (double(n_samples
) / noise
) * db_dtheta
.transpose() * db_dtheta
;
364 // eq 14b, mutatis mutandis (grumble, grumble)
365 Matrix
<double, 6, 1> increment
;
366 scale
.ldlt().solve(dJ_dtheta
, &increment
);
367 estimate
-= increment
;
370 // Transform the estimated parameters from [c | e] back into [b | d].
371 for (size_t i
= 0; i
< 3; ++i
) {
372 scale
.coeffRef(i
) = -1 + sqrt(1 + estimate
.coeff(3 + i
));
373 bias
.coeffRef(i
) = estimate
.coeff(i
) / sqrt(1 + estimate
.coeff(3 + i
));
378 // Private functions specific to the scale factor and orthogonal calibration
379 // version of TWOSTEP
382 * Reconstruct the symmetric E matrix from the last 6 elements of the estimate
385 Matrix3d
E_theta(const Matrix
<double, 9, 1> & theta
)
388 return (Matrix3d() <<
389 theta
.coeff(3), theta
.coeff(6), theta
.coeff(7),
390 theta
.coeff(6), theta
.coeff(4), theta
.coeff(8),
391 theta
.coeff(7), theta
.coeff(8), theta
.coeff(5)).finished();
395 * Compute the gradient of the squared norm of b with respect to theta. Note
396 * that b itself is just a function of theta. Therefore, this function
397 * computes \frac{\delta,\delta\theta'}\left|b(\theta')\right|
401 dnormb_dtheta(const Matrix
<double, 9, 1> & theta
)
403 // Re-form the matrix E from the vector of theta'
404 Matrix3d E
= E_theta(theta
);
405 // Compute (I + E)^-1 * c
408 E
.ldlt().solve(theta
.end
<3>(), &IE_inv_c
);
410 return (Matrix
<double, 9, 1>() << 2 * IE_inv_c
,
411 -IE_inv_c
.coeff(0) * IE_inv_c
.coeff(0),
412 -IE_inv_c
.coeff(1) * IE_inv_c
.coeff(1),
413 -IE_inv_c
.coeff(2) * IE_inv_c
.coeff(2),
415 -2 * IE_inv_c
.coeff(0) * IE_inv_c
.coeff(1),
416 -2 * IE_inv_c
.coeff(0) * IE_inv_c
.coeff(2),
417 -2 * IE_inv_c
.coeff(1) * IE_inv_c
.coeff(2)).finished();
420 // The gradient of the cost function with respect to theta, at a particular
422 // @param centerL: The center of the samples theta'
423 // @param centerMag: The center of the magnitude data
424 // @param theta: The estimate of the bias and scale factor
425 // @return the gradient of the cost function with respect to the
426 // current value of the estimate, theta'
428 dJ_dtheta(const Matrix
<double, 9, 1> & centerL
,
430 const Matrix
<double, 9, 1> & theta
,
431 const Matrix
<double, 9, 1> & dbdtheta
,
435 // \frac{\delta}{\delta \theta'} |b(\theta')|^2
436 Matrix
<double, 9, 1> vectorPart
= dbdtheta
- centerL
;
439 Matrix3d E
= E_theta(theta
);
441 (Matrix3d::Identity() + E
).ldlt().solve(theta
.start
<3>(), &rhs
);
442 double normbthetaSquared
= theta
.start
<3>().dot(rhs
);
444 double scalarPart
= (centerMag
- centerL
.dot(theta
) + normbthetaSquared
446 return scalarPart
* vectorPart
;
448 } // !namespace (anonymous)
451 * Compute the scale factor and bias associated with a vector observer
452 * according to the equation:
453 * B_k = (I_{3x3} + D)^{-1} \times (O^T A_k H_k + b + \epsilon_k)
454 * where k is the sample index,
455 * B_k is the kth measurement
456 * I_{3x3} is a 3x3 identity matrix
457 * D is a 3x3 symmetric matrix of scale factors
458 * O is the orthogonal alignment matrix
459 * A_k is the attitude at the kth sample
460 * b is the bias in the global reference frame
461 * \epsilon_k is the noise at the kth sample
463 * After computing the scale factor and bias matrices, the optimal estimate is
465 * \tilde{B}_k = (I_{3x3} + D)B_k - b
467 * This implementation makes the assumption that \epsilon is a constant white,
468 * gaussian noise source that is common to all k. The misalignment matrix O
471 * @param bias[out] The computed bias, in the global frame
472 * @param scale[out] The computed scale factor matrix, in the sensor frame.
473 * @param samples[in] An array of measurement samples.
474 * @param n_samples The number of samples in the array.
475 * @param referenceField The magnetic field vector at this location.
476 * @param noise The one-sigma squared standard deviation of the observed noise
479 void twostep_bias_scale(Vector3f
& bias
,
481 const Vector3f samples
[],
482 const size_t n_samples
,
483 const Vector3f
& referenceField
,
486 // Define L_k by eq 51 for k = 1 .. n_samples
487 Matrix
<double, Dynamic
, 9> fullSamples(n_samples
, 9);
488 // \hbar{L} by eq 52, simplified by observing that the common noise term
489 // makes this a simple average.
490 Matrix
<double, 1, 9> centerSample
= Matrix
<double, 1, 9>::Zero();
491 // Define the sample differences z_k by eq 23 a)
492 double sampleDeltaMag
[n_samples
];
493 // The center value \hbar{z}
494 double sampleDeltaMagCenter
= 0;
495 // The squared norm of the reference vector
496 double refSquaredNorm
= referenceField
.squaredNorm();
498 for (size_t i
= 0; i
< n_samples
; ++i
) {
499 fullSamples
.row(i
) << 2 * samples
[i
].transpose().cast
<double>(),
500 -(samples
[i
][0] * samples
[i
][0]),
501 -(samples
[i
][1] * samples
[i
][1]),
502 -(samples
[i
][2] * samples
[i
][2]),
503 -2 * (samples
[i
][0] * samples
[i
][1]),
504 -2 * (samples
[i
][0] * samples
[i
][2]),
505 -2 * (samples
[i
][1] * samples
[i
][2]);
507 centerSample
+= fullSamples
.row(i
);
509 sampleDeltaMag
[i
] = samples
[i
].squaredNorm() - refSquaredNorm
;
510 sampleDeltaMagCenter
+= sampleDeltaMag
[i
];
512 sampleDeltaMagCenter
/= n_samples
;
513 centerSample
/= n_samples
;
515 // Define \tilde{L}_k for k = 0 .. n_samples
516 Matrix
<double, Dynamic
, 9> centeredSamples(n_samples
, 9);
517 // Define \tilde{z}_k for k = 0 .. n_samples
518 double centeredMags
[n_samples
];
519 // Compute the term under the summation of eq 57a
520 Matrix
<double, 9, 1> estimateSummation
= Matrix
<double, 9, 1>::Zero();
521 for (size_t i
= 0; i
< n_samples
; ++i
) {
522 centeredSamples
.row(i
) = fullSamples
.row(i
) - centerSample
;
523 centeredMags
[i
] = sampleDeltaMag
[i
] - sampleDeltaMagCenter
;
524 estimateSummation
+= centeredMags
[i
] * centeredSamples
.row(i
).transpose();
526 estimateSummation
/= noise
;
529 Matrix
<double, 9, 9> P_theta_theta_inv
= (1.0f
/ noise
) *
530 centeredSamples
.transpose() * centeredSamples
;
532 #ifdef PRINTF_DEBUGGING
533 SelfAdjointEigenSolver
<Matrix
<double, 9, 9> > eig(P_theta_theta_inv
);
534 std::cout
<< "P_theta_theta_inverse: \n" << P_theta_theta_inv
<< "\n\n";
535 std::cout
<< "P_\\tt^-1 eigenvalues: " << eig
.eigenvalues().transpose()
537 std::cout
<< "P_\\tt^-1 eigenvectors:\n" << eig
.eigenvectors() << "\n";
540 // The current value of the estimate. Initialized to \tilde{\theta}^*
541 Matrix
<double, 9, 1> estimate
;
543 P_theta_theta_inv
.ldlt().solve(estimateSummation
, &estimate
);
545 // estimate i+1 = estimate_i - Fisher^{-1}(at estimate_i)*gradient(theta)
546 // Fisher^{-1} = \tilde{Fisher}^-1 + \hbar{Fisher}^{-1}
549 while (count
++ < 200 && eta
> 1e-8) {
550 static bool warned
= false;
551 if (hasNaN(estimate
)) {
552 std::cout
<< "WARNING: found NaN at time " << count
<< "\n";
556 SelfAdjointEigenSolver
<Matrix3d
> eig_E(E_theta(estimate
));
557 Vector3d S
= eig_E
.eigenvalues();
558 Vector3d W
; W
<< -1 + sqrt(1 + S
.coeff(0)),
559 -1 + sqrt(1 + S
.coeff(1)),
560 -1 + sqrt(1 + S
.coeff(2));
561 scale
= (eig_E
.eigenvectors() * W
.asDiagonal() *
562 eig_E
.eigenvectors().transpose()).cast
<float>();
564 (Matrix3f::Identity() + scale
).ldlt().solve(
565 estimate
.start
<3>().cast
<float>(), &bias
);
566 std::cout
<< "\n\nestimated bias: " << bias
.transpose()
567 << "\nestimated scale:\n" << scale
;
570 Matrix
<double, 1, 9> db_dtheta
= dnormb_dtheta(estimate
);
572 Matrix
<double, 9, 1> dJ_dtheta
= ::dJ_dtheta(centerSample
,
573 sampleDeltaMagCenter
,
579 // Eq 59, with reused storage on db_dtheta
580 db_dtheta
= centerSample
- db_dtheta
;
581 Matrix
<double, 9, 9> scale
= P_theta_theta_inv
+
582 (double(n_samples
) / noise
) * db_dtheta
.transpose() * db_dtheta
;
584 // eq 14b, mutatis mutandis (grumble, grumble)
585 Matrix
<double, 9, 1> increment
;
586 scale
.ldlt().solve(dJ_dtheta
, &increment
);
587 estimate
-= increment
;
588 eta
= increment
.dot(scale
* increment
);
589 std::cout
<< "eta: " << eta
<< "\n";
591 std::cout
<< "terminated at eta = " << eta
592 << " after " << count
<< " iterations\n";
594 if (!isnan(eta
) && !isinf(eta
)) {
595 // Transform the estimated parameters from [c | E] back into [b | D].
597 SelfAdjointEigenSolver
<Matrix3d
> eig_E(E_theta(estimate
));
598 Vector3d S
= eig_E
.eigenvalues();
599 Vector3d W
; W
<< -1 + sqrt(1 + S
.coeff(0)),
600 -1 + sqrt(1 + S
.coeff(1)),
601 -1 + sqrt(1 + S
.coeff(2));
602 scale
= (eig_E
.eigenvectors() * W
.asDiagonal() *
603 eig_E
.eigenvectors().transpose()).cast
<float>();
605 (Matrix3f::Identity() + scale
).ldlt().solve(
606 estimate
.start
<3>().cast
<float>(), &bias
);
608 // return nonsense data. The eigensolver can fall ingo
609 // an infinite loop otherwise.
610 // TODO: Add error code return
611 scale
= Matrix3f::Ones() * std::numeric_limits
<float>::quiet_NaN();
612 bias
= Vector3f::Zero();