Monado OpenXR Runtime
lm_defines.hpp
Go to the documentation of this file.
1// Copyright 2022, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief Defines for Levenberg-Marquardt kinematic optimizer
6 * @author Moses Turner <moses@collabora.com>
7 * @author Charlton Rodda <charlton.rodda@collabora.com>
8 * @ingroup tracking
9 */
10#pragma once
11
12#include "math/m_mathinclude.h"
14#include "util/u_logging.h"
15#include "../kine_common.hpp"
16
17namespace xrt::tracking::hand::mercury::lm {
18
19#define LM_TRACE(lmh, ...) U_LOG_IFL_T(lmh.log_level, __VA_ARGS__)
20#define LM_DEBUG(lmh, ...) U_LOG_IFL_D(lmh.log_level, __VA_ARGS__)
21#define LM_INFO(lmh, ...) U_LOG_IFL_I(lmh.log_level, __VA_ARGS__)
22#define LM_WARN(lmh, ...) U_LOG_IFL_W(lmh.log_level, __VA_ARGS__)
23#define LM_ERROR(lmh, ...) U_LOG_IFL_E(lmh.log_level, __VA_ARGS__)
24
25// Inlines.
26template <typename T>
27inline T
28rad(T degrees)
29{
30 return degrees * T(M_PI / 180.f);
31}
32
33// Number of joints that our ML models output.
34static constexpr size_t kNumNNJoints = 21;
35
36static constexpr size_t kNumFingers = 5;
37
38// This is a lie for the thumb; we usually do the hidden metacarpal trick there
39static constexpr size_t kNumJointsInFinger = 5;
40
41static constexpr size_t kNumOrientationsInFinger = 4;
42
43// These defines look silly, but they are _extremely_ useful for doing work on this optimizer. Please don't remove them.
44#define USE_HAND_SIZE
45#define USE_HAND_TRANSLATION
46#define USE_HAND_ORIENTATION
47#define USE_EVERYTHING_ELSE
48
49// Not tested/tuned well enough; might make tracking slow.
50#undef USE_HAND_PLAUSIBILITY
51// Should work, but our neural nets aren't good enough yet.
52#undef USE_HAND_CURLS
53
54#undef RESIDUALS_HACKING
55
56static constexpr size_t kMetacarpalBoneDim = 3;
57static constexpr size_t kProximalBoneDim = 2;
58static constexpr size_t kFingerDim = kProximalBoneDim + 2;
59static constexpr size_t kThumbDim = kMetacarpalBoneDim + 2;
60static constexpr size_t kHandSizeDim = 1;
61static constexpr size_t kHandTranslationDim = 3;
62static constexpr size_t kHandOrientationDim = 3;
63
64
65// HRTC = Hand Residual Temporal Consistency
66static constexpr size_t kHRTC_HandSize = 1;
67static constexpr size_t kHRTC_RootBoneTranslation = 3;
68static constexpr size_t kHRTC_RootBoneOrientation = 3; // Direct difference between the two angle-axis rotations. This
69 // works well enough because the rotation should be small.
70
71static constexpr size_t kHRTC_ThumbMCPSwingTwist = 3;
72static constexpr size_t kHRTC_ThumbCurls = 2;
73
74static constexpr size_t kHRTC_ProximalSimilarity = 2;
75
76static constexpr size_t kHRTC_FingerMCPSwingTwist = 0;
77static constexpr size_t kHRTC_FingerPXMSwing = 2;
78static constexpr size_t kHRTC_FingerCurls = 2;
79static constexpr size_t kHRTC_CurlSimilarity = 1;
80
81
82static constexpr size_t kHandResidualOneSideXY = (kNumNNJoints * 2);
83static constexpr size_t kHandResidualOneSideDepth = 20; // one less because midxpm joint isn't used
84#ifdef USE_HAND_CURLS
85static constexpr size_t kHandResidualOneSideMatchCurls = 4;
86#else
87static constexpr size_t kHandResidualOneSideMatchCurls = 0;
88#endif
89static constexpr size_t kHandResidualOneSideSize =
90 kHandResidualOneSideXY + kHandResidualOneSideDepth + kHandResidualOneSideMatchCurls;
91
92static constexpr size_t kHandResidualTemporalConsistencyOneFingerSize = //
93 kHRTC_FingerMCPSwingTwist + //
94 kHRTC_FingerPXMSwing + //
95 kHRTC_FingerCurls + //
96#ifdef USE_HAND_PLAUSIBILITY //
97 kHRTC_CurlSimilarity + //
98#endif //
99 0;
100
101static constexpr size_t kHandResidualTemporalConsistencySize = //
102 kHRTC_RootBoneTranslation + //
103 kHRTC_RootBoneOrientation + //
104 kHRTC_ThumbMCPSwingTwist + //
105 kHRTC_ThumbCurls + //
106#ifdef USE_HAND_PLAUSIBILITY //
107 kHRTC_ProximalSimilarity + //
108#endif //
109 (kHandResidualTemporalConsistencyOneFingerSize * 4) + //
110 0;
111
112
114{
115public:
116 HandScalar stabilityRoot;
117 HandScalar stabilityCurlRoot;
118 HandScalar stabilityOtherRoot;
119
120 HandScalar stabilityThumbMCPSwing;
121 HandScalar stabilityThumbMCPTwist;
122
123 HandScalar stabilityFingerMCPSwing;
124 HandScalar stabilityFingerMCPTwist;
125
126 HandScalar stabilityFingerPXMSwingX;
127 HandScalar stabilityFingerPXMSwingY;
128
129 HandScalar stabilityRootPosition;
130 HandScalar stabilityHandSize;
131
132 HandScalar stabilityHandOrientationZ;
133 HandScalar stabilityHandOrientationXY;
134 HandStability(float root = 15.0f)
135 {
136 this->stabilityRoot = root;
137 this->stabilityCurlRoot = this->stabilityRoot * 0.03f;
138 this->stabilityOtherRoot = this->stabilityRoot * 0.03f;
139
140 this->stabilityThumbMCPSwing = this->stabilityCurlRoot * 1.5f;
141 this->stabilityThumbMCPTwist = this->stabilityCurlRoot * 1.5f;
142
143 this->stabilityFingerMCPSwing = this->stabilityCurlRoot * 3.0f;
144 this->stabilityFingerMCPTwist = this->stabilityCurlRoot * 3.0f;
145
146 this->stabilityFingerPXMSwingX = this->stabilityCurlRoot * 0.6f;
147 this->stabilityFingerPXMSwingY = this->stabilityCurlRoot * 1.6f;
148
149 this->stabilityRootPosition = this->stabilityOtherRoot * 25;
150 this->stabilityHandSize = this->stabilityOtherRoot * 1000;
151
152 this->stabilityHandOrientationZ = this->stabilityOtherRoot * 0.5;
153 this->stabilityHandOrientationXY = this->stabilityOtherRoot * 0.8;
154 }
155};
156
157
158static constexpr HandScalar kPlausibilityRoot = 1.0;
159static constexpr HandScalar kPlausibilityProximalSimilarity = 0.05f * kPlausibilityRoot;
160
161static constexpr HandScalar kPlausibilityCurlSimilarityHard = 0.10f * kPlausibilityRoot;
162static constexpr HandScalar kPlausibilityCurlSimilaritySoft = 0.05f * kPlausibilityRoot;
163
164
165constexpr size_t
166calc_input_size(bool optimize_hand_size)
167{
168 size_t out = 0;
169
170#ifdef USE_HAND_TRANSLATION
171 out += kHandTranslationDim;
172#endif
173
174#ifdef USE_HAND_ORIENTATION
175 out += kHandOrientationDim;
176#endif
177
178#ifdef USE_EVERYTHING_ELSE
179 out += kThumbDim;
180 out += (kFingerDim * 4);
181#endif
182
183#ifdef USE_HAND_SIZE
184 if (optimize_hand_size) {
185 out += kHandSizeDim;
186 }
187#endif
188
189 return out;
190}
191
192constexpr size_t
193calc_residual_size(bool stability, bool optimize_hand_size, int num_views)
194{
195 size_t out = 0;
196 for (int i = 0; i < num_views; i++) {
197 out += kHandResidualOneSideSize;
198 }
199
200 if (stability) {
201 out += kHandResidualTemporalConsistencySize;
202 }
203
204 if (optimize_hand_size) {
205 out += kHRTC_HandSize;
206 }
207
208 return out;
209}
210
211// Some templatable spatial types.
212// Heavily inspired by Eigen - one can definitely use Eigen instead, but here I'd rather have more control
213
214template <typename Scalar> struct Quat
215{
216 Scalar x = {};
217 Scalar y = {};
218 Scalar z = {};
219 Scalar w = {};
220
221 /// Default constructor - DOES NOT INITIALIZE VALUES
222 constexpr Quat() {}
223
224 /// Copy constructor
225 constexpr Quat(Quat const &) noexcept(std::is_nothrow_copy_constructible_v<Scalar>) = default;
226
227 /// Move constructor
228 Quat(Quat &&) noexcept(std::is_nothrow_move_constructible_v<Scalar>) = default;
229
230 /// Copy assignment
231 Quat &
232 operator=(Quat const &) = default;
233
234 /// Move assignment
235 Quat &
236 operator=(Quat &&) noexcept = default;
237
238 /// Construct from x, y, z, w scalars
239 template <typename Other>
240 constexpr Quat(Other x, Other y, Other z, Other w) noexcept // NOLINT(bugprone-easily-swappable-parameters)
241 : x{Scalar(x)}, y{Scalar(y)}, z{Scalar(z)}, w{Scalar(w)}
242 {}
243
244 /// So that we can copy a regular Vec2 into the real part of a Jet Vec2
245 template <typename Other> Quat(Quat<Other> const &other) : Quat(other.x, other.y, other.z, other.w) {}
246
247 static Quat
248 Identity()
249 {
250 return Quat(0.f, 0.f, 0.f, 1.f);
251 }
252};
253
254template <typename Scalar> struct Vec3
255{
256 // Note that these are not initialized, for performance reasons.
257 // If you want them initialized, use Zero() or something else
258 Scalar x = {};
259 Scalar y = {};
260 Scalar z = {};
261
262 /// Default constructor - DOES NOT INITIALIZE VALUES
263 constexpr Vec3() {}
264 /// Copy constructor
265 constexpr Vec3(Vec3 const &other) noexcept(std::is_nothrow_copy_constructible_v<Scalar>) = default;
266
267 /// Move constructor
268 Vec3(Vec3 &&) noexcept(std::is_nothrow_move_constructible_v<Scalar>) = default;
269
270 /// Copy assignment
271 Vec3 &
272 operator=(Vec3 const &) = default;
273
274 /// Move assignment
275 Vec3 &
276 operator=(Vec3 &&) noexcept = default;
277
278
279 template <typename Other>
280 constexpr Vec3(Other x, Other y, Other z) noexcept // NOLINT(bugprone-easily-swappable-parameters)
281 : x{Scalar(x)}, y{Scalar(y)}, z{Scalar(z)}
282 {}
283
284 template <typename Other> Vec3(Vec3<Other> const &other) : Vec3(other.x, other.y, other.z) {}
285
286 static Vec3
287 Zero()
288 {
289 return Vec3(0.f, 0.f, 0.f);
290 }
291
292 Scalar
293 norm_sqrd() const
294 {
295 Scalar len_sqrd = (Scalar)(0);
296
297 len_sqrd += this->x * this->x;
298 len_sqrd += this->y * this->y;
299 len_sqrd += this->z * this->z;
300 return len_sqrd;
301 }
302
303 // Norm, vector length, whatever.
304 // WARNING: Can return NaNs in the derivative part of Jets if magnitude is 0, because d/dx(sqrt(x)) at x=0 is
305 // undefined.
306 // There's no norm_safe because generally you need to add zero-checks somewhere *before* calling
307 // this, and it's not possible to produce correct derivatives from here.
308 Scalar
309 norm() const
310 {
311 Scalar len_sqrd = this->norm_sqrd();
312
313 return sqrt(len_sqrd);
314 }
315
316 // WARNING: Will return NaNs if vector magnitude is zero due to zero division.
317 // Do not call this on vectors with zero norm.
318 Vec3
319 normalized() const
320 {
321 Scalar norm = this->norm();
322
323 Vec3<Scalar> retval;
324 retval.x = this->x / norm;
325 retval.y = this->y / norm;
326 retval.z = this->z / norm;
327 return retval;
328 }
329};
330
331template <typename Scalar> struct Vec2
332{
333 Scalar x = {};
334 Scalar y = {};
335
336 /// Default constructor - DOES NOT INITIALIZE VALUES
337 constexpr Vec2() noexcept {}
338
339 /// Copy constructor
340 constexpr Vec2(Vec2 const &) noexcept(std::is_nothrow_copy_constructible_v<Scalar>) = default;
341
342 /// Move constructor
343 constexpr Vec2(Vec2 &&) noexcept(std::is_nothrow_move_constructible_v<Scalar>) = default;
344
345 /// Copy assignment
346 Vec2 &
347 operator=(Vec2 const &) = default;
348
349 /// Move assignment
350 Vec2 &
351 operator=(Vec2 &&) noexcept = default;
352
353 /// So that we can copy a regular Vec2 into the real part of a Jet Vec2
354 template <typename Other>
355 Vec2(Other x, Other y) // NOLINT(bugprone-easily-swappable-parameters)
356 noexcept(std::is_nothrow_constructible_v<Scalar, Other>)
357 : x{Scalar(x)}, y{Scalar(y)}
358 {}
359
360 template <typename Other>
361 Vec2(Vec2<Other> const &other) noexcept(std::is_nothrow_constructible_v<Scalar, Other>) : Vec2(other.x, other.y)
362 {}
363
364 static constexpr Vec2
365 Zero()
366 {
367 return Vec2(0.f, 0.f);
368 }
369};
370
371template <typename T> struct ResidualHelper
372{
373 T *out_residual = nullptr;
374 size_t out_residual_idx = 0;
375
376 ResidualHelper(T *residual) : out_residual(residual)
377 {
378 out_residual_idx = 0;
379 }
380
381 void
382 AddValue(T const &value)
383 {
384 this->out_residual[out_residual_idx++] = value;
385 }
386};
387
388
389template <typename T> struct OptimizerMetacarpalBone
390{
391 Vec2<T> swing = {};
392 T twist = {};
393};
394
395template <typename T> struct OptimizerFinger
396{
397 OptimizerMetacarpalBone<T> metacarpal = {};
398 Vec2<T> proximal_swing = {};
399 // Not Vec2.
400 T rots[2] = {};
401};
402
403template <typename T> struct OptimizerThumb
404{
405 OptimizerMetacarpalBone<T> metacarpal = {};
406 // Again not Vec2.
407 T rots[2] = {};
408};
409
410template <typename T> struct OptimizerHand
411{
412 T hand_size;
413 Vec3<T> wrist_post_location = {};
414 Vec3<T> wrist_post_orientation_aax = {};
415
416 Vec3<T> wrist_final_location = {};
417 Quat<T> wrist_final_orientation = {};
418
419 OptimizerThumb<T> thumb = {};
420 OptimizerFinger<T> finger[4] = {};
421};
422
423
424struct minmax
425{
426 HandScalar min = 0;
427 HandScalar max = 0;
428};
429
431{
432public:
433 minmax mcp_swing_x = {};
434 minmax mcp_swing_y = {};
435 minmax mcp_twist = {};
436
437 minmax pxm_swing_x = {};
438 minmax pxm_swing_y = {};
439
440 minmax curls[2] = {}; // int, dst
441};
442
444{
445public:
446 minmax hand_size = {};
447
448 minmax thumb_mcp_swing_x = {};
449 minmax thumb_mcp_swing_y = {};
450 minmax thumb_mcp_twist = {};
451 minmax thumb_curls[2] = {};
452
453 FingerLimit fingers[4] = {};
454
455 HandLimit()
456 {
457 hand_size = {MIN_HAND_SIZE, MAX_HAND_SIZE};
458
459 thumb_mcp_swing_x = {rad<HandScalar>(-60), rad<HandScalar>(60)};
460 thumb_mcp_swing_y = {rad<HandScalar>(-60), rad<HandScalar>(60)};
461 thumb_mcp_twist = {rad<HandScalar>(-35), rad<HandScalar>(35)};
462
463 for (int i = 0; i < 2; i++) {
464 thumb_curls[i] = {rad<HandScalar>(-90), rad<HandScalar>(40)};
465 }
466
467
468 HandScalar margin = 0.0001;
469
470 fingers[0].mcp_swing_y = {HandScalar(-0.19) - margin, HandScalar(-0.19) + margin};
471 fingers[1].mcp_swing_y = {HandScalar(0.00) - margin, HandScalar(0.00) + margin};
472 fingers[2].mcp_swing_y = {HandScalar(0.19) - margin, HandScalar(0.19) + margin};
473 fingers[3].mcp_swing_y = {HandScalar(0.38) - margin, HandScalar(0.38) + margin};
474
475
476 fingers[0].mcp_swing_x = {HandScalar(-0.02) - margin, HandScalar(-0.02) + margin};
477 fingers[1].mcp_swing_x = {HandScalar(0.00) - margin, HandScalar(0.00) + margin};
478 fingers[2].mcp_swing_x = {HandScalar(0.02) - margin, HandScalar(0.02) + margin};
479 fingers[3].mcp_swing_x = {HandScalar(0.04) - margin, HandScalar(0.04) + margin};
480
481
482 for (int finger_idx = 0; finger_idx < 4; finger_idx++) {
483 FingerLimit &finger = fingers[finger_idx];
484
485 // finger.mcp_swing_x = {rad<HandScalar>(-0.0001), rad<HandScalar>(0.0001)};
486 finger.mcp_twist = {rad<HandScalar>(-4), rad<HandScalar>(4)};
487
488 finger.pxm_swing_x = {rad<HandScalar>(-100), rad<HandScalar>(20)}; // ??? why is it reversed
489 finger.pxm_swing_y = {rad<HandScalar>(-20), rad<HandScalar>(20)};
490
491 for (int i = 0; i < 2; i++) {
492 finger.curls[i] = {rad<HandScalar>(-90), rad<HandScalar>(0)};
493 }
494 }
495 }
496};
497
498static const class HandLimit the_limit = {};
499
500
501template <typename T> struct StereographicObservation
502{
503 Vec2<T> obs[kNumNNJoints];
504};
505
506
507template <typename T> struct DepthObservation
508{
509 T depth_value[kNumNNJoints];
510};
511
512template <typename T> struct ResidualTracker
513{
514 T *out_residual = nullptr;
515 size_t out_residual_idx = {};
516
517 ResidualTracker(T *residual) : out_residual(residual) {}
518
519 void
520 AddValue(T const &value)
521 {
522 this->out_residual[out_residual_idx++] = value;
523 }
524};
525
526
528{
529 bool first_frame = true;
530 bool use_stability = false;
531 bool optimize_hand_size = true;
532 bool is_right = false;
533 float smoothing_factor;
534 int num_observation_views = 0;
535 one_frame_input *observation = nullptr;
536
537 HandScalar target_hand_size = {};
538 HandScalar hand_size_err_mul = {};
539 HandScalar depth_err_mul = {};
540
541
543
544 // Squashed final pose from last frame. We start from here.
545 // At some point this might turn into a pose-prediction instead, we'll see :)
546 Quat<HandScalar> this_frame_pre_rotation = {};
547 Vec3<HandScalar> this_frame_pre_position = {};
548
549 OptimizerHand<HandScalar> last_frame = {};
550
551 // The pose that will take you from the right camera's space to the left camera's space.
552 xrt_pose left_in_right = {};
553
554 // The translation part of the same pose, just easier for Ceres to consume
555 Vec3<HandScalar> left_in_right_translation = {};
556
557 // The orientation part of the same pose, just easier for Ceres to consume
558 Quat<HandScalar> left_in_right_orientation = {};
559
560 Eigen::Matrix<HandScalar, calc_input_size(true), 1> TinyOptimizerInput = {};
561};
562
563template <typename T> struct Translations55
564{
565 Vec3<T> t[kNumFingers][kNumJointsInFinger] = {};
566};
567
568template <typename T> struct Orientations54
569{
570 Quat<T> q[kNumFingers][kNumJointsInFinger] = {};
571};
572
573template <bool optimize_hand_size> struct CostFunctor
574{
575 KinematicHandLM &parent;
576 size_t num_residuals_;
577
578 template <typename T>
579 bool
580 operator()(const T *const x, T *residual) const;
581
582 CostFunctor(KinematicHandLM &in_last_hand, size_t const &num_residuals)
583 : parent(in_last_hand), num_residuals_(num_residuals)
584 {}
585
586 size_t
587 NumResiduals() const
588 {
589 return num_residuals_;
590 }
591};
592
593
594} // namespace xrt::tracking::hand::mercury::lm
Definition: lm_defines.hpp:431
Definition: lm_defines.hpp:444
u_logging_level
Logging level enum.
Definition: u_logging.h:43
@ U_LOGGING_INFO
Info messages: not very verbose, not indicating a problem.
Definition: u_logging.h:46
constexpr HandScalar MIN_HAND_SIZE
Definition: kine_common.hpp:90
Interoperability helpers connecting internal math types and Eigen.
Wrapper header for <math.h> to ensure pi-related math constants are defined.
Definition: lm_defines.hpp:574
Definition: lm_defines.hpp:215
Quat(Quat< Other > const &other)
So that we can copy a regular Vec2 into the real part of a Jet Vec2.
Definition: lm_defines.hpp:245
constexpr Quat(Quat const &) noexcept(std::is_nothrow_copy_constructible_v< Scalar >)=default
Copy constructor.
Quat(Quat &&) noexcept(std::is_nothrow_move_constructible_v< Scalar >)=default
Move constructor.
constexpr Quat()
Default constructor - DOES NOT INITIALIZE VALUES.
Definition: lm_defines.hpp:222
Definition: lm_defines.hpp:332
constexpr Vec2(Vec2 &&) noexcept(std::is_nothrow_move_constructible_v< Scalar >)=default
Move constructor.
constexpr Vec2(Vec2 const &) noexcept(std::is_nothrow_copy_constructible_v< Scalar >)=default
Copy constructor.
constexpr Vec2() noexcept
Default constructor - DOES NOT INITIALIZE VALUES.
Definition: lm_defines.hpp:337
Definition: lm_defines.hpp:255
Vec3(Vec3 &&) noexcept(std::is_nothrow_move_constructible_v< Scalar >)=default
Move constructor.
constexpr Vec3(Vec3 const &other) noexcept(std::is_nothrow_copy_constructible_v< Scalar >)=default
Copy constructor.
constexpr Vec3()
Default constructor - DOES NOT INITIALIZE VALUES.
Definition: lm_defines.hpp:263
Definition: lm_defines.hpp:425
Definition: kine_common.hpp:53
A pose composed of a position and orientation.
Definition: xrt_defines.h:465
Basic logging functionality.