Monado OpenXR Runtime
Loading...
Searching...
No Matches
t_tracker_psmv_fusion.cpp
Go to the documentation of this file.
1// Copyright 2019, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief PS Move tracker code that is expensive to compile.
6 *
7 * Typically built as a part of t_kalman.cpp to reduce incremental build times.
8 *
9 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
10 * @author Pete Black <pblack@collabora.com>
11 * @author Jakob Bornecrantz <jakob@collabora.com>
12 * @ingroup aux_tracking
13 */
14
15#include "tracking/t_fusion.hpp"
18
19#include "math/m_api.h"
21
22#include "util/u_misc.h"
23
24#include "flexkalman/AbsoluteOrientationMeasurement.h"
25#include "flexkalman/FlexibleKalmanFilter.h"
26#include "flexkalman/FlexibleUnscentedCorrect.h"
27#include "flexkalman/PoseSeparatelyDampedConstantVelocity.h"
28#include "flexkalman/PoseState.h"
29
30
31namespace xrt::auxiliary::tracking {
32
33using namespace xrt::auxiliary::math;
34
35//! Anonymous namespace to hide implementation names
36namespace {
37
38 using State = flexkalman::pose_externalized_rotation::State;
39 using ProcessModel = flexkalman::PoseSeparatelyDampedConstantVelocityProcessModel<State>;
40
41 struct TrackingInfo
42 {
43 bool valid{false};
44 bool tracked{false};
45 };
46 class PSMVFusion : public PSMVFusionInterface
47 {
48 public:
49 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
50
51 void
52 clear_position_tracked_flag() override;
53
54 void
55 process_imu_data(timepoint_ns timestamp_ns,
56 const struct xrt_tracking_sample *sample,
57 const struct xrt_vec3 *orientation_variance_optional) override;
58 void
59 process_3d_vision_data(timepoint_ns timestamp_ns,
60 const struct xrt_vec3 *position,
61 const struct xrt_vec3 *variance_optional,
62 const struct xrt_vec3 *lever_arm_optional,
63 float residual_limit) override;
64
65 void
66 get_prediction(timepoint_ns when_ns, struct xrt_space_relation *out_relation) override;
67
68 private:
69 void
70 reset_filter();
71 void
72 reset_filter_and_imu();
73
74 State filter_state;
75 ProcessModel process_model;
76
78
79 timepoint_ns filter_time_ns{0};
80 bool tracked{false};
81 TrackingInfo orientation_state;
82 TrackingInfo position_state;
83 };
84
85
86
87 void
88 PSMVFusion::clear_position_tracked_flag()
89 {
90 position_state.tracked = false;
91 }
92
93 void
94 PSMVFusion::reset_filter()
95 {
96 filter_state = State{};
97 tracked = false;
98 position_state = TrackingInfo{};
99 }
100 void
101 PSMVFusion::reset_filter_and_imu()
102 {
103 reset_filter();
104 orientation_state = TrackingInfo{};
105 imu = SimpleIMUFusion{};
106 }
107
108 void
109 PSMVFusion::process_imu_data(timepoint_ns timestamp_ns,
110 const struct xrt_tracking_sample *sample,
111 const struct xrt_vec3 *orientation_variance_optional)
112 {
113
114 Eigen::Vector3d variance = Eigen::Vector3d::Constant(0.01);
115 if (orientation_variance_optional) {
116 variance = map_vec3(*orientation_variance_optional).cast<double>();
117 }
118 imu.handleAccel(map_vec3(sample->accel_m_s2).cast<double>(), timestamp_ns);
119 imu.handleGyro(map_vec3(sample->gyro_rad_secs).cast<double>(), timestamp_ns);
120 imu.postCorrect();
121
122 //! @todo use better measurements instead of the preceding "simple
123 //! fusion"
124 if (filter_time_ns != 0 && filter_time_ns != timestamp_ns) {
125 float dt = time_ns_to_s(timestamp_ns - filter_time_ns);
126 assert(dt > 0);
127 flexkalman::predict(filter_state, process_model, dt);
128 }
129 filter_time_ns = timestamp_ns;
130 auto meas = flexkalman::AbsoluteOrientationMeasurement{
131 // Must rotate by 180 to align
132 Eigen::Quaterniond(Eigen::AngleAxisd(EIGEN_PI, Eigen::Vector3d::UnitY())) * imu.getQuat(),
133 variance};
134 if (flexkalman::correctUnscented(filter_state, meas)) {
135 orientation_state.tracked = true;
136 orientation_state.valid = true;
137 } else {
138 U_LOG_E(
139 "Got non-finite something when filtering IMU - "
140 "resetting filter and IMU fusion!");
141 reset_filter_and_imu();
142 }
143 // 7200 deg/sec
144 constexpr double max_rad_per_sec = 20 * double(EIGEN_PI) * 2;
145 if (filter_state.angularVelocity().squaredNorm() > max_rad_per_sec * max_rad_per_sec) {
146 U_LOG_E(
147 "Got excessive angular velocity when filtering "
148 "IMU - resetting filter and IMU fusion!");
149 reset_filter_and_imu();
150 }
151 }
152
153 void
154 PSMVFusion::process_3d_vision_data(timepoint_ns timestamp_ns,
155 const struct xrt_vec3 *position,
156 const struct xrt_vec3 *variance_optional,
157 const struct xrt_vec3 *lever_arm_optional,
158 float residual_limit)
159 {
160 Eigen::Vector3f pos = map_vec3(*position);
161 Eigen::Vector3d variance{1.e-4, 1.e-4, 4.e-4};
162 if (variance_optional) {
163 variance = map_vec3(*variance_optional).cast<double>();
164 }
165 Eigen::Vector3d lever_arm{0, 0.09, 0};
166 if (lever_arm_optional) {
167 lever_arm = map_vec3(*lever_arm_optional).cast<double>();
168 }
169 auto measurement = xrt::auxiliary::tracking::AbsolutePositionLeverArmMeasurement{pos.cast<double>(),
170 lever_arm, variance};
171 double resid = measurement.getResidual(filter_state).norm();
172
173 if (resid > residual_limit) {
174 // Residual arbitrarily "too large"
175 U_LOG_W(
176 "measurement residual is %f, resetting "
177 "filter state",
178 resid);
179 reset_filter();
180 return;
181 }
182 if (flexkalman::correctUnscented(filter_state, measurement)) {
183 tracked = true;
184 position_state.valid = true;
185 position_state.tracked = true;
186 } else {
187 U_LOG_W(
188 "Got non-finite something when filtering "
189 "tracker - resetting filter!");
190 reset_filter();
191 }
192 }
193
194 void
195 PSMVFusion::get_prediction(timepoint_ns when_ns, struct xrt_space_relation *out_relation)
196 {
197 if (out_relation == NULL) {
198 return;
199 }
200 // Clear to identity values
201 U_ZERO(out_relation);
202 out_relation->pose.orientation.w = 1;
203 if (!tracked || filter_time_ns == 0) {
204 return;
205 }
206 float dt = time_ns_to_s(when_ns - filter_time_ns);
207 auto predicted_state = flexkalman::getPrediction(filter_state, process_model, dt);
208
209 map_vec3(out_relation->pose.position) = predicted_state.position().cast<float>();
210 map_quat(out_relation->pose.orientation) = predicted_state.getQuaternion().cast<float>();
211 map_vec3(out_relation->linear_velocity) = predicted_state.velocity().cast<float>();
212 map_vec3(out_relation->angular_velocity) = predicted_state.angularVelocity().cast<float>();
213
214 uint64_t flags = 0;
215 if (position_state.valid) {
216 flags |= XRT_SPACE_RELATION_POSITION_VALID_BIT;
217 flags |= XRT_SPACE_RELATION_LINEAR_VELOCITY_VALID_BIT;
218 if (position_state.tracked) {
219 flags |= XRT_SPACE_RELATION_POSITION_TRACKED_BIT;
220 }
221 }
222 if (orientation_state.valid) {
223 flags |= XRT_SPACE_RELATION_ORIENTATION_VALID_BIT;
224 flags |= XRT_SPACE_RELATION_ANGULAR_VELOCITY_VALID_BIT;
225 if (orientation_state.tracked) {
226 flags |= XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT;
227 }
228 }
229 out_relation->relation_flags = (xrt_space_relation_flags)flags;
230 }
231} // namespace
232
233
234std::unique_ptr<PSMVFusionInterface>
235PSMVFusionInterface::create()
236{
237 auto ret = std::make_unique<PSMVFusion>();
238 return ret;
239}
240} // namespace xrt::auxiliary::tracking
For PS Move-like things, where there's a directly-computed absolute position that is not at the track...
Definition t_fusion.hpp:181
A simple IMU fusion class.
Definition t_imu_fusion.hpp:42
bool handleAccel(Eigen::Vector3d const &accel, timepoint_ns timestamp)
Process an accelerometer report.
Definition t_imu_fusion.hpp:256
void postCorrect()
Normalize internal state.
Definition t_imu_fusion.hpp:158
bool handleGyro(Eigen::Vector3d const &gyro, timepoint_ns timestamp)
Process a gyroscope report.
Definition t_imu_fusion.hpp:221
Eigen::Quaterniond getQuat() const
Get the current state orientation.
Definition t_imu_fusion.hpp:68
#define U_LOG_E(...)
Log a message at U_LOGGING_ERROR level, conditional on the global log level.
Definition u_logging.h:442
#define U_LOG_W(...)
Log a message at U_LOGGING_WARN level, conditional on the global log level.
Definition u_logging.h:439
int64_t timepoint_ns
Integer timestamp type.
Definition u_time.h:77
static double time_ns_to_s(time_duration_ns ns)
Convert nanoseconds duration to double seconds.
Definition u_time.h:97
#define U_ZERO(PTR)
Zeroes the correct amount of memory based on the type pointed-to by the argument.
Definition u_misc.h:68
xrt_space_relation_flags
Flags of which components of a xrt_space_relation is valid.
Definition xrt_defines.h:642
C interface to math library.
Interoperability helpers connecting internal math types and Eigen.
C++-only functionality in the Math helper library.
Definition m_documentation.hpp:15
static Eigen::Map< const Eigen::Vector3f > position(const struct xrt_pose &pose)
Return a Eigen type wrapping a pose's position (const).
Definition m_eigen_interop.hpp:217
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:31
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:57
Definition m_space.cpp:87
A relation with two spaces, includes velocity and acceleration.
Definition xrt_defines.h:670
IMU Sample.
Definition xrt_tracking.h:121
A 3 element vector with single floats.
Definition xrt_defines.h:289
C++ sensor fusion/filtering code that uses flexkalman.
C++ sensor fusion/filtering code that uses flexkalman.
PS Move tracker code.
Very small misc utils.