The open source OpenXR runtime
at main 491 lines 13 kB view raw
1// Copyright 2020-2023, Collabora, Ltd. 2// Copyright 2020, Nova King. 3// SPDX-License-Identifier: BSL-1.0 4/*! 5 * @file 6 * @brief RealSense helper driver for in-device SLAM 6DOF tracking. 7 * @author Moshi Turner <moshiturner@protonmail.com> 8 * @author Nova King <technobaboo@gmail.com> 9 * @author Jakob Bornecrantz <jakob@collabora.com> 10 * @ingroup drv_realsense 11 */ 12 13#include "xrt/xrt_defines.h" 14#include "xrt/xrt_device.h" 15#include "math/m_api.h" 16#include "math/m_space.h" 17#include "math/m_predict.h" 18#include "math/m_relation_history.h" 19 20#include "os/os_time.h" 21#include "os/os_threading.h" 22 23#include "util/u_time.h" 24#include "util/u_device.h" 25#include "util/u_logging.h" 26 27#include "util/u_json.h" 28#include "util/u_config_json.h" 29 30#include "rs_driver.h" 31 32#include <librealsense2/rs.h> 33#include <librealsense2/h/rs_pipeline.h> 34#include <librealsense2/h/rs_option.h> 35#include <librealsense2/h/rs_frame.h> 36 37#include <stdio.h> 38#include <assert.h> 39#include <stdlib.h> 40 41 42/*! 43 * Convenience macro to print out a pose, only used for debugging 44 */ 45#define print_pose(msg, pose) \ 46 U_LOG_E(msg " %f %f %f %f %f %f %f", pose.position.x, pose.position.y, pose.position.z, pose.orientation.x, \ 47 pose.orientation.y, pose.orientation.z, pose.orientation.w) 48 49/*! 50 * Device-SLAM tracked RealSense device (T26X series). 51 * 52 * @implements xrt_device 53 */ 54struct rs_ddev 55{ 56 struct xrt_device base; 57 58 struct m_relation_history *relation_hist; 59 60 struct os_thread_helper oth; 61 62 bool enable_mapping; 63 bool enable_pose_jumping; 64 bool enable_relocalization; 65 bool enable_pose_prediction; 66 bool enable_pose_filtering; //!< Forward compatibility for when that 1-euro filter is working 67 68 struct rs_container rsc; //!< Container of realsense API related objects 69}; 70 71 72/*! 73 * Helper to convert a xdev to a @ref rs_ddev. 74 */ 75static inline struct rs_ddev * 76rs_ddev(struct xrt_device *xdev) 77{ 78 return (struct rs_ddev *)xdev; 79} 80 81/*! 82 * Simple helper to check and print error messages. 83 */ 84static int 85check_error(struct rs_ddev *rs, rs2_error *e) 86{ 87 if (e == NULL) { 88 return 0; 89 } 90 91 U_LOG_E("rs_error was raised when calling %s(%s):", rs2_get_failed_function(e), rs2_get_failed_args(e)); 92 U_LOG_E("%s", rs2_get_error_message(e)); 93 94 return 1; 95} 96 97/*! 98 * Frees all RealSense resources. 99 */ 100static void 101close_ddev(struct rs_ddev *rs) 102{ 103 struct rs_container *rsc = &rs->rsc; 104 rs2_pipeline_stop(rsc->pipeline, NULL); 105 rs_container_cleanup(&rs->rsc); 106} 107 108#define CHECK_RS2() \ 109 do { \ 110 if (check_error(rs, e) != 0) { \ 111 close_ddev(rs); \ 112 return 1; \ 113 } \ 114 } while (0) 115 116 117/*! 118 * Create all RealSense resources needed for 6DOF tracking. 119 */ 120static int 121create_ddev(struct rs_ddev *rs, int device_idx) 122{ 123 assert(rs != NULL); 124 rs2_error *e = NULL; 125 126 struct rs_container *rsc = &rs->rsc; 127 128 rsc->context = rs2_create_context(RS2_API_VERSION, &e); 129 CHECK_RS2(); 130 131 rsc->device_list = rs2_query_devices(rsc->context, &e); 132 CHECK_RS2(); 133 134 rsc->pipeline = rs2_create_pipeline(rsc->context, &e); 135 CHECK_RS2(); 136 137 rsc->config = rs2_create_config(&e); 138 CHECK_RS2(); 139 140 // Set the pipeline to start specifically on the realsense device the prober selected 141 rsc->device_idx = device_idx; 142 rsc->device = rs2_create_device(rsc->device_list, rsc->device_idx, &e); 143 CHECK_RS2(); 144 145 bool ddev_has_serial = rs2_supports_device_info(rsc->device, RS2_CAMERA_INFO_SERIAL_NUMBER, &e); 146 CHECK_RS2(); 147 148 if (ddev_has_serial) { 149 150 const char *ddev_serial = rs2_get_device_info(rsc->device, RS2_CAMERA_INFO_SERIAL_NUMBER, &e); 151 CHECK_RS2(); 152 153 rs2_config_enable_device(rsc->config, ddev_serial, &e); 154 CHECK_RS2(); 155 156 } else { 157 U_LOG_W("Unexpected, the realsense device in use does not provide a serial number."); 158 } 159 160 rs2_delete_device(rsc->device); 161 162 rs2_config_enable_stream(rsc->config, // 163 RS2_STREAM_POSE, // Type 164 0, // Index 165 0, // Width 166 0, // Height 167 RS2_FORMAT_6DOF, // Format 168 200, // FPS 169 &e); 170 CHECK_RS2(); 171 172 rsc->profile = rs2_config_resolve(rsc->config, rsc->pipeline, &e); 173 CHECK_RS2(); 174 175 rsc->device = rs2_pipeline_profile_get_device(rsc->profile, &e); 176 CHECK_RS2(); 177 178 rs2_sensor_list *sensors = rs2_query_sensors(rsc->device, &e); 179 CHECK_RS2(); 180 181 //! @todo 0 index hardcoded, check device with RS2_EXTENSION_POSE_SENSOR or similar instead 182 rs2_sensor *sensor = rs2_create_sensor(sensors, 0, &e); 183 CHECK_RS2(); 184 185 rs2_set_option((rs2_options *)sensor, RS2_OPTION_ENABLE_MAPPING, rs->enable_mapping ? 1.0f : 0.0f, &e); 186 CHECK_RS2(); 187 188 if (rs->enable_mapping) { 189 // Neither of these options mean anything if mapping is off; in fact it 190 // errors out if we mess with these with mapping off 191 rs2_set_option((rs2_options *)sensor, RS2_OPTION_ENABLE_RELOCALIZATION, 192 rs->enable_relocalization ? 1.0f : 0.0f, &e); 193 CHECK_RS2(); 194 195 rs2_set_option((rs2_options *)sensor, RS2_OPTION_ENABLE_POSE_JUMPING, 196 rs->enable_pose_jumping ? 1.0f : 0.0f, &e); 197 CHECK_RS2(); 198 } 199 200 rsc->profile = rs2_pipeline_start_with_config(rsc->pipeline, rsc->config, &e); 201 CHECK_RS2(); 202 203 rs2_delete_sensor(sensor); 204 rs2_delete_sensor_list(sensors); 205 206 return 0; 207} 208 209/*! 210 * Process a frame as 6DOF data, does not assume ownership of the frame. 211 */ 212static void 213process_frame(struct rs_ddev *rs, rs2_frame *frame) 214{ 215 rs2_error *e = NULL; 216 int ret = 0; 217 218 ret = rs2_is_frame_extendable_to(frame, RS2_EXTENSION_POSE_FRAME, &e); 219 if (check_error(rs, e) != 0 || ret == 0) { 220 return; 221 } 222 223 rs2_pose camera_pose; 224 rs2_pose_frame_get_pose_data(frame, &camera_pose, &e); 225 if (check_error(rs, e) != 0) { 226 return; 227 } 228 229#if 0 230 rs2_timestamp_domain domain = rs2_get_frame_timestamp_domain(frame, &e); 231 if (check_error(rs, e) != 0) { 232 return; 233 } 234#endif 235 236 double timestamp_milliseconds = rs2_get_frame_timestamp(frame, &e); 237 if (check_error(rs, e) != 0) { 238 return; 239 } 240 241 // Close enough 242 uint64_t now_real_ns = os_realtime_get_ns(); 243 uint64_t now_monotonic_ns = os_monotonic_get_ns(); 244 uint64_t timestamp_ns = (uint64_t)(timestamp_milliseconds * 1000.0 * 1000.0); 245 246 // How far in the past is it? 247 uint64_t diff_ns = now_real_ns - timestamp_ns; 248 249 // Adjust the timestamp to monotonic time. 250 timestamp_ns = now_monotonic_ns - diff_ns; 251 252 /* 253 * Transfer the data to the struct. 254 */ 255 256 struct xrt_space_relation relation; 257 258 // Rotation/angular 259 relation.pose.orientation.x = camera_pose.rotation.x; 260 relation.pose.orientation.y = camera_pose.rotation.y; 261 relation.pose.orientation.z = camera_pose.rotation.z; 262 relation.pose.orientation.w = camera_pose.rotation.w; 263 relation.angular_velocity.x = camera_pose.angular_velocity.x; 264 relation.angular_velocity.y = camera_pose.angular_velocity.y; 265 relation.angular_velocity.z = camera_pose.angular_velocity.z; 266 267 // Position/linear 268 relation.pose.position.x = camera_pose.translation.x; 269 relation.pose.position.y = camera_pose.translation.y; 270 relation.pose.position.z = camera_pose.translation.z; 271 relation.linear_velocity.x = camera_pose.velocity.x; 272 relation.linear_velocity.y = camera_pose.velocity.y; 273 relation.linear_velocity.z = camera_pose.velocity.z; 274 275 // clang-format off 276 relation.relation_flags = 277 XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | 278 XRT_SPACE_RELATION_POSITION_VALID_BIT | 279 XRT_SPACE_RELATION_LINEAR_VELOCITY_VALID_BIT | 280 XRT_SPACE_RELATION_ANGULAR_VELOCITY_VALID_BIT | 281 XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT | 282 XRT_SPACE_RELATION_POSITION_TRACKED_BIT; 283 // clang-format on 284 285 m_relation_history_push(rs->relation_hist, &relation, timestamp_ns); 286} 287 288static int 289update(struct rs_ddev *rs) 290{ 291 rs2_frame *frames; 292 rs2_error *e = NULL; 293 294 frames = rs2_pipeline_wait_for_frames(rs->rsc.pipeline, RS2_DEFAULT_TIMEOUT, &e); 295 if (check_error(rs, e) != 0) { 296 return 1; 297 } 298 299 int num_frames = rs2_embedded_frames_count(frames, &e); 300 if (check_error(rs, e) != 0) { 301 return 1; 302 } 303 304 for (int i = 0; i < num_frames; i++) { 305 rs2_frame *frame = rs2_extract_frame(frames, i, &e); 306 if (check_error(rs, e) != 0) { 307 rs2_release_frame(frames); 308 return 1; 309 } 310 311 // Does not assume ownership of the frame. 312 process_frame(rs, frame); 313 rs2_release_frame(frame); 314 315 rs2_release_frame(frames); 316 } 317 318 return 0; 319} 320 321static void * 322rs_run_thread(void *ptr) 323{ 324 struct rs_ddev *rs = (struct rs_ddev *)ptr; 325 326 os_thread_helper_lock(&rs->oth); 327 328 while (os_thread_helper_is_running_locked(&rs->oth)) { 329 330 os_thread_helper_unlock(&rs->oth); 331 332 int ret = update(rs); 333 if (ret < 0) { 334 return NULL; 335 } 336 } 337 338 return NULL; 339} 340 341static bool 342load_config(struct rs_ddev *rs) 343{ 344 struct u_config_json config_json = {0}; 345 346 u_config_json_open_or_create_main_file(&config_json); 347 if (!config_json.file_loaded) { 348 return false; 349 } 350 351 const cJSON *realsense_config_json = u_json_get(config_json.root, "config_realsense_ddev"); 352 if (realsense_config_json == NULL) { 353 return false; 354 } 355 356 const cJSON *mapping = u_json_get(realsense_config_json, "enable_mapping"); 357 const cJSON *pose_jumping = u_json_get(realsense_config_json, "enable_pose_jumping"); 358 const cJSON *relocalization = u_json_get(realsense_config_json, "enable_relocalization"); 359 const cJSON *pose_prediction = u_json_get(realsense_config_json, "enable_pose_prediction"); 360 const cJSON *pose_filtering = u_json_get(realsense_config_json, "enable_pose_filtering"); 361 362 // if json key isn't in the json, default to true. if it is in there, use json value 363 if (mapping != NULL) { 364 rs->enable_mapping = cJSON_IsTrue(mapping); 365 } 366 if (pose_jumping != NULL) { 367 rs->enable_pose_jumping = cJSON_IsTrue(pose_jumping); 368 } 369 if (relocalization != NULL) { 370 rs->enable_relocalization = cJSON_IsTrue(relocalization); 371 } 372 if (pose_prediction != NULL) { 373 rs->enable_pose_prediction = cJSON_IsTrue(pose_prediction); 374 } 375 if (pose_filtering != NULL) { 376 rs->enable_pose_filtering = cJSON_IsTrue(pose_filtering); 377 } 378 379 cJSON_Delete(config_json.root); 380 381 return true; 382} 383 384 385/* 386 * 387 * Device functions. 388 * 389 */ 390 391static xrt_result_t 392rs_ddev_get_tracked_pose(struct xrt_device *xdev, 393 enum xrt_input_name name, 394 int64_t at_timestamp_ns, 395 struct xrt_space_relation *out_relation) 396{ 397 struct rs_ddev *rs = rs_ddev(xdev); 398 399 if (name != XRT_INPUT_GENERIC_TRACKER_POSE) { 400 U_LOG_XDEV_UNSUPPORTED_INPUT(&rs->base, u_log_get_global_level(), name); 401 return XRT_ERROR_INPUT_UNSUPPORTED; 402 } 403 404 m_relation_history_get(rs->relation_hist, at_timestamp_ns, out_relation); 405 return XRT_SUCCESS; 406} 407 408static void 409rs_ddev_destroy(struct xrt_device *xdev) 410{ 411 struct rs_ddev *rs = rs_ddev(xdev); 412 413 // Destroy the thread object. 414 os_thread_helper_destroy(&rs->oth); 415 416 close_ddev(rs); 417 418 m_relation_history_destroy(&rs->relation_hist); 419 420 free(rs); 421} 422 423 424/* 425 * 426 * 'Exported' functions. 427 * 428 */ 429 430struct xrt_device * 431rs_ddev_create(int device_idx) 432{ 433 struct rs_ddev *rs = U_DEVICE_ALLOCATE(struct rs_ddev, U_DEVICE_ALLOC_TRACKING_NONE, 1, 0); 434 435 m_relation_history_create(&rs->relation_hist); 436 437 rs->enable_mapping = true; 438 rs->enable_pose_jumping = true; 439 rs->enable_relocalization = true; 440 rs->enable_pose_prediction = true; 441 rs->enable_pose_filtering = true; 442 443 if (load_config(rs)) { 444 U_LOG_D("Used config file"); 445 } else { 446 U_LOG_D("Did not use config file"); 447 } 448 449 U_LOG_D("Realsense opts are %i %i %i %i %i\n", rs->enable_mapping, rs->enable_pose_jumping, 450 rs->enable_relocalization, rs->enable_pose_prediction, rs->enable_pose_filtering); 451 u_device_populate_function_pointers(&rs->base, rs_ddev_get_tracked_pose, rs_ddev_destroy); 452 rs->base.name = XRT_DEVICE_REALSENSE; 453 rs->base.tracking_origin->type = XRT_TRACKING_TYPE_EXTERNAL_SLAM; 454 455 // Print name. 456 snprintf(rs->base.str, XRT_DEVICE_NAME_LEN, "Intel RealSense Device-SLAM"); 457 snprintf(rs->base.serial, XRT_DEVICE_NAME_LEN, "Intel RealSense Device-SLAM"); 458 459 rs->base.inputs[0].name = XRT_INPUT_GENERIC_TRACKER_POSE; 460 461 int ret = 0; 462 463 464 465 // Thread and other state. 466 ret = os_thread_helper_init(&rs->oth); 467 if (ret != 0) { 468 U_LOG_E("Failed to init threading!"); 469 rs_ddev_destroy(&rs->base); 470 return NULL; 471 } 472 473 ret = create_ddev(rs, device_idx); 474 if (ret != 0) { 475 rs_ddev_destroy(&rs->base); 476 return NULL; 477 } 478 479 ret = os_thread_helper_start(&rs->oth, rs_run_thread, rs); 480 if (ret != 0) { 481 U_LOG_E("Failed to start thread!"); 482 rs_ddev_destroy(&rs->base); 483 return NULL; 484 } 485 486 rs->base.supported.orientation_tracking = true; 487 rs->base.supported.position_tracking = true; 488 rs->base.device_type = XRT_DEVICE_TYPE_GENERIC_TRACKER; 489 490 return &rs->base; 491}