Monado OpenXR Runtime
Loading...
Searching...
No Matches
internal_math.hpp
Go to the documentation of this file.
1// Copyright 2026, Beyley Cardellio
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief Generic math helpers for constellation optimizer code.
6 * @author Beyley Cardellio <ep1cm1n10n123@gmail.com>
7 * @ingroup tracking
8 */
9
10#pragma once
11
13
15#include "math/m_quatexpmap.hpp"
17
18#include <Eigen/Core>
19#include <Eigen/Geometry>
20
21#include "ceres/jet.h"
22#include "ceres/product_manifold.h"
23#include "ceres/autodiff_manifold.h"
24#include "ceres/ceres.h"
25
26#include "pose_optimize.hpp"
27#include "math.hpp"
28
29#include <ceres/crs_matrix.h>
30#include <cstring>
31#include <cmath>
32
33
34namespace xrt::tracking::constellation::optimizer {
35
36/*!
37 * Optimize directly on the pre-undistorted points, don't compute residuals in distorted pixel space.
38 *
39 * We don't have a "project" routine for the CV1 distortion algorithm, so we work in undistorted space, instead.
40 */
41constexpr bool kOptimizeUndistortedPoints = true;
42//! Print debug info about the residuals as we're computing them.
43constexpr bool kResidualDebugPrint = false;
44
45constexpr double kMaxGravity = MATH_GRAVITY_M_S2 * 1.01;
46constexpr double kMinGravity = MATH_GRAVITY_M_S2 * 0.99;
47
48/*!
49 * Fewer correspondences than this and the observation is dropped. Any less and this is likely a degenerate solve.
50 */
52
53/*!
54 * Where a camera observation stops being treated as data and starts being treated as a mistake, in standard
55 * deviations. Used to size the Huber loss function.
56 *
57 * The loss is applied to a whole observation rather than to each LED of it, so this is measured against the spread of
58 * the observation's chi-square rather than against a single reprojection error, and the threshold it produces grows
59 * with the number of correspondences.
60 */
61constexpr double kCameraHuberDeltaSigmas = 3.0;
62
63using namespace xrt::auxiliary::math;
64using namespace xrt::auxiliary::tracking;
65
66using Eigen::Map;
67using Eigen::Matrix;
68using Eigen::Matrix3d;
69using Eigen::MatrixBase;
70using Eigen::MatrixXd;
71using Eigen::Quaternion;
72using Eigen::Quaterniond;
73using Eigen::Vector;
74using Eigen::Vector2;
75using Eigen::Vector3;
76using Eigen::Vector3d;
77using Eigen::Vector4;
78using Eigen::VectorXd;
79
80using ceres::Jet;
81
82
83/*
84 *
85 * Types
86 *
87 */
88
89template <typename T, int N> struct SeedableVector
90{
91 using VectorType = Vector<T, N>;
92
93public: // Fields
94 VectorType vec;
95 bool seeded{false};
96
97 /*!
98 * The value this state is held linearized around, once it has been anchored.
99 *
100 * A marginalization prior is a statement about one particular linearization of the states it covers, and it
101 * goes on making that statement long after the solve has moved them. Everything that touches an anchored
102 * state has to keep linearizing it here, or the frozen factor and the live residuals stop describing the
103 * same point.
104 */
105 VectorType first_estimate;
106
107 //! Whether @ref first_estimate has been taken.
108 bool anchored{false};
109
110public: // Methods
111 SeedableVector() = default;
112
113 /*!
114 * Returns the data to use for a parameter, asserts you have seeded the data before this.
115 */
116 T *
118 {
119 assert(this->seeded);
120 return this->vec.data();
121 }
122
123 /*!
124 * Returns the vector for you to write into and seed. Assumes you have seeded it after this function is called.
125 */
126 VectorType &
128 {
129 this->seeded = true;
130 return this->vec;
131 }
132
133 //! Freezes the current value as the point this state is linearized at, unless it already has one.
134 void
136 {
137 assert(this->seeded);
138
139 if (this->anchored) {
140 return;
141 }
142
143 this->first_estimate = this->vec;
144 this->anchored = true;
145 }
146
147 //! The frozen linearization point, or null for a state that has never been anchored.
148 const T *
150 {
151 return this->anchored ? this->first_estimate.data() : nullptr;
152 }
153
154 //! Forgets the frozen linearization point, so the state linearizes wherever it currently is again.
155 void
157 {
158 this->anchored = false;
159 }
160
161 //! Drops everything held here, for a slot about to be handed to a state of its own.
162 void
164 {
165 this->seeded = false;
166 this->anchored = false;
167 }
168};
169
170// Covariance lives in the manifold's tangent space, which is one dimension smaller than the ambient
171// parameter block: the quaternion contributes 3 tangent dimensions, not 4.
172typedef Eigen::Matrix<double, kPoseCovarianceSize, kPoseCovarianceSize> PoseStateCovarianceMatrix;
173
174/*
175 *
176 * Parameter block types
177 *
178 */
179
180/*!
181 * The strength of gravity in the world, as a parameter block of its own.
182 *
183 * This is a property of where the capture was taken rather than of any one sensor, so every IMU in a solve shares this
184 * single block while each keeps its own @ref ImuBias.
185 */
186template <typename T> struct WorldGravity
187{
188 static_assert(!std::is_const_v<T>,
189 "block scalar must not be const — did a `T*` overload deduce T = const double?");
190
191 static constexpr int kNumParameters = 1;
192
193 static constexpr int kGravityMagIndex = 0;
194
195 //! Gravity in meters per second squared.
197
199
201
202 WorldGravity(Eigen::Ref<const Vector<T, kNumParameters>> parameters) : gravity_mag(parameters[kGravityMagIndex])
203 {}
204
205 void
206 pack(Eigen::Ref<Vector<T, kNumParameters>> parameters) const
207 {
208 parameters[kGravityMagIndex] = this->gravity_mag;
209 }
210
211 //! Gravity as a vector, which points along +Y since world up is -Y in the OpenCV space the optimizer works in.
213 toVector() const
214 {
215 return Vector3<T>(T(0), this->gravity_mag, T(0));
216 }
217};
218
219//! A parameter block for a camera with 2 degrees of freedom, pitch and roll. Yaw is fixed to 0, and it has no position.
220template <typename T> struct PitchRoll
221{
222 static_assert(!std::is_const_v<T>,
223 "block scalar must not be const — did a `T*` overload deduce T = const double?");
224
225 static constexpr int kNumParameters = 2;
226
227 Vector2<T> pitch_roll;
228
229 void
230 toQuaternion(Quaternion<T> &q) const
231 {
232 // @todo don't optimize on euler angles, use something without singularities.
233 // what are the chances someone has a camera pointed 90 degrees down?
234
235 q = Quaternion<T>(Eigen::AngleAxis<T>(pitch_roll.y(), Vector3<T>::UnitZ()));
236 q *= Quaternion<T>(Eigen::AngleAxis<T>(pitch_roll.x(), Vector3<T>::UnitX()));
237 }
238
239 Eigen::Transform<T, 3, Eigen::Isometry>
240 toTransform() const
241 {
243 this->toQuaternion(q);
244
245 return Eigen::Transform<T, 3, Eigen::Isometry>(q);
246 }
247
248 PitchRoll(Eigen::Ref<const Vector<T, kNumParameters>> parameters) : pitch_roll(parameters) {}
249};
250
251/*!
252 * Pose with 6 degrees of freedom, translation and rotation.
253 *
254 * Stored as a translation and a unit quaternion, so the ambient block is one larger than the 6 degrees of freedom
255 * it stands for. @ref PoseManifold supplies the retraction that takes the tangent space back to 6.
256 */
257template <typename T> struct Pose
258{
259 static_assert(!std::is_const_v<T>,
260 "block scalar must not be const — did a `T*` overload deduce T = const double?");
261
262 //! Size of the parameter block handed to Ceres: (tX, tY, tZ), (qX, qY, qZ, qW).
263 static constexpr int kNumParameters = 7;
264 //! Degrees of freedom the manifold exposes to the solver.
265 static constexpr int kNumTangentParameters = 6;
266 static constexpr int kNumResiduals = 6;
267
268 Vector3<T> translation;
269 Quaternion<T> rotation;
270
271 Pose(Quaternion<T> quat) : translation(Eigen::Vector3<T>::Zero()), rotation(quat) {}
272
273 Pose(xrt_pose pose) : Pose(map_quat(pose.orientation).cast<T>().normalized())
274 {
275 this->translation = Vector3<T>(pose.position.x, pose.position.y, pose.position.z);
276 }
277
278 Pose(Eigen::Ref<const Vector<T, kNumParameters>> parameters)
279 : translation(parameters.template segment<3>(kPoseStatePosStart)),
280 rotation(Quaternion<T>(parameters.template segment<4>(kPoseStateRotStart)))
281 {}
282
283 template <typename TransformType> Pose(const Eigen::Transform<TransformType, 3, Eigen::Isometry> &transform)
284 {
285 this->translation = transform.translation().template cast<T>();
286 Quaternion<TransformType> q(transform.rotation());
287 this->rotation = q.template cast<T>();
288 }
289
290 void
291 pack(Eigen::Ref<Vector<T, kNumParameters>> parameters) const
292 {
293 parameters.template segment<3>(kPoseStatePosStart) = this->translation;
294 parameters.template segment<4>(kPoseStateRotStart) = this->rotation.coeffs();
295 }
296
297 Eigen::Transform<T, 3, Eigen::Isometry>
298 toTransform() const
299 {
300 return Eigen::Translation<T, 3>(translation.x(), translation.y(), translation.z()) *
301 Eigen::Transform<T, 3, Eigen::Isometry>(this->rotation);
302 }
303
304 void
305 getQuaternion(Quaternion<T> &q) const
306 {
307 q = this->rotation;
308 }
309
311 toXrtPose() const
312 {
313 xrt_pose pose;
314
315 map_vec3(pose.position) = this->translation.template cast<float>();
316 map_quat(pose.orientation) = this->rotation.coeffs().template cast<float>();
317
318 return pose;
319 }
320
321private:
322 Pose() = default;
323};
324
325/*!
326 * Pose with 9 degrees of freedom, translation, rotation, and linear velocity.
327 *
328 * Stored as a `Pose` with an extra 3-parameter vector for the velocity.
329 *
330 * See @ref PoseWithVelocityManifold for the correct way to optimize on this,
331 * which takes tangent space from 10 -> 9 parameters (quat exponential map).
332 */
333template <typename T> struct PoseWithVelocity : Pose<T>
334{
335 static_assert(!std::is_const_v<T>,
336 "block scalar must not be const — did a `T*` overload deduce T = const double?");
337
338 static constexpr int kNumParameters = Pose<T>::kNumParameters + 3;
339 static constexpr int kNumTangentParameters = Pose<T>::kNumTangentParameters + 3;
340
341 Vector3<T> velocity;
342
343 PoseWithVelocity(xrt_pose p) : Pose<T>(p), velocity(Vector3<T>::Zero()) {}
344
345 template <typename Derived>
346 PoseWithVelocity(xrt_pose p, const Vector3<Derived> &velocity)
347 : Pose<T>(p), velocity(velocity.template cast<T>())
348 {}
349
350 PoseWithVelocity(Eigen::Ref<const Vector<T, kNumParameters>> parameters)
351 : Pose<T>(parameters.template segment<Pose<T>::kNumParameters>(0)),
352 velocity(Vector3<T>(parameters.template segment<3>(Pose<T>::kNumParameters)))
353 {}
354
355 void
356 pack(Eigen::Ref<Vector<T, kNumParameters>> parameters) const
357 {
358 this->Pose<T>::pack(parameters.template segment<Pose<T>::kNumParameters>(0));
359 parameters.template segment<3>(Pose<T>::kNumParameters) = this->velocity;
360 }
361};
362
363template <typename T> struct ImuExtrinsics
364{
365 static_assert(!std::is_const_v<T>,
366 "block scalar must not be const — did a `T*` overload deduce T = const double?");
367
368 //! (qX, qY, qZ, qW)
369 static constexpr int kNumParameters = 4;
370 //! Degrees of freedom
371 static constexpr int kNumTangentParameters = 3;
372
373 Quaternion<T> Q_imu_model;
374
375 template <typename Derived> ImuExtrinsics(Quaternion<Derived> quat) : Q_imu_model(quat.template cast<T>()) {}
376
377 ImuExtrinsics(Eigen::Ref<const Vector<T, kNumParameters>> parameters)
378 : Q_imu_model(Quaternion<T>(parameters.template segment<4>(0)))
379 {}
380
381 void
382 pack(Eigen::Ref<Vector<T, kNumParameters>> parameters) const
383 {
384 parameters.template segment<4>(0) = this->Q_imu_model.coeffs();
385 }
386};
387
388/*
389 *
390 * Manifolds
391 *
392 */
393
394/*!
395 * Right-multiplying quaternion manifold, matches our local convention.
396 */
398{
399 template <typename T>
400 bool
401 Plus(const T *x, const T *delta, T *x_plus_delta) const
402 {
403 const Eigen::Map<const Eigen::Quaternion<T>> q(x);
404 const Eigen::Map<const Eigen::Vector3<T>> w(delta);
405 Eigen::Map<Eigen::Quaternion<T>> result(x_plus_delta);
406
407 result = q * quat_exp_so3(w);
408 return true;
409 }
410
411 template <typename T>
412 bool
413 Minus(const T *y, const T *x, T *y_minus_x) const
414 {
415 const Eigen::Map<const Eigen::Quaternion<T>> q_y(y);
416 const Eigen::Map<const Eigen::Quaternion<T>> q_x(x);
417 Eigen::Map<Eigen::Vector3<T>> result(y_minus_x);
418
419 result = quat_ln_so3(Eigen::Quaternion<T>(q_x.conjugate() * q_y));
420 return true;
421 }
422};
423
424//! Our quaternion manifold functor
425typedef ceres::AutoDiffManifold<QuaternionManifoldFunctor, 4, 3> QuaternionManifold;
426
427//! Pose manifold, (tX, tY, tZ), (qX, qY, qZ, qW)
428typedef ceres::ProductManifold<ceres::EuclideanManifold<3>, QuaternionManifold> PoseManifold;
429
430//! Pose plus linear velocity, (tX, tY, tZ), (qX, qY, qZ, qW), (vX, vY, vZ). 10 ambient, 9 tangent.
431typedef ceres::ProductManifold<ceres::EuclideanManifold<3>, //
433 ceres::EuclideanManifold<3>> //
435
436
437/*
438 *
439 * Helper functions
440 *
441 */
442
443//! Converts an @ref xrt_pose into an Eigen Isometry3f
444inline Eigen::Isometry3f
446{
447 return {Eigen::Translation3f{map_vec3(p.position)} * map_quat(p.orientation)};
448}
449
450/*!
451 * Finds a LED in the model by its id.
452 *
453 * A LED's id is an opaque identifier chosen by the driver, not its position in the model: drivers are free to
454 * derive it from device-specific numbering (the Rift uses the headset's own LED indices, which include a slot for
455 * the IMU). So a blob's matched_device_led_id has to be resolved through here rather than used as an index.
456 *
457 * @return The index of the LED in the model, or -1 when no LED carries that id.
458 */
459static inline int32_t
460findLedIndexById(const t_constellation_tracker_led_model *leds_model, t_constellation_led_id_it led_id)
461{
462 for (size_t i = 0; i < leds_model->led_count; i++) {
463 if (leds_model->leds[i].id == led_id) {
464 return static_cast<int32_t>(i);
465 }
466 }
467
468 return -1;
469}
470
471/*!
472 * Measures how well one kind of factor was fit, at whatever the problem's parameters currently hold.
473 *
474 * Both factor types are whitened, so this is directly comparable between them and against 1.
475 *
476 * @param problem The problem, evaluated at its current parameter values.
477 * @param residual_blocks The blocks belonging to this kind of factor.
478 */
480evaluateFactorFitness(ceres::Problem &problem, const std::vector<ceres::ResidualBlockId> &residual_blocks);
481
482//! Converts a CRS matrix to a dense Eigen matrix
483void
484matrixCrsToEigen(const ceres::CRSMatrix &mat_crs, Eigen::MatrixXd &mat);
485
486/*!
487 * Marginalize the first @p num_base_states states out of the linearized system given by @p jacobian and
488 * @p residual, handing back the linear prior that replaces them.
489 *
490 * Inspired/specialized from MarginalizationError::marginalizeOut() from OKVIS.
491 */
492void
493marginalize(const VectorXd &residual, //
494 const MatrixXd &jacobian, //
495 const int num_base_states, //
496 VectorXd &out_e0, //
497 MatrixXd &out_j); //
498
499/*
500 *
501 * LED residuals
502 *
503 */
504
505//! X and Y in the image, for one LED.
506constexpr int kNumLedResiduals = 2;
507
508/*!
509 * How well a blob centroid locates the LED that lit it, in pixels.
510 *
511 * The camera residuals are reprojection errors, so they are only comparable to the whitened IMU residuals once they
512 * are divided by the noise that produced them. This is the same floor the per-frame PnP puts on its own residual
513 * sigma, since a blob has about this much pixels of noise, at least on CV1.
514 */
515constexpr double kBlobPositionSigmaPixels = 0.16;
516
517template <typename T>
518static void
519project_led(const camera_model &params,
520 const Eigen::Quaternion<T> &Q_cam_model,
521 const Eigen::Vector3<T> &T_cam_model,
522 const Eigen::Vector3<T> &T_model_led,
523 Eigen::Vector2<T> &out_projected_point,
524 T *led_depth_m)
525{
526 // Rotate the point around the pose's local frame.
527 Eigen::Vector3<T> T_cam_led = Q_cam_model * T_model_led;
528 // Translate the point into the camera frame.
529 T_cam_led += T_cam_model;
530
531 if (led_depth_m != nullptr) {
532 *led_depth_m = T_cam_led.z();
533 }
534
535 Eigen::Vector2<T> projected_point = {T(1e6), T(1e6)};
536
537 if (T_cam_led.z() <= T(0)) {
538 // The point is behind the camera, so we can't project it. Return a large value to indicate this.
539 out_projected_point = projected_point;
540 return;
541 }
542
544
545 (void)camera_models::project(dist, //
546 T_cam_led.x(), //
547 T_cam_led.y(), //
548 T_cam_led.z(), //
549 projected_point.x(), //
550 projected_point.y()); //
551
552 out_projected_point = projected_point;
553}
554
555template <typename T, typename Derived>
556static void
557computeLedResidual(const camera_model &params,
558 const Eigen::Vector3<T> &T_cam_model,
559 const Eigen::Quaternion<T> &Q_cam_model,
560 const Eigen::Vector2<T> &blob_position_2d,
561 const Eigen::Vector3<T> &T_model_led,
562 Eigen::MatrixBase<Derived> &residual)
563{
564 Eigen::Vector2<T> predicted_point;
565 T led_depth;
566 project_led<T>(params, //
567 Q_cam_model, //
568 T_cam_model, //
569 T_model_led, //
570 predicted_point, //
571 &led_depth); //
572
573 if constexpr (kResidualDebugPrint) {
574 if constexpr (std::is_same_v<T, double>) {
575 U_LOG_W("meas=(%f,%f)", blob_position_2d.x(), blob_position_2d.y());
576 U_LOG_W("pred=(%f,%f)", predicted_point.x(), predicted_point.y());
577
578 U_LOG_W("pose=(%f,%f,%f), (%f,%f,%f)", T_cam_model.x(), T_cam_model.y(), T_cam_model.z(),
579 Q_cam_model.x(), Q_cam_model.y(), Q_cam_model.z());
580
581 if (std::isnan(predicted_point.x()) || std::isnan(predicted_point.y())) {
582 U_LOG_W("Projected point is NaN, led_depth=%f", led_depth);
583
584 // XRT_DEBUGBREAK();
585 }
586 } else {
587 U_LOG_W("meas=(%f,%f)", blob_position_2d.x().a, blob_position_2d.y().a);
588 U_LOG_W("pred=(%f,%f)", predicted_point.x().a, predicted_point.y().a);
589
590 U_LOG_W("pose=(%f,%f,%f), (%f,%f,%f)", T_cam_model.x().a, T_cam_model.y().a, T_cam_model.z().a,
591 Q_cam_model.x().a, Q_cam_model.y().a, Q_cam_model.z().a);
592
593 if (std::isnan(predicted_point.x().a) || std::isnan(predicted_point.y().a)) {
594 U_LOG_W("Projected point is NaN, led_depth=%f", led_depth.a);
595
596 // XRT_DEBUGBREAK();
597 }
598 }
599 }
600
601 // Compute the residual
602 residual = predicted_point - blob_position_2d;
603
604 /*
605 * Provide a smooth penalty for points behind the camera, since if we just set a large residual,
606 * the solver will have no derivative to try to work back from. This solution penalizes bad
607 * poses, while pushing the optimizer towards the correct solution (no LEDs behind the camera).
608 */
609 if (led_depth < T(0.001)) {
610 residual += Eigen::Vector2<T>::Constant(T(1000) * (T(0.001) - T(led_depth)));
611 }
612}
613
614}; // namespace xrt::tracking::constellation::optimizer
Definition utility_northstar.h:779
Definition utility_northstar.h:270
Definition utility_northstar.h:16
#define U_LOG_W(...)
Log a message at U_LOGGING_WARN level, conditional on the global log level.
Definition u_logging.h:439
#define MATH_GRAVITY_M_S2
Standard gravity acceleration constant.
Definition m_api.h:53
void marginalize(const VectorXd &residual, const MatrixXd &jacobian, const int num_base_states, VectorXd &out_e0, MatrixXd &out_j)
Marginalize the first num_base_states states out of the linearized system given by jacobian and resid...
Definition internal_math.cpp:313
void matrixCrsToEigen(const ceres::CRSMatrix &mat_crs, Eigen::MatrixXd &mat)
Converts a CRS matrix to a dense Eigen matrix.
Definition internal_math.cpp:293
FactorFitness evaluateFactorFitness(ceres::Problem &problem, const std::vector< ceres::ResidualBlockId > &residual_blocks)
Measures how well one kind of factor was fit, at whatever the problem's parameters currently hold.
Definition internal_math.cpp:272
constexpr size_t kMinCorrespondencesPerObservation
Fewer correspondences than this and the observation is dropped.
Definition internal_math.hpp:51
constexpr double kCameraHuberDeltaSigmas
Where a camera observation stops being treated as data and starts being treated as a mistake,...
Definition internal_math.hpp:61
ceres::ProductManifold< ceres::EuclideanManifold< 3 >, QuaternionManifold, ceres::EuclideanManifold< 3 > > PoseWithVelocityManifold
Pose plus linear velocity, (tX, tY, tZ), (qX, qY, qZ, qW), (vX, vY, vZ). 10 ambient,...
Definition internal_math.hpp:434
static int32_t findLedIndexById(const t_constellation_tracker_led_model *leds_model, t_constellation_led_id_it led_id)
Finds a LED in the model by its id.
Definition internal_math.hpp:460
constexpr bool kResidualDebugPrint
Print debug info about the residuals as we're computing them.
Definition internal_math.hpp:43
Eigen::Isometry3f isometryFromPose(const xrt_pose &p)
Converts an xrt_pose into an Eigen Isometry3f.
Definition internal_math.hpp:445
ceres::ProductManifold< ceres::EuclideanManifold< 3 >, QuaternionManifold > PoseManifold
Pose manifold, (tX, tY, tZ), (qX, qY, qZ, qW)
Definition internal_math.hpp:428
constexpr int kNumLedResiduals
X and Y in the image, for one LED.
Definition internal_math.hpp:506
constexpr double kBlobPositionSigmaPixels
How well a blob centroid locates the LED that lit it, in pixels.
Definition internal_math.hpp:515
ceres::AutoDiffManifold< QuaternionManifoldFunctor, 4, 3 > QuaternionManifold
Our quaternion manifold functor.
Definition internal_math.hpp:425
constexpr bool kOptimizeUndistortedPoints
Optimize directly on the pre-undistorted points, don't compute residuals in distorted pixel space.
Definition internal_math.hpp:41
Interoperability helpers connecting internal math types and Eigen.
Base implementations for math library.
Helpers for quatexpmap math for bigceres usage.
Generic math helpers for constellation optimizer code.
C++-only functionality in the Math helper library.
Definition m_documentation.hpp:15
Eigen::Quaternion< typename Derived::Scalar > quat_exp_so3(Eigen::MatrixBase< Derived > const &vec)
Fully-templated free function for quaternion exponentiation, SO(3) version as described by Grassia.
Definition m_quatexpmap.hpp:119
static Eigen::Map< const Eigen::Quaternionf > map_quat(const struct xrt_quat &q)
Wrap an internal quaternion struct in an Eigen type, const overload.
Definition m_eigen_interop.hpp:40
static Eigen::Map< const Eigen::Quaternionf > orientation(const struct xrt_pose &pose)
Return a Eigen type wrapping a pose's orientation (const).
Definition m_eigen_interop.hpp:232
Eigen::Matrix< Scalar, 3, 1 > quat_ln_so3(Eigen::Quaternion< Scalar > const &quat)
Fully-templated free function for quaternion log map, SO(3) version.
Definition m_quatexpmap.hpp:148
static Eigen::Map< const Eigen::Vector3f > map_vec3(const struct xrt_vec3 &v)
Wrap an internal 3D vector struct in an Eigen type, const overload.
Definition m_eigen_interop.hpp:90
RANSAC PnP pose refinement.
Definition camera_model.h:21
struct t_camera_model_params calib_pinhole
An identity calibration model.
Definition camera_model.h:38
struct t_camera_model_params calib_true
The true calibration of the camera.
Definition camera_model.h:36
Floating point calibration data for a single calibrated camera.
Definition t_camera_models.h:62
The LED model is a series of points which define the real-world positions of all LEDs.
Definition t_constellation.h:301
size_t led_count
The number of LEDs in this model.
Definition t_constellation.h:305
struct t_constellation_tracker_led * leds
The LEDs in this model.
Definition t_constellation.h:303
t_constellation_led_id_it id
A unique ID for this LED, which distinguishes it from all other LEDs.
Definition t_constellation.h:278
How well one kind of sensor was fit, so that a bad solve can be attributed to the sensor that could n...
Definition math.hpp:31
static constexpr int kNumTangentParameters
Degrees of freedom.
Definition internal_math.hpp:371
static constexpr int kNumParameters
(qX, qY, qZ, qW)
Definition internal_math.hpp:369
A parameter block for a camera with 2 degrees of freedom, pitch and roll. Yaw is fixed to 0,...
Definition internal_math.hpp:221
Pose with 6 degrees of freedom, translation and rotation.
Definition internal_math.hpp:258
static constexpr int kNumParameters
Size of the parameter block handed to Ceres: (tX, tY, tZ), (qX, qY, qZ, qW).
Definition internal_math.hpp:263
static constexpr int kNumTangentParameters
Degrees of freedom the manifold exposes to the solver.
Definition internal_math.hpp:265
Pose with 9 degrees of freedom, translation, rotation, and linear velocity.
Definition internal_math.hpp:334
Right-multiplying quaternion manifold, matches our local convention.
Definition internal_math.hpp:398
void unseed()
Drops everything held here, for a slot about to be handed to a state of its own.
Definition internal_math.hpp:163
bool anchored
Whether first_estimate has been taken.
Definition internal_math.hpp:108
const T * anchorData() const
The frozen linearization point, or null for a state that has never been anchored.
Definition internal_math.hpp:149
T * data()
Returns the data to use for a parameter, asserts you have seeded the data before this.
Definition internal_math.hpp:117
VectorType first_estimate
The value this state is held linearized around, once it has been anchored.
Definition internal_math.hpp:105
VectorType & seedVec()
Returns the vector for you to write into and seed.
Definition internal_math.hpp:127
void releaseAnchor()
Forgets the frozen linearization point, so the state linearizes wherever it currently is again.
Definition internal_math.hpp:156
void anchor()
Freezes the current value as the point this state is linearized at, unless it already has one.
Definition internal_math.hpp:135
The strength of gravity in the world, as a parameter block of its own.
Definition internal_math.hpp:187
T gravity_mag
Gravity in meters per second squared.
Definition internal_math.hpp:196
Vector3< T > toVector() const
Gravity as a vector, which points along +Y since world up is -Y in the OpenCV space the optimizer wor...
Definition internal_math.hpp:213
A pose composed of a position and orientation.
Definition xrt_defines.h:513
Camera (un)projection C++ API for various camera models.