···2828#include "tracking/t_euroc_recorder.h"
2929#include "tracking/t_openvr_tracker.h"
3030#include "tracking/t_tracking.h"
3131+#include "tracking/t_vit_loader.h"
31323232-#include <slam_tracker.hpp>
3333+#include "vit/vit_interface.h"
3434+3335#include <opencv2/core/mat.hpp>
3436#include <opencv2/core/version.hpp>
3537···69717072//! @see t_slam_tracker_config
7173DEBUG_GET_ONCE_LOG_OPTION(slam_log, "SLAM_LOG", U_LOGGING_INFO)
7474+DEBUG_GET_ONCE_OPTION(vit_system_library_path, "VIT_SYSTEM_LIBRARY_PATH", NULL)
7275DEBUG_GET_ONCE_OPTION(slam_config, "SLAM_CONFIG", nullptr)
7376DEBUG_GET_ONCE_BOOL_OPTION(slam_ui, "SLAM_UI", false)
7477DEBUG_GET_ONCE_BOOL_OPTION(slam_submit_from_start, "SLAM_SUBMIT_FROM_START", false)
···344347struct TrackerSlam
345348{
346349 struct xrt_tracked_slam base = {};
347347- struct xrt_frame_node node = {}; //!< Will be called on destruction
348348- slam_tracker *slam; //!< Pointer to the external SLAM system implementation
350350+ struct xrt_frame_node node = {}; //!< Will be called on destruction
351351+ struct t_vit_bundle vit; //!< VIT system function pointers
352352+ enum vit_tracker_pose_capability caps; //!< VIT tracker bitfield capabilities
353353+ struct vit_tracker *tracker; //!< Pointer to the tracker created by the loaded VIT system;
349354350355 struct xrt_slam_sinks sinks = {}; //!< Pointers to the sinks below
351356 struct xrt_frame_sink cam_sinks[XRT_TRACKING_MAX_SLAM_CAMS]; //!< Sends camera frames to the SLAM system
352357 struct xrt_imu_sink imu_sink = {}; //!< Sends imu samples to the SLAM system
353358 struct xrt_pose_sink gt_sink = {}; //!< Register groundtruth trajectory for stats
354354- bool submit; //!< Whether to submit data pushed to sinks to the SLAM tracker
355355- int cam_count; //!< Number of cameras used for tracking
359359+ bool submit; //!< Whether to submit data pushed to sinks to the SLAM tracker
360360+ uint32_t cam_count; //!< Number of cameras used for tracking
356361357362 struct u_var_button reset_state_btn; //!< Reset tracker state button
358363359364 enum u_logging_level log_level; //!< Logging level for the SLAM tracker, set by SLAM_LOG var
360360- struct os_thread_helper oth; //!< Thread where the external SLAM system runs
361365 MatFrame *cv_wrapper; //!< Wraps a xrt_frame in a cv::Mat to send to the SLAM system
362366363367 struct xrt_slam_sinks *euroc_recorder; //!< EuRoC dataset recording sinks
···426430 //! Tracker timing info for performance evaluation
427431 struct
428432 {
429429- bool ext_available = false; //!< Whether the SLAM system supports the timing extension
430430- bool ext_enabled = false; //!< Whether the timing extension is enabled
433433+ bool enabled = false; //!< Whether the timing extension is enabled
431434 float dur_ms[UI_TIMING_POSE_COUNT]; //!< Timing durations in ms
432435 int idx = 0; //!< Index of latest entry in @ref dur_ms
433436 u_var_combo start_ts; //!< UI combo box to select initial timing measurement
···465468 vector<FeatureCounter> fcs; //!< Store feature count info for each camera
466469 u_var_curves fcs_ui; //!< Display of `fcs` in UI
467470468468- bool ext_available = false; //!< Whether the SLAM system supports the features extension
469469- bool ext_enabled = false; //!< Whether the features extension is enabled
471471+ bool enabled = false; //!< Whether the features extension is enabled
470472 struct u_var_button enable_btn; //!< Toggle extension
471473 } features;
472474···492494static void
493495timing_ui_setup(TrackerSlam &t)
494496{
497497+ t.timing.enabled = false;
498498+495499 u_var_add_ro_ftext(&t, "\n%s", "Tracker timing");
496500497501 // Setup toggle button
···499503 u_var_button_cb cb = [](void *t_ptr) {
500504 TrackerSlam *t = (TrackerSlam *)t_ptr;
501505 u_var_button &btn = t->timing.enable_btn;
502502- bool &e = t->timing.ext_enabled;
503503- e = !e;
506506+ bool e = !t->timing.enabled;
504507 snprintf(btn.label, sizeof(btn.label), "%s", msg[e]);
505505- const auto params = make_shared<FPARAMS_EPET>(e);
506506- shared_ptr<void> _;
507507- t->slam->use_feature(F_ENABLE_POSE_EXT_TIMING, params, _);
508508+ vit_result_t vres =
509509+ t->vit.tracker_set_pose_capabilities(t->tracker, VIT_TRACKER_POSE_CAPABILITY_TIMING, e);
510510+ if (vres != VIT_SUCCESS) {
511511+ U_LOG_IFL_E(t->log_level, "Failed to set tracker timing capability");
512512+ return;
513513+ }
514514+ t->timing.enabled = e;
508515 };
509516 t.timing.enable_btn.cb = cb;
510510- t.timing.enable_btn.disabled = !t.timing.ext_available;
517517+ t.timing.enable_btn.disabled = (t.caps & VIT_TRACKER_POSE_CAPABILITY_TIMING) == 0;
511518 t.timing.enable_btn.ptr = &t;
512512- u_var_add_button(&t, &t.timing.enable_btn, msg[t.timing.ext_enabled]);
519519+ u_var_add_button(&t, &t.timing.enable_btn, msg[t.timing.enabled]);
520520+521521+ // We provide two timing columns by default, even if there is no extension support
522522+ t.timing.columns = {"sampled", "received_by_monado"};
513523514514- // Setup graph
524524+ // Only fill the timing columns if the tracker supports pose timing
525525+ if ((t.caps & VIT_TRACKER_POSE_CAPABILITY_TIMING) != 0) {
526526+ vit_tracker_timing_titles titles = {};
527527+ vit_result_t vres = t.vit.tracker_get_timing_titles(t.tracker, &titles);
528528+ if (vres != VIT_SUCCESS) {
529529+ SLAM_ERROR("Failed to get timing titles from tracker");
530530+ return;
531531+ }
532532+533533+ // Copies the titles locally.
534534+ std::vector<std::string> cols(titles.titles, titles.titles + titles.count);
535535+536536+ t.timing.columns.insert(t.timing.columns.begin() + 1, cols.begin(), cols.end());
537537+ }
515538516539 // Construct null-separated array of options for the combo box
517540 using namespace std::string_literals;
···546569547570//! Updates timing UI with info from a computed pose and returns that info
548571static vector<timepoint_ns>
549549-timing_ui_push(TrackerSlam &t, const pose &p)
572572+timing_ui_push(TrackerSlam &t, const vit_pose_t *pose, int64_t ts)
550573{
551574 timepoint_ns now = os_monotonic_get_ns();
552552- vector<timepoint_ns> tss = {p.timestamp, now};
575575+ vector<timepoint_ns> tss = {ts, now};
553576554577 // Add extra timestamps if the SLAM tracker provides them
555555- shared_ptr<pose_extension> ext = p.find_pose_extension(pose_ext_type::TIMING);
556556- if (ext) {
557557- pose_ext_timing pet = *std::static_pointer_cast<pose_ext_timing>(ext);
558558- tss.insert(tss.begin() + 1, pet.timing.begin(), pet.timing.end());
559559- }
578578+ if (t.timing.enabled) {
579579+ vit_pose_timing timing;
580580+ vit_result_t vres = t.vit.pose_get_timing(pose, &timing);
581581+ if (vres != VIT_SUCCESS) {
582582+ // Even if the timing is enabled, some of the poses already in the queue won't have it enabled.
583583+ if (vres != VIT_ERROR_NOT_ENABLED) {
584584+ SLAM_ERROR("Failed to get pose timing");
585585+ }
560586561561- // The two timestamps to compare in the graph
562562- timepoint_ns start = tss.at(t.timing.start_ts_idx);
563563- timepoint_ns end = tss.at(t.timing.end_ts_idx);
587587+ return {};
588588+ }
564589565565- // Push to the UI graph
566566- float tss_ms = (end - start) / U_TIME_1MS_IN_NS;
567567- t.timing.idx = (t.timing.idx + 1) % UI_TIMING_POSE_COUNT;
568568- t.timing.dur_ms[t.timing.idx] = tss_ms;
569569- constexpr float a = 1.0f / UI_TIMING_POSE_COUNT; // Exponential moving average
570570- t.timing.ui.reference_timing = (1 - a) * t.timing.ui.reference_timing + a * tss_ms;
590590+ std::vector<int64_t> data(timing.timestamps, timing.timestamps + timing.count);
591591+ tss.insert(tss.begin() + 1, data.begin(), data.end());
592592+593593+ // The two timestamps to compare in the graph
594594+ timepoint_ns start = tss.at(t.timing.start_ts_idx);
595595+ timepoint_ns end = tss.at(t.timing.end_ts_idx);
596596+597597+ // Push to the UI graph
598598+ float tss_ms = (end - start) / U_TIME_1MS_IN_NS;
599599+ t.timing.idx = (t.timing.idx + 1) % UI_TIMING_POSE_COUNT;
600600+ t.timing.dur_ms[t.timing.idx] = tss_ms;
601601+ constexpr float a = 1.0f / UI_TIMING_POSE_COUNT; // Exponential moving average
602602+ t.timing.ui.reference_timing = (1 - a) * t.timing.ui.reference_timing + a * tss_ms;
603603+ }
571604572605 return tss;
573606}
···582615static void
583616features_ui_setup(TrackerSlam &t)
584617{
585585- // We can't do anything useful if the system doesn't implement the feature
586586- if (!t.features.ext_available) {
587587- return;
588588- }
618618+ t.features.enabled = false;
589619590620 u_var_add_ro_ftext(&t, "\n%s", "Tracker features");
591621···594624 u_var_button_cb cb = [](void *t_ptr) {
595625 TrackerSlam *t = (TrackerSlam *)t_ptr;
596626 u_var_button &btn = t->features.enable_btn;
597597- bool &e = t->features.ext_enabled;
598598- e = !e;
627627+ bool e = !t->features.enabled;
599628 snprintf(btn.label, sizeof(btn.label), "%s", msg[e]);
600600- const auto params = make_shared<FPARAMS_EPEF>(e);
601601- shared_ptr<void> _;
602602- t->slam->use_feature(F_ENABLE_POSE_EXT_FEATURES, params, _);
629629+ vit_result_t vres =
630630+ t->vit.tracker_set_pose_capabilities(t->tracker, VIT_TRACKER_POSE_CAPABILITY_FEATURES, e);
631631+ if (vres != VIT_SUCCESS) {
632632+ U_LOG_IFL_E(t->log_level, "Failed to set tracker features capability");
633633+ return;
634634+ }
635635+ t->features.enabled = e;
603636 };
604637 t.features.enable_btn.cb = cb;
605605- t.features.enable_btn.disabled = !t.features.ext_available;
638638+ t.features.enable_btn.disabled = (t.caps & VIT_TRACKER_POSE_CAPABILITY_FEATURES) == 0;
606639 t.features.enable_btn.ptr = &t;
607607- u_var_add_button(&t, &t.features.enable_btn, msg[t.features.ext_enabled]);
640640+ u_var_add_button(&t, &t.features.enable_btn, msg[t.features.enabled]);
608641609642 // Setup graph
610610-611643 u_var_curve_getter getter = [](void *fs_ptr, int i) -> u_var_curve_point {
612644 auto *fs = (TrackerSlam::Features::FeatureCounter *)fs_ptr;
613645 timepoint_ns now = os_monotonic_get_ns();
···631663 t.features.fcs_ui.ylabel = "Number of features";
632664633665 t.features.fcs.resize(t.cam_count);
634634- for (int i = 0; i < t.cam_count; i++) {
666666+ for (uint32_t i = 0; i < t.cam_count; ++i) {
635667 auto &fc = t.features.fcs[i];
636668 fc.cam_name = "Cam" + to_string(i);
637669···646678}
647679648680static vector<int>
649649-features_ui_push(TrackerSlam &t, const pose &ppp)
681681+features_ui_push(TrackerSlam &t, const vit_pose_t *pose, int64_t ts)
650682{
651651- if (!t.features.ext_available) {
652652- return {};
653653- }
654654-655655- shared_ptr<pose_extension> ext = ppp.find_pose_extension(pose_ext_type::FEATURES);
656656- if (!ext) {
683683+ if (!t.features.enabled) {
657684 return {};
658685 }
659686660660- pose_ext_features pef = *std::static_pointer_cast<pose_ext_features>(ext);
661661-662687 // Push to the UI graph
663688 vector<int> fcs{};
664664- for (size_t i = 0; i < pef.features_per_cam.size(); i++) {
665665- int count = pef.features_per_cam.at(i).size();
666666- t.features.fcs.at(i).addFeatureCount(ppp.timestamp, count);
667667- fcs.push_back(count);
689689+ for (uint32_t i = 0; i < t.cam_count; ++i) {
690690+ vit_pose_features features = {};
691691+ vit_result_t vres = t.vit.pose_get_features(pose, i, &features);
692692+ if (vres != VIT_SUCCESS) {
693693+ // Even if the features are enabled, some of the poses already in the queue won't have it
694694+ // enabled.
695695+ if (vres != VIT_ERROR_NOT_ENABLED) {
696696+ SLAM_ERROR("Failed to get pose features for camera %u", i);
697697+ }
698698+699699+ return {};
700700+ }
701701+702702+ t.features.fcs.at(i).addFeatureCount(ts, features.count);
703703+ fcs.push_back(features.count);
668704 }
669705670706 return fcs;
···789825static bool
790826flush_poses(TrackerSlam &t)
791827{
792792- pose tracked_pose{};
793793- bool got_one = t.slam->try_dequeue_pose(tracked_pose);
794828795795- bool dequeued = got_one;
796796- while (dequeued) {
829829+ vit_pose_t *pose;
830830+ vit_result_t vres = t.vit.tracker_pop_pose(t.tracker, &pose);
831831+ if (vres != VIT_SUCCESS) {
832832+ SLAM_ERROR("Failed to get pose from VIT tracker");
833833+ }
834834+835835+ if (pose == NULL) {
836836+ SLAM_TRACE("No poses to flush");
837837+ return false;
838838+ }
839839+840840+ do {
797841 // New pose
798798- pose np = tracked_pose;
799799- int64_t nts = np.timestamp;
800800- xrt_vec3 npos{np.px, np.py, np.pz};
801801- xrt_quat nrot{np.rx, np.ry, np.rz, np.rw};
842842+ vit_pose_data_t data;
843843+ vres = t.vit.pose_get_data(pose, &data);
844844+ if (vres != VIT_SUCCESS) {
845845+ SLAM_ERROR("Failed to get pose data from VIT tracker");
846846+ return false;
847847+ }
848848+849849+ int64_t nts = data.timestamp;
850850+851851+ xrt_vec3 npos{data.px, data.py, data.pz};
852852+ xrt_quat nrot{data.ox, data.oy, data.oz, data.ow};
802853803854 // Last relation
804855 xrt_space_relation lr = XRT_SPACE_RELATION_ZERO;
···810861 double dt = time_ns_to_s(nts - lts);
811862812863 SLAM_TRACE("Dequeued SLAM pose ts=%ld p=[%f,%f,%f] r=[%f,%f,%f,%f]", //
813813- nts, np.px, np.py, np.pz, np.rx, np.ry, np.rz, np.rw);
864864+ nts, data.px, data.py, data.pz, data.ox, data.oy, data.oz, data.ow);
814865866866+ // TODO linear velocity from the VIT system
815867 // Compute new relation based on new pose and velocities since last pose
816868 xrt_space_relation rel{};
817869 rel.relation_flags = XRT_SPACE_RELATION_BITMASK_ALL;
···830882 xrt_pose_sample pose_sample = {nts, rel.pose};
831883 xrt_sink_push_pose(t.euroc_recorder->gt, &pose_sample);
832884833833- // Push even if timing extension is disabled
834834- auto tss = timing_ui_push(t, np);
885885+ auto tss = timing_ui_push(t, pose, nts);
835886 t.slam_times_writer->push(tss);
836887837837- if (t.features.ext_enabled) {
838838- vector feat_count = features_ui_push(t, np);
888888+ if (t.features.enabled) {
889889+ vector feat_count = features_ui_push(t, pose, nts);
839890 t.slam_features_writer->push({nts, feat_count});
840891 }
841892842842- dequeued = t.slam->try_dequeue_pose(tracked_pose);
843843- }
844844-845845- if (!got_one) {
846846- SLAM_TRACE("No poses to flush");
847847- }
893893+ t.vit.pose_destroy(pose);
894894+ } while (t.vit.tracker_pop_pose(t.tracker, &pose) == VIT_SUCCESS && pose);
848895849849- return got_one;
896896+ return true;
850897}
851898852899//! Integrates IMU samples on top of a base pose and predicts from that
···10831130 u_var_add_root(&t, "SLAM Tracker", true);
10841131 u_var_add_log_level(&t, &t.log_level, "Log Level");
10851132 u_var_add_bool(&t, &t.submit, "Submit data to SLAM");
11331133+10861134 u_var_button_cb reset_state_cb = [](void *t_ptr) {
10871087- TrackerSlam *t = (TrackerSlam *)t_ptr;
10881088- shared_ptr<void> _;
10891089- t->slam->use_feature(F_RESET_TRACKER_STATE, _, _);
11351135+ TrackerSlam &t = *(TrackerSlam *)t_ptr;
11361136+11371137+ vit_result_t vres = t.vit.tracker_reset(t.tracker);
11381138+ if (vres != VIT_SUCCESS) {
11391139+ SLAM_WARN("Failed to reset VIT tracker");
11401140+ }
10901141 };
10911142 t.reset_state_btn.cb = reset_state_cb;
10921143 t.reset_state_btn.ptr = &t;
···11331184}
1134118511351186static void
11361136-add_camera_calibration(const TrackerSlam &t, const t_slam_camera_calibration *calib, int cam_index)
11871187+add_camera_calibration(const TrackerSlam &t, const t_slam_camera_calibration *calib, uint32_t cam_index)
11371188{
11381189 const t_camera_calibration &view = calib->base;
11391139- const auto params = make_shared<FPARAMS_ACC>();
1140119011411141- params->cam_index = cam_index;
11421142- params->width = view.image_size_pixels.w;
11431143- params->height = view.image_size_pixels.h;
11441144- params->frequency = calib->frequency;
11911191+ vit_camera_calibration params = {};
11921192+ params.camera_index = cam_index;
11931193+ params.width = view.image_size_pixels.w;
11941194+ params.height = view.image_size_pixels.h;
11951195+ params.frequency = calib->frequency;
1145119611461146- params->fx = view.intrinsics[0][0];
11471147- params->fy = view.intrinsics[1][1];
11481148- params->cx = view.intrinsics[0][2];
11491149- params->cy = view.intrinsics[1][2];
11971197+ params.fx = view.intrinsics[0][0];
11981198+ params.fy = view.intrinsics[1][1];
11991199+ params.cx = view.intrinsics[0][2];
12001200+ params.cy = view.intrinsics[1][2];
1150120111511202 switch (view.distortion_model) {
11521152- case T_DISTORTION_OPENCV_RADTAN_8:
11531153- params->distortion_model = "rt8";
11541154- params->distortion.push_back(view.rt8.k1);
11551155- params->distortion.push_back(view.rt8.k2);
11561156- params->distortion.push_back(view.rt8.p1);
11571157- params->distortion.push_back(view.rt8.p2);
11581158- params->distortion.push_back(view.rt8.k3);
11591159- params->distortion.push_back(view.rt8.k4);
11601160- params->distortion.push_back(view.rt8.k5);
11611161- params->distortion.push_back(view.rt8.k6);
12031203+ case T_DISTORTION_OPENCV_RADTAN_8: {
12041204+ params.model = VIT_CAMERA_DISTORTION_RT8;
12051205+ const size_t size = sizeof(struct t_camera_calibration_rt8_params) + sizeof(double);
12061206+ params.distortion_count = size / sizeof(double);
12071207+ SLAM_ASSERT_(params.distortion_count == 9);
12081208+12091209+ memcpy(params.distortion, &view.rt8, size);
12101210+11621211 // -1 metric radius tells Basalt to estimate the metric radius on its own.
11631163- params->distortion.push_back(-1.0);
11641164- SLAM_ASSERT_(params->distortion.size() == 9);
12121212+ params.distortion[8] = -1.f;
11651213 break;
11661166- case T_DISTORTION_WMR:
11671167- params->distortion_model = "rt8";
11681168- params->distortion.push_back(view.wmr.k1);
11691169- params->distortion.push_back(view.wmr.k2);
11701170- params->distortion.push_back(view.wmr.p1);
11711171- params->distortion.push_back(view.wmr.p2);
11721172- params->distortion.push_back(view.wmr.k3);
11731173- params->distortion.push_back(view.wmr.k4);
11741174- params->distortion.push_back(view.wmr.k5);
11751175- params->distortion.push_back(view.wmr.k6);
11761176- params->distortion.push_back(view.wmr.rpmax);
11771177- SLAM_ASSERT_(params->distortion.size() == 9);
12141214+ }
12151215+ case T_DISTORTION_WMR: {
12161216+ params.model = VIT_CAMERA_DISTORTION_RT8;
12171217+ const size_t size = sizeof(struct t_camera_calibration_rt8_params) + sizeof(double);
12181218+ params.distortion_count = size / sizeof(double);
12191219+ SLAM_ASSERT_(params.distortion_count == 9);
12201220+12211221+ memcpy(params.distortion, &view.wmr, size);
12221222+12231223+ params.distortion[8] = view.wmr.rpmax;
12241224+11781225 break;
11791179- case T_DISTORTION_FISHEYE_KB4:
11801180- params->distortion_model = "kb4";
11811181- params->distortion.push_back(view.kb4.k1);
11821182- params->distortion.push_back(view.kb4.k2);
11831183- params->distortion.push_back(view.kb4.k3);
11841184- params->distortion.push_back(view.kb4.k4);
11851185- SLAM_ASSERT_(params->distortion.size() == 4);
12261226+ }
12271227+ case T_DISTORTION_FISHEYE_KB4: {
12281228+ params.model = VIT_CAMERA_DISTORTION_KB4;
12291229+ const size_t size = sizeof(struct t_camera_calibration_kb4_params);
12301230+ params.distortion_count = size / sizeof(double);
12311231+ SLAM_ASSERT_(params.distortion_count == 4);
12321232+12331233+ memcpy(params.distortion, &view.kb4, size);
11861234 break;
12351235+ }
11871236 default:
11881237 SLAM_ASSERT(false, "SLAM doesn't support distortion type %s",
11891238 t_stringify_camera_distortion_model(view.distortion_model));
12391239+ break;
11901240 }
1191124111921242 xrt_matrix_4x4 T; // Row major T_imu_cam
11931243 math_matrix_4x4_transpose(&calib->T_imu_cam, &T);
11941194- params->t_imu_cam = cv::Matx<float, 4, 4>{T.v};
1195124411961196- shared_ptr<FRESULT_ACC> result{};
11971197- t.slam->use_feature(F_ADD_CAMERA_CALIBRATION, params, result);
12451245+ // Converts the xrt_matrix_4x4 from float to double
12461246+ for (size_t i = 0; i < ARRAY_SIZE(params.transform); ++i)
12471247+ params.transform[i] = T.v[i];
12481248+12491249+ vit_result_t vres = t.vit.tracker_add_camera_calibration(t.tracker, ¶ms);
12501250+ if (vres != VIT_SUCCESS) {
12511251+ SLAM_ERROR("Failed to add camera calibration for camera %u", cam_index);
12521252+ }
11981253}
1199125412001255static void
12011256add_imu_calibration(const TrackerSlam &t, const t_slam_imu_calibration *imu_calib)
12021257{
12031203- const auto params = make_shared<FPARAMS_AIC>();
12041204- params->imu_index = 0; // Multiple IMU setups unsupported
12051205- params->frequency = imu_calib->frequency;
12581258+ vit_imu_calibration_t params = {};
12591259+ params.imu_index = 0;
12601260+ params.frequency = imu_calib->frequency;
12611261+12621262+ // TODO improve memcpy size calculation
1206126312071264 const t_inertial_calibration &accel = imu_calib->base.accel;
12081208- params->accel.transform = cv::Matx<double, 3, 3>{&accel.transform[0][0]};
12091209- params->accel.offset = cv::Matx<double, 3, 1>{&accel.offset[0]};
12101210- params->accel.bias_std = cv::Matx<double, 3, 1>{&accel.bias_std[0]};
12111211- params->accel.noise_std = cv::Matx<double, 3, 1>{&accel.noise_std[0]};
12651265+ memcpy(params.accel.transform, accel.transform, sizeof(double) * 9);
12661266+ memcpy(params.accel.offset, accel.offset, sizeof(double) * 3);
12671267+ memcpy(params.accel.bias_std, accel.bias_std, sizeof(double) * 3);
12681268+ memcpy(params.accel.noise_std, accel.noise_std, sizeof(double) * 3);
1212126912131270 const t_inertial_calibration &gyro = imu_calib->base.gyro;
12141214- params->gyro.transform = cv::Matx<double, 3, 3>{&gyro.transform[0][0]};
12151215- params->gyro.offset = cv::Matx<double, 3, 1>{&gyro.offset[0]};
12161216- params->gyro.bias_std = cv::Matx<double, 3, 1>{&gyro.bias_std[0]};
12171217- params->gyro.noise_std = cv::Matx<double, 3, 1>{&gyro.noise_std[0]};
12711271+ memcpy(params.gyro.transform, gyro.transform, sizeof(double) * 9);
12721272+ memcpy(params.gyro.offset, gyro.offset, sizeof(double) * 3);
12731273+ memcpy(params.gyro.bias_std, gyro.bias_std, sizeof(double) * 3);
12741274+ memcpy(params.gyro.noise_std, gyro.noise_std, sizeof(double) * 3);
1218127512191219- shared_ptr<FRESULT_AIC> result{};
12201220- t.slam->use_feature(F_ADD_IMU_CALIBRATION, params, result);
12761276+ vit_result_t vres = t.vit.tracker_add_imu_calibration(t.tracker, ¶ms);
12771277+ if (vres != VIT_SUCCESS) {
12781278+ SLAM_ERROR("Failed to add imu calibration");
12791279+ }
12211280}
1222128112231282static void
12241283send_calibration(const TrackerSlam &t, const t_slam_calibration &c)
12251284{
12851285+ vit_tracker_capability_t caps;
12861286+ vit_result_t vres = t.vit.tracker_get_capabilities(t.tracker, &caps);
12871287+ if (vres != VIT_SUCCESS) {
12881288+ SLAM_ERROR("Failed to get VIT tracker capabilities");
12891289+ return;
12901290+ }
12911291+12261292 // Try to send camera calibration data to the SLAM system
12271227- for (int i = 0; i < c.cam_count; i++) {
12281228- if (t.slam->supports_feature(F_ADD_CAMERA_CALIBRATION)) {
12931293+ if ((caps & VIT_TRACKER_CAPABILITY_CAMERA_CALIBRATION) != 0) {
12941294+ for (int i = 0; i < c.cam_count; i++) {
12291295 SLAM_INFO("Sending Camera %d calibration from Monado", i);
12301296 add_camera_calibration(t, &c.cams[i], i);
12311231- } else {
12321232- SLAM_INFO("Camera %d will use the calibration provided by the SLAM_CONFIG file", i);
12331297 }
12981298+ } else {
12991299+ SLAM_WARN("Tracker doesn't support camera calibration");
12341300 }
1235130112361302 // Try to send IMU calibration data to the SLAM system
12371237- if (t.slam->supports_feature(F_ADD_IMU_CALIBRATION)) {
13031303+ if ((caps & VIT_TRACKER_CAPABILITY_IMU_CALIBRATION) != 0) {
12381304 SLAM_INFO("Sending IMU calibration from Monado");
12391305 add_imu_calibration(t, &c.imu);
12401306 } else {
12411241- SLAM_INFO("The IMU will use the calibration provided by the SLAM_CONFIG file");
13071307+ SLAM_WARN("Tracker doesn't support IMU calibration");
12421308 }
12431309}
12441310···1323138913241390 //! @todo There are many conversions like these between xrt and
13251391 //! slam_tracker.hpp types. Implement a casting mechanism to avoid copies.
13261326- imu_sample sample{ts, a.x, a.y, a.z, w.x, w.y, w.z};
13921392+ vit_imu_sample_t sample = {};
13931393+ sample.timestamp = ts;
13941394+ sample.ax = a.x;
13951395+ sample.ay = a.y;
13961396+ sample.az = a.z;
13971397+ sample.wx = w.x;
13981398+ sample.wy = w.y;
13991399+ sample.wz = w.z;
14001400+13271401 if (t.submit) {
13281328- t.slam->push_imu_sample(sample);
14021402+ t.vit.tracker_push_imu_sample(t.tracker, &sample);
13291403 }
1330140413311405 xrt_sink_push_imu(t.euroc_recorder->imu, s);
···1340141413411415//! Push the frame to the external SLAM system
13421416static void
13431343-receive_frame(TrackerSlam &t, struct xrt_frame *frame, int cam_index)
14171417+receive_frame(TrackerSlam &t, struct xrt_frame *frame, uint32_t cam_index)
13441418{
13451419 XRT_TRACE_MARKER();
1346142014211421+ SLAM_DASSERT_(frame->timestamp < INT64_MAX);
14221422+14231423+ // Return early if we don't submit
14241424+ if (!t.submit) {
14251425+ return;
14261426+ }
14271427+13471428 if (cam_index == t.cam_count - 1) {
13481429 flush_poses(t); // Useful to flush SLAM poses when no openxr app is open
13491430 }
14311431+13501432 SLAM_DASSERT(t.last_cam_ts[0] != INT64_MIN || cam_index == 0, "First frame was not a cam0 frame");
1351143313521434 // Check monotonically increasing timestamps
···1360144213611443 // Construct and send the image sample
13621444 cv::Mat img = t.cv_wrapper->wrap(frame);
13631363- SLAM_DASSERT_(frame->timestamp < INT64_MAX);
13641364- img_sample sample{ts, img, cam_index};
13651365- if (t.submit) {
14451445+14461446+ vit_img_sample sample = {};
14471447+ sample.cam_index = cam_index;
14481448+ sample.timestamp = ts;
14491449+ sample.data = img.ptr();
14501450+ sample.width = img.cols;
14511451+ sample.height = img.rows;
14521452+ sample.stride = img.step;
14531453+ sample.size = img.cols * img.rows;
14541454+14551455+ // TODO check format before
14561456+ switch (frame->format) {
14571457+ case XRT_FORMAT_L8: sample.format = VIT_IMAGE_FORMAT_L8; break;
14581458+ case XRT_FORMAT_R8G8B8: sample.format = VIT_IMAGE_FORMAT_R8G8B8; break;
14591459+ default: SLAM_ERROR("Unknown image format"); return;
14601460+ }
14611461+14621462+ // TODO masks
14631463+14641464+ {
13661465 XRT_TRACE_IDENT(slam_push);
13671367- t.slam->push_frame(sample);
14661466+ t.vit.tracker_push_img_sample(t.tracker, &sample);
13681467 }
13691468}
13701469···13921491 t_slam_receive_cam4, //
13931492};
1394149314941494+13951495extern "C" void
13961496t_slam_node_break_apart(struct xrt_frame_node *node)
13971497{
···13991499 if (t.ovr_tracker != NULL) {
14001500 t_openvr_tracker_stop(t.ovr_tracker);
14011501 }
14021402- t.slam->finalize();
14031403- t.slam->stop();
14041404- os_thread_helper_stop_and_wait(&t.oth);
15021502+15031503+ vit_result_t vres = t.vit.tracker_stop(t.tracker);
15041504+ if (vres != VIT_SUCCESS) {
15051505+ SLAM_ERROR("Failed to stop VIT tracker");
15061506+ return;
15071507+ }
15081508+14051509 SLAM_DEBUG("SLAM tracker dismantled");
14061510}
14071511···14141518 if (t.ovr_tracker != NULL) {
14151519 t_openvr_tracker_destroy(t.ovr_tracker);
14161520 }
14171417- os_thread_helper_destroy(&t_ptr->oth);
14181521 delete t.gt.trajectory;
14191522 delete t.slam_times_writer;
14201523 delete t.slam_features_writer;
···14301533 os_mutex_destroy(&t.lock_ff);
14311534 m_ff_vec3_f32_free(&t.filter.pos_ff);
14321535 m_ff_vec3_f32_free(&t.filter.rot_ff);
14331433- delete t_ptr->slam;
15361536+14341537 delete t_ptr->cv_wrapper;
14351435- delete t_ptr;
14361436-}
1437153814381438-//! Runs the external SLAM system in a separate thread
14391439-extern "C" void *
14401440-t_slam_run(void *ptr)
14411441-{
14421442- auto &t = *(TrackerSlam *)ptr;
14431443- SLAM_DEBUG("SLAM tracker starting");
14441444- t.slam->start();
14451445- return NULL;
15391539+ t_ptr->vit.tracker_destroy(t_ptr->tracker);
15401540+ t_vit_bundle_unload(&t_ptr->vit);
15411541+15421542+ delete t_ptr;
14461543}
1447154414481448-//! Starts t_slam_run
14491545extern "C" int
14501546t_slam_start(struct xrt_tracked_slam *xts)
14511547{
14521548 auto &t = *container_of(xts, TrackerSlam, base);
14531453- int ret = os_thread_helper_start(&t.oth, t_slam_run, &t);
14541454- SLAM_ASSERT(ret == 0, "Unable to start thread");
15491549+ vit_result_t vres = t.vit.tracker_start(t.tracker);
15501550+ if (vres != VIT_SUCCESS) {
15511551+ SLAM_ERROR("Failed to start VIT tracker");
15521552+ return -1;
15531553+ }
15541554+14551555 SLAM_DEBUG("SLAM tracker started");
14561456- return ret;
15561556+ return 0;
14571557}
1458155814591559extern "C" void
14601560t_slam_fill_default_config(struct t_slam_tracker_config *config)
14611561{
14621562 config->log_level = debug_get_log_option_slam_log();
15631563+ config->vit_system_library_path = debug_get_option_vit_system_library_path();
14631564 config->slam_config = debug_get_option_slam_config();
14641565 config->slam_ui = debug_get_bool_option_slam_ui();
14651566 config->submit_from_start = debug_get_bool_option_slam_submit_from_start();
···1487158814881589 enum u_logging_level log_level = config->log_level;
1489159014901490- // Check that the external SLAM system built is compatible
14911491- int ima = IMPLEMENTATION_VERSION_MAJOR;
14921492- int imi = IMPLEMENTATION_VERSION_MINOR;
14931493- int ipa = IMPLEMENTATION_VERSION_PATCH;
14941494- int hma = HEADER_VERSION_MAJOR;
14951495- int hmi = HEADER_VERSION_MINOR;
14961496- int hpa = HEADER_VERSION_PATCH;
14971497- U_LOG_IFL_I(log_level, "External SLAM system built %d.%d.%d, expected %d.%d.%d.", ima, imi, ipa, hma, hmi, hpa);
14981498- if (IMPLEMENTATION_VERSION_MAJOR != HEADER_VERSION_MAJOR) {
14991499- U_LOG_IFL_E(log_level, "Incompatible external SLAM system found.");
15911591+ std::unique_ptr<TrackerSlam> t_ptr = std::make_unique<TrackerSlam>();
15921592+ TrackerSlam &t = *t_ptr;
15931593+15941594+ t.log_level = log_level;
15951595+15961596+ if (config->vit_system_library_path == NULL) {
15971597+ SLAM_WARN("No VIT system library set, use VIT_SYSTEM_LIBRARY_PATH to set a tracker");
15981598+ SLAM_WARN("Attempting to load 'libbasalt.so' from system");
15991599+ config->vit_system_library_path = "libbasalt.so";
16001600+ }
16011601+16021602+ SLAM_INFO("Loading VIT system library from '%s'", config->vit_system_library_path);
16031603+16041604+ if (!t_vit_bundle_load(&t.vit, config->vit_system_library_path)) {
16051605+ SLAM_ERROR("Failed to load VIT system library from '%s'", config->vit_system_library_path);
15001606 return -1;
15011607 }
15021502- U_LOG_IFL_I(log_level, "Initializing compatible external SLAM system.");
1503160815041609 // Check the user has provided a SLAM_CONFIG file
15051610 const char *config_file = config->slam_config;
15061611 bool some_calib = config->slam_calib != nullptr;
15071612 if (!config_file && !some_calib) {
15081508- U_LOG_IFL_W(log_level, "Unable to determine sensor calibration, did you forget to set SLAM_CONFIG?");
16131613+ SLAM_WARN("Unable to determine sensor calibration, did you forget to set SLAM_CONFIG?");
15091614 return -1;
15101615 }
1511161615121512- auto &t = *(new TrackerSlam{});
15131513- t.log_level = log_level;
15141514- t.cv_wrapper = new MatFrame();
15151515-15161516- t.base.get_tracked_pose = t_slam_get_tracked_pose;
15171517-15181518- slam_config system_config = {};
15191519- system_config.config_file = config_file ? make_shared<string>(config_file) : nullptr;
16171617+ struct vit_config system_config = {};
16181618+ system_config.file = config_file;
15201619 system_config.cam_count = config->cam_count;
15211620 system_config.show_ui = config->slam_ui;
15221522- t.slam = new slam_tracker{system_config};
16211621+16221622+ vit_result_t vres = t.vit.tracker_create(&system_config, &t.tracker);
16231623+ if (vres != VIT_SUCCESS) {
16241624+ SLAM_ERROR("Failed to create VIT tracker");
16251625+ return -1;
16261626+ }
16271627+16281628+ vres = t.vit.tracker_get_pose_capabilities(t.tracker, &t.caps);
16291629+ if (vres != VIT_SUCCESS) {
16301630+ SLAM_ERROR("Failed to get VIT tracker pose capabilities");
16311631+ return -1;
16321632+ }
16331633+16341634+ t.base.get_tracked_pose = t_slam_get_tracked_pose;
1523163515241636 if (!config_file) {
15251637 SLAM_INFO("Using calibration from driver and default pipeline settings");
···15271639 } else {
15281640 SLAM_INFO("Using sensor calibration provided by the SLAM_CONFIG file");
15291641 }
15301530-15311531- t.slam->initialize();
1532164215331643 SLAM_ASSERT(t_slam_receive_cam[ARRAY_SIZE(t_slam_receive_cam) - 1] != nullptr, "See `cam_sink_push` docs");
15341644 t.sinks.cam_count = config->cam_count;
···15491659 t.node.break_apart = t_slam_node_break_apart;
15501660 t.node.destroy = t_slam_node_destroy;
1551166115521552- int ret = os_thread_helper_init(&t.oth);
15531553- SLAM_ASSERT(ret == 0, "Unable to initialize thread");
15541554-15551662 xrt_frame_context_add(xfctx, &t.node);
1556166315571664 t.euroc_recorder = euroc_recorder_create(xfctx, NULL, t.cam_count, false);
···1566167315671674 t.gt.trajectory = new Trajectory{};
1568167515691569- // Setup timing extension
15701570-15711571- // Probe for timing extension.
15721572- bool has_timing_extension = t.slam->supports_feature(F_ENABLE_POSE_EXT_TIMING);
15731573- t.timing.ext_available = has_timing_extension;
15741574-15751575- // We provide two timing columns by default, even if there is no extension support
15761576- t.timing.columns = {"sampled", "received_by_monado"};
15771577-15781578- if (has_timing_extension) {
15791579- bool enable_timing_extension = config->timing_stat;
15801580-15811581- const auto params = make_shared<FPARAMS_EPET>(enable_timing_extension);
15821582- shared_ptr<void> result;
15831583- t.slam->use_feature(F_ENABLE_POSE_EXT_TIMING, params, result);
15841584- vector<string> cols = *std::static_pointer_cast<FRESULT_EPET>(result);
15851585-15861586- t.timing.columns.insert(t.timing.columns.begin() + 1, cols.begin(), cols.end());
15871587- t.timing.ext_enabled = enable_timing_extension;
15881588- }
15891589-15901590- // Setup features extension
15911591- bool has_features_extension = t.slam->supports_feature(F_ENABLE_POSE_EXT_FEATURES);
15921592- t.features.ext_available = has_features_extension;
15931593- if (has_features_extension) {
15941594- bool enable_features_extension = config->features_stat;
15951595-15961596- const auto params = make_shared<FPARAMS_EPET>(enable_features_extension);
15971597- shared_ptr<void> _;
15981598- t.slam->use_feature(F_ENABLE_POSE_EXT_FEATURES, params, _);
15991599-16001600- t.features.ext_enabled = enable_features_extension;
16011601- }
16021602-16031676 // Setup CSV files
16041677 bool write_csvs = config->write_csvs;
16051678 string dir = config->csv_path;
···16211694 }
16221695 }
1623169616241624- *out_xts = &t.base;
16251625- *out_sink = &t.sinks;
16971697+ // Get ownership
16981698+ TrackerSlam *tracker = t_ptr.release();
16991699+17001700+ *out_xts = &tracker->base;
17011701+ *out_sink = &tracker->sinks;
1626170216271703 SLAM_DEBUG("SLAM tracker created");
16281704 return 0;
+7-6
src/xrt/auxiliary/tracking/t_tracking.h
···646646 */
647647struct t_slam_tracker_config
648648{
649649- enum u_logging_level log_level; //!< SLAM tracking logging level
650650- const char *slam_config; //!< Config file path, format is specific to the SLAM implementation in use
651651- int cam_count; //!< Number of cameras in use
652652- bool slam_ui; //!< Whether to open the external UI of the external SLAM system
653653- bool submit_from_start; //!< Whether to submit data to the SLAM tracker without user action
654654- int openvr_groundtruth_device; //!< If >0, use lighthouse as groundtruth, see @ref openvr_device
649649+ enum u_logging_level log_level; //!< SLAM tracking logging level
650650+ const char *vit_system_library_path; //!< Path to the VIT system library
651651+ const char *slam_config; //!< Config file path, format is specific to the SLAM implementation in use
652652+ int cam_count; //!< Number of cameras in use
653653+ bool slam_ui; //!< Whether to open the external UI of the external SLAM system
654654+ bool submit_from_start; //!< Whether to submit data to the SLAM tracker without user action
655655+ int openvr_groundtruth_device; //!< If >0, use lighthouse as groundtruth, see @ref openvr_device
655656 enum t_slam_prediction_type prediction; //!< Which level of prediction to use
656657 bool write_csvs; //!< Whether to enable CSV writers from the start for later analysis
657658 const char *csv_path; //!< Path to write CSVs to