···7272DEBUG_GET_ONCE_BOOL_OPTION(slam_ui, "SLAM_UI", false)
7373DEBUG_GET_ONCE_BOOL_OPTION(slam_submit_from_start, "SLAM_SUBMIT_FROM_START", false)
7474DEBUG_GET_ONCE_NUM_OPTION(slam_openvr_groundtruth_device, "SLAM_OPENVR_GROUNDTRUTH_DEVICE", 0)
7575-DEBUG_GET_ONCE_NUM_OPTION(slam_prediction_type, "SLAM_PREDICTION_TYPE", long(SLAM_PRED_SP_SO_IA_IL))
7575+DEBUG_GET_ONCE_NUM_OPTION(slam_prediction_type, "SLAM_PREDICTION_TYPE", long(SLAM_PRED_IP_IO_IA_IL))
7676DEBUG_GET_ONCE_BOOL_OPTION(slam_write_csvs, "SLAM_WRITE_CSVS", false)
7777DEBUG_GET_ONCE_OPTION(slam_csv_path, "SLAM_CSV_PATH", "evaluation/")
7878DEBUG_GET_ONCE_BOOL_OPTION(slam_timing_stat, "SLAM_TIMING_STAT", true)
···374374 t_slam_prediction_type pred_type;
375375 u_var_combo pred_combo; //!< UI combo box to select @ref pred_type
376376 RelationHistory slam_rels{}; //!< A history of relations produced purely from external SLAM tracker data
377377+ int dbg_pred_every = 1; //!< Skip X SLAM poses so that you get tracked mostly by the prediction algo
378378+ int dbg_pred_counter = 0; //!< SLAM pose counter for prediction debugging
379379+ struct os_mutex lock_ff; //!< Lock for gyro_ff and accel_ff.
377380 struct m_ff_vec3_f32 *gyro_ff; //!< Last gyroscope samples
378381 struct m_ff_vec3_f32 *accel_ff; //!< Last accelerometer samples
379382 vector<u_sink_debug> ui_sink; //!< Sink to display frames in UI of each camera
···817820 rel.linear_velocity = (npos - lpos) / dt;
818821 math_quat_finite_difference(&lrot, &nrot, dt, &rel.angular_velocity);
819822820820- t.slam_rels.push(rel, nts);
823823+ // Push to relationship history unless we are debugging prediction
824824+ if (t.dbg_pred_counter % t.dbg_pred_every == 0) {
825825+ t.slam_rels.push(rel, nts);
826826+ }
827827+ t.dbg_pred_counter = (t.dbg_pred_counter + 1) % t.dbg_pred_every;
821828822829 gt_ui_push(t, nts, rel.pose);
823830 t.slam_traj_writer->push(nts, rel.pose);
831831+ xrt_pose_sample pose_sample = {nts, rel.pose};
832832+ xrt_sink_push_pose(t.euroc_recorder->gt, &pose_sample);
824833825834 // Push even if timing extension is disabled
826835 auto tss = timing_ui_push(t, np);
···841850 return got_one;
842851}
843852853853+//! Integrates IMU samples on top of a base pose and predicts from that
854854+static void
855855+predict_pose_from_imu(TrackerSlam &t,
856856+ timepoint_ns when_ns,
857857+ xrt_space_relation base_rel, // Pose to integrate IMUs on top of
858858+ timepoint_ns base_rel_ts,
859859+ struct xrt_space_relation *out_relation)
860860+{
861861+ os_mutex_lock(&t.lock_ff);
862862+863863+ // Find oldest imu index i that is newer than latest SLAM pose (or -1)
864864+ int i = 0;
865865+ uint64_t imu_ts = UINT64_MAX;
866866+ xrt_vec3 _;
867867+ while (m_ff_vec3_f32_get(t.gyro_ff, i, &_, &imu_ts)) {
868868+ if ((int64_t)imu_ts < base_rel_ts) {
869869+ i--; // Back to the oldest newer-than-SLAM IMU index (or -1)
870870+ break;
871871+ }
872872+ i++;
873873+ }
874874+875875+ if (i == -1) {
876876+ SLAM_WARN("No IMU samples received after latest SLAM pose (and frame)");
877877+ }
878878+879879+ xrt_space_relation integ_rel = base_rel;
880880+ timepoint_ns integ_rel_ts = base_rel_ts;
881881+ xrt_quat &o = integ_rel.pose.orientation;
882882+ xrt_vec3 &p = integ_rel.pose.position;
883883+ xrt_vec3 &w = integ_rel.angular_velocity;
884884+ xrt_vec3 &v = integ_rel.linear_velocity;
885885+ bool clamped = false; // If when_ns is older than the latest IMU ts
886886+887887+ while (i >= 0) { // Decreasing i increases timestamp
888888+ // Get samples
889889+ xrt_vec3 g{};
890890+ xrt_vec3 a{};
891891+ uint64_t g_ts{};
892892+ uint64_t a_ts{};
893893+ bool got = true;
894894+ got &= m_ff_vec3_f32_get(t.gyro_ff, i, &g, &g_ts);
895895+ got &= m_ff_vec3_f32_get(t.accel_ff, i, &a, &a_ts);
896896+ timepoint_ns ts = g_ts;
897897+898898+899899+ // Checks
900900+ if (ts > when_ns) {
901901+ clamped = true;
902902+ //! @todo Instead of using same a and g values, do an interpolated sample like this:
903903+ // a = prev_a + ((when_ns - prev_ts) / (ts - prev_ts)) * (a - prev_a);
904904+ // g = prev_g + ((when_ns - prev_ts) / (ts - prev_ts)) * (g - prev_g);
905905+ ts = when_ns; // clamp ts to when_ns
906906+ }
907907+ SLAM_DASSERT(got && g_ts == a_ts, "Failure getting synced gyro and accel samples");
908908+ SLAM_DASSERT(ts >= base_rel_ts, "Accessing imu sample that is older than latest SLAM pose");
909909+910910+ // Update time
911911+ float dt = (float)time_ns_to_s(ts - integ_rel_ts);
912912+ integ_rel_ts = ts;
913913+914914+ // Integrate gyroscope
915915+ xrt_quat angvel_delta{};
916916+ xrt_vec3 scaled_half_g = g * dt * 0.5f;
917917+ math_quat_exp(&scaled_half_g, &angvel_delta); // Same as using math_quat_from_angle_vector(g/dt)
918918+ math_quat_rotate(&o, &angvel_delta, &o); // Orientation
919919+ math_quat_rotate_derivative(&o, &g, &w); // Angular velocity
920920+921921+ // Integrate accelerometer
922922+ xrt_vec3 world_accel{};
923923+ math_quat_rotate_vec3(&o, &a, &world_accel);
924924+ world_accel += t.gravity_correction;
925925+ v += world_accel * dt; // Linear velocity
926926+ p += v * dt + world_accel * (dt * dt * 0.5f); // Position
927927+928928+ if (clamped) {
929929+ break;
930930+ }
931931+ i--;
932932+ }
933933+934934+ os_mutex_unlock(&t.lock_ff);
935935+936936+ // Do the prediction based on the updated relation
937937+ double last_imu_to_now_dt = time_ns_to_s(when_ns - integ_rel_ts);
938938+ xrt_space_relation predicted_relation{};
939939+ m_predict_relation(&integ_rel, last_imu_to_now_dt, &predicted_relation);
940940+941941+ *out_relation = predicted_relation;
942942+}
943943+844944//! Return our best guess of the relation at time @p when_ns using all the data the tracker has.
845945static void
846946predict_pose(TrackerSlam &t, timepoint_ns when_ns, struct xrt_space_relation *out_relation)
847947{
848948 XRT_TRACE_MARKER();
849949850850- bool valid_pred_type = t.pred_type >= SLAM_PRED_NONE && t.pred_type <= SLAM_PRED_SP_SO_IA_IL;
950950+ bool valid_pred_type = t.pred_type >= SLAM_PRED_NONE && t.pred_type < SLAM_PRED_COUNT;
851951 SLAM_DASSERT(valid_pred_type, "Invalid prediction type (%d)", t.pred_type);
852952853953 // Get last relation computed purely from SLAM data
···874974 return;
875975 }
876976977977+978978+ if (t.pred_type == SLAM_PRED_IP_IO_IA_IL) {
979979+ predict_pose_from_imu(t, when_ns, rel, (int64_t)rel_ts, out_relation);
980980+ return;
981981+ }
982982+983983+ os_mutex_lock(&t.lock_ff);
984984+877985 // Update angular velocity with gyro data
878986 if (t.pred_type >= SLAM_PRED_SP_SO_IA_SL) {
879987 xrt_vec3 avg_gyro{};
···891999 double slam_to_imu_dt = time_ns_to_s(t.last_imu_ts - rel_ts);
8921000 rel.linear_velocity += world_accel * slam_to_imu_dt;
8931001 }
10021002+10031003+ os_mutex_unlock(&t.lock_ff);
89410048951005 // Do the prediction based on the updated relation
8961006 double slam_to_now_dt = time_ns_to_s(when_ns - rel_ts);
···9591069setup_ui(TrackerSlam &t)
9601070{
9611071 t.pred_combo.count = SLAM_PRED_COUNT;
962962- t.pred_combo.options = "None\0Interpolate SLAM poses\0Also gyro\0Also accel (needs gravity correction)\0\0";
10721072+ t.pred_combo.options = "None\0Interpolate SLAM poses\0Also gyro\0Also accel\0Latest IMU\0";
9631073 t.pred_combo.value = (int *)&t.pred_type;
9641074 t.ui_sink = vector<u_sink_debug>(t.cam_count);
9651075 for (size_t i = 0; i < t.ui_sink.size(); i++) {
9661076 u_sink_debug_init(&t.ui_sink[i]);
9671077 }
10781078+ os_mutex_init(&t.lock_ff);
9681079 m_ff_vec3_f32_alloc(&t.gyro_ff, 1000);
9691080 m_ff_vec3_f32_alloc(&t.accel_ff, 1000);
9701081 m_ff_vec3_f32_alloc(&t.filter.pos_ff, 1000);
···99111029921103 u_var_add_gui_header(&t, NULL, "Prediction");
9931104 u_var_add_combo(&t, &t.pred_combo, "Prediction Type");
11051105+ u_var_add_i32(&t, &t.dbg_pred_every, "Debug prediction skips (try 30)");
9941106 u_var_add_ro_ff_vec3_f32(&t, t.gyro_ff, "Gyroscope");
9951107 u_var_add_ro_ff_vec3_f32(&t, t.accel_ff, "Accelerometer");
9961108 u_var_add_f32(&t, &t.gravity_correction.z, "Gravity Correction");
···1208132012091321 struct xrt_vec3 gyro = {(float)w.x, (float)w.y, (float)w.z};
12101322 struct xrt_vec3 accel = {(float)a.x, (float)a.y, (float)a.z};
13231323+ os_mutex_lock(&t.lock_ff);
12111324 m_ff_vec3_f32_push(t.gyro_ff, &gyro, ts);
12121325 m_ff_vec3_f32_push(t.accel_ff, &accel, ts);
13261326+ os_mutex_unlock(&t.lock_ff);
12131327}
1214132812151329//! Push the frame to the external SLAM system
···12981412 }
12991413 m_ff_vec3_f32_free(&t.gyro_ff);
13001414 m_ff_vec3_f32_free(&t.accel_ff);
14151415+ os_mutex_destroy(&t.lock_ff);
13011416 m_ff_vec3_f32_free(&t.filter.pos_ff);
13021417 m_ff_vec3_f32_free(&t.filter.rot_ff);
13031418 delete t_ptr->slam;
+2
src/xrt/auxiliary/tracking/t_tracking.h
···599599 SLAM_PRED_SP_SO_SA_SL, //!< Predicts from last two SLAM poses only
600600 SLAM_PRED_SP_SO_IA_SL, //!< Predicts from last SLAM pose with angular velocity computed from IMU
601601 SLAM_PRED_SP_SO_IA_IL, //!< Predicts from last SLAM pose with angular and linear velocity computed from IMU
602602+ SLAM_PRED_IP_IO_IA_IL, //!< Predicts from a pose that is the last SLAM pose with the IMU samples that came after
603603+ //!< it integrated on top; velocities from latest IMU sample.
602604 SLAM_PRED_COUNT,
603605};
604606