···3737#include <iomanip>
3838#include <map>
3939#include <string>
4040+#include <vector>
40414142#define SLAM_TRACE(...) U_LOG_IFL_T(t.log_level, __VA_ARGS__)
4243#define SLAM_DEBUG(...) U_LOG_IFL_D(t.log_level, __VA_ARGS__)
···7374DEBUG_GET_ONCE_OPTION(slam_csv_path, "SLAM_CSV_PATH", "evaluation/")
7475DEBUG_GET_ONCE_BOOL_OPTION(slam_timing_stat, "SLAM_TIMING_STAT", true)
7576DEBUG_GET_ONCE_BOOL_OPTION(slam_features_stat, "SLAM_FEATURES_STAT", true)
7777+DEBUG_GET_ONCE_NUM_OPTION(slam_cam_count, "SLAM_CAM_COUNT", 2)
76787779//! Namespace for the interface to the external SLAM tracking system
7880namespace xrt::auxiliary::tracking::slam {
···346348 struct xrt_frame_node node = {}; //!< Will be called on destruction
347349 slam_tracker *slam; //!< Pointer to the external SLAM system implementation
348350349349- struct xrt_slam_sinks sinks = {}; //!< Pointers to the sinks below
350350- struct xrt_frame_sink left_sink = {}; //!< Sends left camera frames to the SLAM system
351351- struct xrt_frame_sink right_sink = {}; //!< Sends right camera frames to the SLAM system
352352- struct xrt_imu_sink imu_sink = {}; //!< Sends imu samples to the SLAM system
353353- 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
351351+ struct xrt_slam_sinks sinks = {}; //!< Pointers to the sinks below
352352+ struct xrt_frame_sink cam_sinks[XRT_TRACKING_MAX_SLAM_CAMS]; //!< Sends camera frames to the SLAM system
353353+ struct xrt_imu_sink imu_sink = {}; //!< Sends imu samples to the SLAM system
354354+ struct xrt_pose_sink gt_sink = {}; //!< Register groundtruth trajectory for stats
355355+ bool submit; //!< Whether to submit data pushed to sinks to the SLAM tracker
356356+ int cam_count; //!< Number of cameras used for tracking
355357356358 enum u_logging_level log_level; //!< Logging level for the SLAM tracker, set by SLAM_LOG var
357359 struct os_thread_helper oth; //!< Thread where the external SLAM system runs
···360362 struct xrt_slam_sinks *euroc_recorder; //!< EuRoC dataset recording sinks
361363362364 // Used mainly for checking that the timestamps come in order
363363- timepoint_ns last_imu_ts = INT64_MIN; //!< Last received IMU sample timestamp
364364- timepoint_ns last_left_ts = INT64_MIN; //!< Last received left image timestamp
365365- timepoint_ns last_right_ts = INT64_MIN; //!< Last received right image timestamp
365365+ timepoint_ns last_imu_ts; //!< Last received IMU sample timestamp
366366+ vector<timepoint_ns> last_cam_ts; //!< Last received image timestamp per cam
366367367368 // Prediction
368369369370 //! Type of prediction to use
370371 t_slam_prediction_type pred_type;
371371- u_var_combo pred_combo; //!< UI combo box to select @ref pred_type
372372- RelationHistory slam_rels{}; //!< A history of relations produced purely from external SLAM tracker data
373373- struct m_ff_vec3_f32 *gyro_ff; //!< Last gyroscope samples
374374- struct m_ff_vec3_f32 *accel_ff; //!< Last accelerometer samples
375375- struct u_sink_debug ui_left_sink; //!< Sink to display left frames in UI
376376- struct u_sink_debug ui_right_sink; //!< Sink to display right frames in UI
372372+ u_var_combo pred_combo; //!< UI combo box to select @ref pred_type
373373+ RelationHistory slam_rels{}; //!< A history of relations produced purely from external SLAM tracker data
374374+ struct m_ff_vec3_f32 *gyro_ff; //!< Last gyroscope samples
375375+ struct m_ff_vec3_f32 *accel_ff; //!< Last accelerometer samples
376376+ vector<u_sink_debug> ui_sink; //!< Sink to display frames in UI of each camera
377377378378 //! Used to correct accelerometer measurements when integrating into the prediction.
379379 //! @todo Should be automatically computed instead of required to be filled manually through the UI.
···621621 return {time_ns_to_s(now - ts), double(count)};
622622 };
623623624624- t.features.fcs_ui.curve_count = NUM_CAMS;
624624+ t.features.fcs_ui.curve_count = t.cam_count;
625625 t.features.fcs_ui.xlabel = "Last seconds";
626626 t.features.fcs_ui.ylabel = "Number of features";
627627628628- t.features.fcs.resize(NUM_CAMS);
629629- for (int i = 0; i < NUM_CAMS; i++) {
628628+ t.features.fcs.resize(t.cam_count);
629629+ for (int i = 0; i < t.cam_count; i++) {
630630 auto &fc = t.features.fcs[i];
631631 fc.cam_name = "Cam" + to_string(i);
632632···956956 t.pred_combo.count = SLAM_PRED_COUNT;
957957 t.pred_combo.options = "None\0Interpolate SLAM poses\0Also gyro\0Also accel (needs gravity correction)\0\0";
958958 t.pred_combo.value = (int *)&t.pred_type;
959959- u_sink_debug_init(&t.ui_left_sink);
960960- u_sink_debug_init(&t.ui_right_sink);
959959+ t.ui_sink = vector<u_sink_debug>(t.cam_count);
960960+ for (size_t i = 0; i < t.ui_sink.size(); i++) {
961961+ u_sink_debug_init(&t.ui_sink[i]);
962962+ }
961963 m_ff_vec3_f32_alloc(&t.gyro_ff, 1000);
962964 m_ff_vec3_f32_alloc(&t.accel_ff, 1000);
963965 m_ff_vec3_f32_alloc(&t.filter.pos_ff, 1000);
···987989 u_var_add_ro_ff_vec3_f32(&t, t.gyro_ff, "Gyroscope");
988990 u_var_add_ro_ff_vec3_f32(&t, t.accel_ff, "Accelerometer");
989991 u_var_add_f32(&t, &t.gravity_correction.z, "Gravity Correction");
990990- u_var_add_sink_debug(&t, &t.ui_left_sink, "Left Camera");
991991- u_var_add_sink_debug(&t, &t.ui_right_sink, "Right Camera");
992992+ for (size_t i = 0; i < t.ui_sink.size(); i++) {
993993+ char label[] = "Camera NNNN";
994994+ (void)snprintf(label, sizeof(label), "Camera %zu", i);
995995+ u_var_add_sink_debug(&t, &t.ui_sink[i], label);
996996+ }
992997993998 u_var_add_gui_header(&t, NULL, "Stats");
994999 u_var_add_ro_ftext(&t, "\n%s", "Record to CSV files");
···11151120 }
11161121}
1117112211181118-11191123} // namespace xrt::auxiliary::tracking::slam
1120112411211125using namespace xrt::auxiliary::tracking::slam;
···1171117511721176//! Receive and send IMU samples to the external SLAM system
11731177extern "C" void
11741174-t_slam_imu_sink_push(struct xrt_imu_sink *sink, struct xrt_imu_sample *s)
11781178+t_slam_receive_imu(struct xrt_imu_sink *sink, struct xrt_imu_sample *s)
11751179{
11761180 auto &t = *container_of(sink, TrackerSlam, imu_sink);
11771181···1201120512021206//! Push the frame to the external SLAM system
12031207static void
12041204-push_frame(TrackerSlam &t, struct xrt_frame *frame, bool is_left)
12081208+receive_frame(TrackerSlam &t, struct xrt_frame *frame, int cam_index)
12051209{
12061206- SLAM_DASSERT(t.last_left_ts != INT64_MIN || is_left, "First frame was a right frame");
12101210+ SLAM_DASSERT(t.last_cam_ts[0] != INT64_MIN || cam_index == 0, "First frame was not a cam0 frame");
1207121112081212 // Construct and send the image sample
12091213 cv::Mat img = t.cv_wrapper->wrap(frame);
12101214 SLAM_DASSERT_(frame->timestamp < INT64_MAX);
12111211- img_sample sample{(int64_t)frame->timestamp, img, is_left};
12151215+ img_sample sample{(int64_t)frame->timestamp, img, cam_index};
12121216 if (t.submit) {
12131217 t.slam->push_frame(sample);
12141218 }
12151215- SLAM_TRACE("%s frame t=%lu", is_left ? " left" : "right", frame->timestamp);
12191219+ SLAM_TRACE("cam%d frame t=%lu", cam_index, frame->timestamp);
1216122012171221 // Check monotonically increasing timestamps
12181218- timepoint_ns &last_ts = is_left ? t.last_left_ts : t.last_right_ts;
12221222+ timepoint_ns &last_ts = t.last_cam_ts[cam_index];
12191223 SLAM_DASSERT(sample.timestamp > last_ts, "Frame (%ld) is older than last (%ld)", sample.timestamp, last_ts);
12201224 last_ts = sample.timestamp;
12211225}
1222122612231223-extern "C" void
12241224-t_slam_frame_sink_push_left(struct xrt_frame_sink *sink, struct xrt_frame *frame)
12251225-{
12261226- auto &t = *container_of(sink, TrackerSlam, left_sink);
12271227- int cam_id = 0;
12281228- push_frame(t, frame, cam_id);
12291229- u_sink_debug_push_frame(&t.ui_left_sink, frame);
12301230- xrt_sink_push_frame(t.euroc_recorder->cams[0], frame);
12311231-}
12271227+#define DEFINE_RECEIVE_CAM(cam_id) \
12281228+ extern "C" void t_slam_receive_cam##cam_id(struct xrt_frame_sink *sink, struct xrt_frame *frame) \
12291229+ { \
12301230+ auto &t = *container_of(sink, TrackerSlam, cam_sinks[cam_id]); \
12311231+ receive_frame(t, frame, cam_id); \
12321232+ u_sink_debug_push_frame(&t.ui_sink[cam_id], frame); \
12331233+ xrt_sink_push_frame(t.euroc_recorder->cams[cam_id], frame); \
12341234+ }
12351235+12361236+DEFINE_RECEIVE_CAM(0)
12371237+DEFINE_RECEIVE_CAM(1)
12381238+DEFINE_RECEIVE_CAM(2)
12391239+DEFINE_RECEIVE_CAM(3)
12401240+DEFINE_RECEIVE_CAM(4)
1232124112331233-extern "C" void
12341234-t_slam_frame_sink_push_right(struct xrt_frame_sink *sink, struct xrt_frame *frame)
12351235-{
12361236- auto &t = *container_of(sink, TrackerSlam, right_sink);
12371237- int cam_id = 1;
12381238- push_frame(t, frame, cam_id);
12391239- u_sink_debug_push_frame(&t.ui_right_sink, frame);
12401240- xrt_sink_push_frame(t.euroc_recorder->cams[1], frame);
12411241-}
12421242+//! Define a function for each XRT_TRACKING_MAX_SLAM_CAMS and reference it in this array
12431243+void (*t_slam_receive_cam[XRT_TRACKING_MAX_SLAM_CAMS])(xrt_frame_sink *, xrt_frame *) = {
12441244+ t_slam_receive_cam0, //
12451245+ t_slam_receive_cam1, //
12461246+ t_slam_receive_cam2, //
12471247+ t_slam_receive_cam3, //
12481248+ t_slam_receive_cam4, //
12491249+};
1242125012431251extern "C" void
12441252t_slam_node_break_apart(struct xrt_frame_node *node)
···12641272 delete t.pred_traj_writer;
12651273 delete t.filt_traj_writer;
12661274 u_var_remove_root(t_ptr);
12671267- u_sink_debug_destroy(&t.ui_left_sink);
12681268- u_sink_debug_destroy(&t.ui_right_sink);
12751275+ for (size_t i = 0; i < t.ui_sink.size(); i++) {
12761276+ u_sink_debug_destroy(&t.ui_sink[i]);
12771277+ }
12691278 m_ff_vec3_f32_free(&t.gyro_ff);
12701279 m_ff_vec3_f32_free(&t.accel_ff);
12711280 m_ff_vec3_f32_free(&t.filter.pos_ff);
···13081317 config->csv_path = debug_get_option_slam_csv_path();
13091318 config->timing_stat = debug_get_bool_option_slam_timing_stat();
13101319 config->features_stat = debug_get_bool_option_slam_features_stat();
13111311- config->stereo_calib = NULL;
13201320+ config->cam_count = int(debug_get_num_option_slam_cam_count());
13121321 config->imu_calib = NULL;
13131322 config->extra_calib = NULL;
13141323}
···1357136613581367 slam_config system_config = {};
13591368 system_config.config_file = config_file ? make_shared<string>(config_file) : nullptr;
13601360- system_config.cam_count = NUM_CAMS;
13691369+ system_config.cam_count = config->cam_count;
13611370 system_config.show_ui = config->slam_ui;
13621371 t.slam = new slam_tracker{system_config};
13631372···1370137913711380 t.slam->initialize();
1372138113731373- t.left_sink.push_frame = t_slam_frame_sink_push_left;
13741374- t.right_sink.push_frame = t_slam_frame_sink_push_right;
13751375- t.imu_sink.push_imu = t_slam_imu_sink_push;
13761376- t.gt_sink.push_pose = t_slam_gt_sink_push;
13821382+ SLAM_ASSERT(t_slam_receive_cam[ARRAY_SIZE(t_slam_receive_cam) - 1] != nullptr, "See `cam_sink_push` docs");
13831383+ t.sinks.cam_count = config->cam_count;
13841384+ for (int i = 0; i < XRT_TRACKING_MAX_SLAM_CAMS; i++) {
13851385+ t.cam_sinks[i].push_frame = t_slam_receive_cam[i];
13861386+ t.sinks.cams[i] = &t.cam_sinks[i];
13871387+ }
1377138813781378- t.sinks.cam_count = NUM_CAMS;
13791379- t.sinks.cams[0] = &t.left_sink;
13801380- t.sinks.cams[1] = &t.right_sink;
13891389+ t.imu_sink.push_imu = t_slam_receive_imu;
13811390 t.sinks.imu = &t.imu_sink;
13911391+13921392+ t.gt_sink.push_pose = t_slam_gt_sink_push;
13821393 t.sinks.gt = &t.gt_sink;
1383139413841395 t.submit = config->submit_from_start;
13961396+ t.cam_count = config->cam_count;
1385139713861398 t.node.break_apart = t_slam_node_break_apart;
13871399 t.node.destroy = t_slam_node_destroy;
···13921404 xrt_frame_context_add(xfctx, &t.node);
1393140513941406 t.euroc_recorder = euroc_recorder_create(xfctx, NULL, false);
14071407+14081408+ t.last_imu_ts = INT64_MIN;
14091409+ t.last_cam_ts = vector<timepoint_ns>(t.cam_count, INT64_MIN);
1395141013961411 t.pred_type = config->prediction;
13971412
+1
src/xrt/auxiliary/tracking/t_tracking.h
···626626{
627627 enum u_logging_level log_level; //!< SLAM tracking logging level
628628 const char *slam_config; //!< Config file path, format is specific to the SLAM implementation in use
629629+ int cam_count; //!< Number of cameras in use
629630 bool slam_ui; //!< Whether to open the external UI of the external SLAM system
630631 bool submit_from_start; //!< Whether to submit data to the SLAM tracker without user action
631632 enum t_slam_prediction_type prediction; //!< Which level of prediction to use
+1-1
src/xrt/drivers/rift_s/rift_s_tracker.c
···211211 t_slam_fill_default_config(&config);
212212213213 /* No need to refcount these parameters */
214214- config.stereo_calib = t->stereo_calib;
214214+ config.cam_count = 2;
215215 config.imu_calib = &t->slam_imu_calib;
216216 config.extra_calib = &t->slam_extra_calib;
217217
+1-1
src/xrt/drivers/wmr/wmr_hmd.c
···14621462#ifdef XRT_FEATURE_SLAM
14631463 struct t_slam_tracker_config config = {0};
14641464 t_slam_fill_default_config(&config);
14651465- config.stereo_calib = stereo_calib; // No need to do refcount here
14651465+ config.cam_count = 2;
14661466 config.imu_calib = imu_calib;
14671467 config.extra_calib = extra_calib;
14681468 if (debug_get_option_slam_submit_from_start() == NULL) {
+4-1
src/xrt/state_trackers/prober/p_tracking.c
···255255 assert(fact->xfs->source_id == 0xECD0FEED && "xfs is not Euroc, unsynced open_video_device?");
256256257257#ifdef XRT_FEATURE_SLAM
258258- int ret = t_slam_create(&fact->xfctx, NULL, &fact->xts, &sinks);
258258+ struct t_slam_tracker_config st_config;
259259+ t_slam_fill_default_config(&st_config);
260260+261261+ int ret = t_slam_create(&fact->xfctx, &st_config, &fact->xts, &sinks);
259262 if (ret != 0) {
260263 U_LOG_W("Unable to initialize SLAM tracking, the Euroc driver will not be tracked");
261264 }