File plugin.cpp
File List > plugins > realsense > plugin.cpp
Go to the documentation of this file
#include "plugin.hpp"
#include
#include
#include // Include OpenCV API
#include
#include
#include
using namespace ILLIXR;
using namespace ILLIXR::data_format;
static constexpr int IMAGE_WIDTH_D4XX = 640;
static constexpr int IMAGE_HEIGHT_D4XX = 480;
static constexpr int FPS_D4XX = 30;
static constexpr int GYRO_RATE_D4XX = 400; // 200 or 400
static constexpr int ACCEL_RATE_D4XX = 250; // 63 or 250
static constexpr int IMAGE_WIDTH_T26X = 848;
static constexpr int IMAGE_HEIGHT_T26X = 800;
[[maybe_unused]] realsense::realsense(const std::string& name, phonebook* pb)
: plugin{name, pb}
, switchboard_{phonebook_->lookup_impl<switchboard>()}
, clock_{phonebook_->lookup_impl<relative_clock>()}
, imu_{switchboard_->get_writer<imu_type>("imu")}
, cam_{switchboard_->get_writer<binocular_cam_type>("cam")}
, rgb_depth_{switchboard_->get_writer<rgb_depth_type>("rgb_depth")}
, realsense_cam_{switchboard_->get_env("REALSENSE_CAM", "auto")} {
spdlogger(switchboard_->get_env_char("REALSENSE_LOG_LEVEL"));
accel_data_.iteration = -1;
config_.disable_all_streams();
configure_camera();
}
void realsense::callback(const rs2::frame& frame) {
std::lock_guard<std::mutex> lock(mutex_);
// This lock guarantees that concurrent invocations of `callback` are serialized.
// Even if the API does not invoke `callback` in parallel, this is still important for the memory-model.
// Without this lock, prior invocations of `callback` are not necessarily "happens-before" ordered, so accessing
// persistent variables constitutes a data-race, which is undefined behavior in the C++ memory model.
// This callback function may start running before the relative clock is started. If that happens, the data
// timestamps will be messed up. We therefore add this guard to ignore all data samples before the clock is started.
if (!clock_->is_started()) {
return;
}
if (auto mf = frame.as<rs2::motion_frame>()) {
std::string s = mf.get_profile().stream_name();
if (s == "Accel") {
accel_data_.data = mf.get_motion_data();
accel_data_.iteration = iteration_accel_;
iteration_accel_++;
}
if (s == "Gyro") {
if (last_iteration_accel_ == accel_data_.iteration) {
return;
}
last_iteration_accel_ = accel_data_.iteration;
rs2_vector accel = accel_data_.data;
double ts = mf.get_timestamp();
rs2_vector gyro_data = mf.get_motion_data();
// IMU data
Eigen::Vector3f la = {accel.x, accel.y, accel.z};
Eigen::Vector3f av = {gyro_data.x, gyro_data.y, gyro_data.z};
// Time as ullong (nanoseconds)
auto imu_time = static_cast<ullong>(ts * 1000000);
if (!first_imu_time_) {
first_imu_time_ = imu_time;
first_real_time_imu_ = clock_->now();
}
// Time as time_point
time_point imu_time_point{*first_real_time_imu_ + std::chrono::nanoseconds(imu_time - *first_imu_time_)};
// Submit to switchboard
imu_.put(imu_.allocate<imu_type>({imu_time_point, av.cast<double>(), la.cast<double>()}));
}
}
if (auto fs = frame.as<rs2::frameset>()) {
double ts = fs.get_timestamp();
auto cam_time = static_cast<ullong>(ts * 1000000);
if (!first_cam_time_) {
first_cam_time_ = cam_time;
first_real_time_cam_ = clock_->now();
}
time_point cam_time_point{*first_real_time_cam_ + std::chrono::nanoseconds(cam_time - *first_cam_time_)};
if (cam_select_ == D4XXI) {
rs2::video_frame ir_frame_left = fs.get_infrared_frame(1);
rs2::video_frame ir_frame_right = fs.get_infrared_frame(2);
rs2::video_frame depth_frame = fs.get_depth_frame();
rs2::video_frame rgb_frame = fs.get_color_frame();
cv::Mat ir_left = cv::Mat(cv::Size(IMAGE_WIDTH_D4XX, IMAGE_HEIGHT_D4XX), CV_8UC1, (void*) ir_frame_left.get_data());
cv::Mat ir_right =
cv::Mat(cv::Size(IMAGE_WIDTH_D4XX, IMAGE_HEIGHT_D4XX), CV_8UC1, (void*) ir_frame_right.get_data());
cv::Mat rgb = cv::Mat(cv::Size(IMAGE_WIDTH_D4XX, IMAGE_HEIGHT_D4XX), CV_8UC3, (void*) rgb_frame.get_data());
cv::Mat depth = cv::Mat(cv::Size(IMAGE_WIDTH_D4XX, IMAGE_HEIGHT_D4XX), CV_16UC1, (void*) depth_frame.get_data());
cv::Mat converted_depth;
float depth_scale = pipeline_.get_active_profile()
.get_device()
.first<rs2::depth_sensor>()
.get_depth_scale(); // for converting measurements into millimeters
depth.convertTo(converted_depth, CV_32FC1, depth_scale * 1000.f);
cam_.put(cam_.allocate<binocular_cam_type>({cam_time_point, ir_left, ir_right}));
rgb_depth_.put(rgb_depth_.allocate<rgb_depth_type>({cam_time_point, rgb, depth}));
} else if (cam_select_ == T26X) {
rs2::video_frame fisheye_frame_left = fs.get_fisheye_frame(1);
rs2::video_frame fisheye_frame_right = fs.get_fisheye_frame(2);
cv::Mat fisheye_left =
cv::Mat(cv::Size(IMAGE_WIDTH_T26X, IMAGE_HEIGHT_T26X), CV_8UC1, (void*) fisheye_frame_left.get_data());
cv::Mat fisheye_right =
cv::Mat(cv::Size(IMAGE_WIDTH_T26X, IMAGE_HEIGHT_T26X), CV_8UC1, (void*) fisheye_frame_right.get_data());
cam_.put(cam_.allocate<binocular_cam_type>({cam_time_point, fisheye_left, fisheye_right}));
}
}
};
realsense::~realsense() {
pipeline_.stop();
}
void realsense::find_supported_devices(const rs2::device_list& devices) {
bool gyro_found{false};
bool accel_found{false};
for (rs2::device device : devices) {
if (device.supports(RS2_CAMERA_INFO_PRODUCT_LINE)) {
std::string product_line = device.get_info(RS2_CAMERA_INFO_PRODUCT_LINE);
#ifndef NDEBUG
spdlog::get(name_)->debug("Found Product Line: {}", product_line);
#endif
if (product_line == "D400") {
#ifndef NDEBUG
spdlog::get(name_)->debug("Checking for supported streams");
#endif
std::vector<rs2::sensor> sensors = device.query_sensors();
for (const rs2::sensor& sensor : sensors) {
std::vector<rs2::stream_profile> stream_profiles = sensor.get_stream_profiles();
// Currently, all D4XX cameras provide infrared, RGB, and depth, so we only need to check for accel and
// gyro
for (auto&& sp : stream_profiles) {
if (sp.stream_type() == RS2_STREAM_GYRO) {
gyro_found = true;
}
if (sp.stream_type() == RS2_STREAM_ACCEL) {
accel_found = true;
}
}
}
if (accel_found && gyro_found) {
D4XXI_found_ = true;
#ifndef NDEBUG
spdlog::get(name_)->debug("Supported D4XX found!");
#endif
}
} else if (product_line == "T200") {
T26X_found_ = true;
#ifndef NDEBUG
spdlog::get(name_)->debug("T26X found!");
#endif
}
}
}
if (!T26X_found_ && !D4XXI_found_) {
#ifndef NDEBUG
spdlog::get(name_)->warn("No supported Realsense device detected!");
#endif
}
}
void realsense::configure_camera() {
rs2::context ctx;
rs2::device_list devices = ctx.query_devices();
// This plugin assumes only one device should be connected to the system. If multiple supported devices are found the
// preference is to choose D4XX with IMU over T26X systems.
find_supported_devices(devices);
if (realsense_cam_ == "auto") {
if (D4XXI_found_) {
cam_select_ = D4XXI;
#ifndef NDEBUG
spdlog::get(name_)->debug("Setting cam_select_: D4XX");
#endif
} else if (T26X_found_) {
cam_select_ = T26X;
#ifndef NDEBUG
spdlog::get(name_)->debug("Setting cam_select_: T26X");
#endif
}
} else if ((realsense_cam_ == "D4XX") && D4XXI_found_) {
cam_select_ = D4XXI;
#ifndef NDEBUG
spdlog::get(name_)->debug("Setting cam_select_: D4XX");
#endif
} else if ((realsense_cam_ == "T26X") && T26X_found_) {
cam_select_ = T26X;
#ifndef NDEBUG
spdlog::get(name_)->debug("Setting cam_select_: T26X");
#endif
}
if (cam_select_ == UNSUPPORTED) {
ILLIXR::abort("Supported Realsense device NOT found!");
}
if (cam_select_ == T26X) {
// T26X series has fixed options for accel rate, gyro rate, fisheye resolution, and FPS
config_.enable_stream(RS2_STREAM_ACCEL, RS2_FORMAT_MOTION_XYZ32F); // 62 Hz
config_.enable_stream(RS2_STREAM_GYRO, RS2_FORMAT_MOTION_XYZ32F); // 200 Hz
config_.enable_stream(RS2_STREAM_FISHEYE, 1, RS2_FORMAT_Y8); // 848x800, 30 FPS
config_.enable_stream(RS2_STREAM_FISHEYE, 2, RS2_FORMAT_Y8); // 848x800, 30 FPS
profiles_ = pipeline_.start(config_, [&](const rs2::frame& frame) {
this->callback(frame);
});
} else if (cam_select_ == D4XXI) {
config_.enable_stream(RS2_STREAM_ACCEL, RS2_FORMAT_MOTION_XYZ32F,
ACCEL_RATE_D4XX); // adjustable to 0, 63 (default), 250 hz
config_.enable_stream(RS2_STREAM_GYRO, RS2_FORMAT_MOTION_XYZ32F,
GYRO_RATE_D4XX); // adjustable set to 0, 200 (default), 400 hz
config_.enable_stream(RS2_STREAM_INFRARED, 1, IMAGE_WIDTH_D4XX, IMAGE_HEIGHT_D4XX, RS2_FORMAT_Y8, FPS_D4XX);
config_.enable_stream(RS2_STREAM_INFRARED, 2, IMAGE_WIDTH_D4XX, IMAGE_HEIGHT_D4XX, RS2_FORMAT_Y8, FPS_D4XX);
config_.enable_stream(RS2_STREAM_COLOR, IMAGE_WIDTH_D4XX, IMAGE_HEIGHT_D4XX, RS2_FORMAT_BGR8, FPS_D4XX);
config_.enable_stream(RS2_STREAM_DEPTH, IMAGE_WIDTH_D4XX, IMAGE_HEIGHT_D4XX, RS2_FORMAT_Z16, FPS_D4XX);
profiles_ = pipeline_.start(config_, [&](const rs2::frame& frame) {
this->callback(frame);
});
profiles_.get_device().first<rs2::depth_sensor>().set_option(
RS2_OPTION_EMITTER_ENABLED, 0.f); // disables IR emitter to use stereo images for SLAM but degrades depth
// quality in low texture environments.
}
}
PLUGIN_MAIN(realsense)