/******************************************************************************* * Copyright (c) 2023 Orbbec 3D Technology, Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ #include "orbbec_camera/ob_camera_node.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "orbbec_camera/utils.h" #include #include #include "diagnostic_msgs/msg/diagnostic_status.hpp" #include "libobsensor/hpp/Utils.hpp" #if defined(USE_RK_HW_DECODER) #include "orbbec_camera/rk_mpp_decoder.h" #elif defined(USE_NV_HW_DECODER) #include "orbbec_camera/jetson_nv_decoder.h" #endif #include namespace orbbec_camera { using namespace std::chrono_literals; std::string OBCameraNode::normalizeDepthFilterName(const std::string &filter_name) { if (filter_name == "HardwareNoiseRemoval") { return "HardwareNoiseRemovalFilter"; } if (filter_name == "SpatialFilter") { return "SpatialAdvancedFilter"; } if (filter_name == "DispOutliers" || filter_name == "DepthOutliersFilter") { return "DispOutliersFilter"; } if (filter_name == "EnhancedDepth" || filter_name == "EnhancedDepthFilter") { return "EnhancedDepthFilter"; } return filter_name; } namespace { constexpr char kEnhancedDepthSupportedTargetResolutions[] = "640x480/1280x720/1280x800"; constexpr char kEnhancedDepthSupportedDepthFormats[] = "Y10/Y11/Y12/Y14/Y16/Z16"; std::string getDepthFilterStatusName(const std::string &filter_name) { if (filter_name == "SpatialAdvancedFilter") { return "SpatialFilter"; } if (filter_name == "DisparityTransform") { return "DisparityToDepth"; } return filter_name; } const std::unordered_map &enhancedDepthColorFormatMap() { static const std::unordered_map kFormatMap = { {OB_FORMAT_YUYV, FORMAT_YUYV_TO_RGB}, {OB_FORMAT_UYVY, FORMAT_UYVY_TO_RGB}, {OB_FORMAT_MJPG, FORMAT_MJPG_TO_RGB}, {OB_FORMAT_BGR, FORMAT_BGR_TO_RGB}, {OB_FORMAT_RGBA, FORMAT_RGBA_TO_RGB}, {OB_FORMAT_Y16, FORMAT_Y16_TO_RGB}, {OB_FORMAT_Y8, FORMAT_Y8_TO_RGB}, }; return kFormatMap; } bool isEnhancedDepthColorFormatSupported(OBFormat format) { return format == OB_FORMAT_RGB || enhancedDepthColorFormatMap().count(format) > 0; } std::filesystem::path resolveConfigJsonFilePath(const std::string &file_path) { std::filesystem::path path(file_path); if ((file_path == "~" || file_path.rfind("~/", 0) == 0) && std::getenv("HOME") != nullptr) { path = std::filesystem::path(std::getenv("HOME")); if (file_path.size() > 2) { path /= file_path.substr(2); } } if (path.is_relative()) { path = std::filesystem::absolute(path); } return path.lexically_normal(); } bool configJsonContainsApplicationConfig(const std::string &file_path, const rclcpp::Logger &logger) { if (file_path.empty()) { return false; } const auto resolved_file_path = resolveConfigJsonFilePath(file_path); std::ifstream config_file(resolved_file_path); if (!config_file.good()) { return false; } try { nlohmann::json config_json; config_file >> config_json; return config_json.contains("application_config"); } catch (const std::exception &e) { RCLCPP_WARN_STREAM(logger, "Config JSON application_config check failed file=" << resolved_file_path.string() << " error=\"" << e.what() << "\""); } return false; } std::string getDepthFilterStatusParamName(const std::string &filter_name, const std::string ¶m_name) { if (filter_name == "SpatialAdvancedFilter" && param_name == "disp_diff") { return "diff_threshold"; } if (filter_name == "SpatialModerateFilter" && param_name == "disp_diff") { return "diff_threshold"; } if (filter_name == "TemporalFilter" && param_name == "diff_scale") { return "diff_threshold"; } if (filter_name == "DecimationFilter" && param_name == "decimate") { return "scale"; } return param_name; } std::string getDepthFilterConfigParamName(const std::string &filter_name, const std::string ¶m_name) { if ((filter_name == "SpatialAdvancedFilter" || filter_name == "SpatialModerateFilter") && param_name == "diff_threshold") { return "disp_diff"; } if (filter_name == "TemporalFilter" && param_name == "diff_threshold") { return "diff_scale"; } if (filter_name == "DecimationFilter" && param_name == "scale") { return "decimate"; } if (filter_name == "SequenceIdFilter" && (param_name == "id" || param_name == "sequence_id" || param_name == "sequence_id_filter_id")) { return "sequenceid"; } if (filter_name == "HoleFillingFilter" && param_name == "mode") { return "hole_filling_mode"; } return param_name; } bool shouldExposeDepthFilterParams(const std::string &filter_name) { return filter_name != "MgcNoiseRemovalFilter" && filter_name != "LutNoiseRemovalFilter" && filter_name != "DisparityTransform" && filter_name != "EdgeNoiseRemovalFilter"; } std::string formatFilterConfigValue(const OBFilterConfigSchemaItem &config_schema, double value) { switch (config_schema.type) { case OB_FILTER_CONFIG_VALUE_TYPE_INT: { return std::to_string(static_cast(value)); } case OB_FILTER_CONFIG_VALUE_TYPE_BOOLEAN: return value != 0.0 ? std::string("true") : std::string("false"); case OB_FILTER_CONFIG_VALUE_TYPE_FLOAT: default: { std::ostringstream ss; ss << value; return ss.str(); } } } std::string trimFilterConfigValue(const std::string &value) { auto begin = value.begin(); while (begin != value.end() && std::isspace(static_cast(*begin))) { ++begin; } auto end = value.end(); while (end != begin && std::isspace(static_cast(*(end - 1)))) { --end; } return std::string(begin, end); } std::string lowerFilterConfigValue(std::string value) { std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); return value; } bool equalsIgnoreCase(std::string lhs, std::string rhs) { std::transform(lhs.begin(), lhs.end(), lhs.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); std::transform(rhs.begin(), rhs.end(), rhs.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); return lhs == rhs; } std::string dispOutliersSearchModeToString(int search_mode) { switch (search_mode) { case 0: return "FULL"; case 1: return "OFFSET_80"; default: return ""; } } bool parseDispOutliersSearchMode(const std::string &raw_value, int &search_mode, std::string &message, bool allow_sdk_default = false) { const auto value = trimFilterConfigValue(raw_value); const auto lower_value = lowerFilterConfigValue(value); if (allow_sdk_default && lower_value.empty()) { search_mode = -1; return true; } if (lower_value == "full") { search_mode = 0; return true; } if (lower_value == "offset_80") { search_mode = 1; return true; } message = allow_sdk_default ? "Filter config 'search_mode' expects one of FULL, OFFSET_80, or empty" : "Filter config 'search_mode' expects one of FULL, OFFSET_80"; return false; } std::string lowerParameterValue(std::string value) { std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); return value; } std::string formatParameterValue(const std::string &value) { return value.empty() ? std::string("\"\"") : "'" + value + "'"; } std::string formatValidParameterValues(const std::vector &valid_values) { std::stringstream ss; for (size_t i = 0; i < valid_values.size(); ++i) { if (i != 0) { ss << ", "; } ss << formatParameterValue(valid_values[i]); } return ss.str(); } std::string normalizeClosedSetParameterValue(const rclcpp::Logger &logger, const std::string ¶m_name, const std::string &value, const std::vector &valid_values, const std::string &default_value) { const auto lower_value = lowerParameterValue(value); for (const auto &valid_value : valid_values) { if (lower_value == lowerParameterValue(valid_value)) { return valid_value; } } RCLCPP_ERROR_STREAM(logger, "Invalid parameter " << param_name << " " << formatParameterValue(value) << ". Valid values: " << formatValidParameterValues(valid_values) << ". Skip setting and use " << formatParameterValue(default_value) << "."); return default_value; } bool parseFilterConfigDouble(const std::string &raw_value, double &parsed_value, std::string &message) { const auto value = trimFilterConfigValue(raw_value); if (value.empty()) { message = "Filter config value is empty"; return false; } try { size_t parsed_chars = 0; parsed_value = std::stod(value, &parsed_chars); if (parsed_chars != value.size()) { message = "Filter config value '" + raw_value + "' is not a valid number"; return false; } } catch (const std::exception &) { message = "Filter config value '" + raw_value + "' is not a valid number"; return false; } return true; } bool parseFilterConfigValue(const OBFilterConfigSchemaItem &schema, const std::string &raw_value, double &parsed_value, std::string &message) { const auto value = trimFilterConfigValue(raw_value); if (schema.type == OB_FILTER_CONFIG_VALUE_TYPE_BOOLEAN) { const auto lower_value = lowerFilterConfigValue(value); if (lower_value == "true" || lower_value == "1") { parsed_value = 1.0; return true; } if (lower_value == "false" || lower_value == "0") { parsed_value = 0.0; return true; } message = "Filter config '" + std::string(schema.name) + "' expects a boolean value"; return false; } if (!parseFilterConfigDouble(value, parsed_value, message)) { return false; } if (schema.type == OB_FILTER_CONFIG_VALUE_TYPE_INT && std::floor(parsed_value) != parsed_value) { message = "Filter config '" + std::string(schema.name) + "' expects an integer value"; return false; } if (parsed_value < schema.min || parsed_value > schema.max) { std::ostringstream ss; ss << "Filter config '" << schema.name << "' value " << parsed_value << " is out of range [" << schema.min << ", " << schema.max << "]"; message = ss.str(); return false; } return true; } int64_t getSystemNowUs() { return std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count(); } int64_t getSteadyNowUs() { return std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) .count(); } } // namespace void OBCameraNode::appendDepthFilterParam(DepthFilterState &filter_state, const std::string &name, const std::string &value) { orbbec_camera_msgs::msg::DepthFilterParam param; param.name = name; param.value = value; filter_state.params.push_back(param); } DepthFilterState OBCameraNode::buildDepthFilterState( const std::string &filter_name, bool enabled, const std::shared_ptr &filter) const { const auto normalized_filter_name = normalizeDepthFilterName(filter_name); DepthFilterState filter_state; filter_state.filter_name = getDepthFilterStatusName(normalized_filter_name); filter_state.enabled = enabled; auto to_param_value = [](const auto &value) { std::ostringstream ss; ss << value; return ss.str(); }; if (normalized_filter_name == "NoiseRemovalFilter") { appendDepthFilterParam(filter_state, "min_diff", to_param_value(noise_removal_filter_min_diff_)); appendDepthFilterParam(filter_state, "max_size", to_param_value(noise_removal_filter_max_size_)); } else if (normalized_filter_name == "HardwareNoiseRemovalFilter") { appendDepthFilterParam(filter_state, "threshold", to_param_value(hardware_noise_removal_filter_threshold_)); } else if (normalized_filter_name == "DispOutliersFilter") { appendDepthFilterParam(filter_state, "search_mode", dispOutliersSearchModeToString(disp_outliers_filter_search_mode_)); } if (filter_state.params.empty() && filter && shouldExposeDepthFilterParams(normalized_filter_name)) { auto format_filter_config_value = [](const OBFilterConfigSchemaItem &config_schema, double value) { switch (config_schema.type) { case OB_FILTER_CONFIG_VALUE_TYPE_INT: { return std::to_string(static_cast(value)); } case OB_FILTER_CONFIG_VALUE_TYPE_BOOLEAN: return value != 0.0 ? std::string("true") : std::string("false"); case OB_FILTER_CONFIG_VALUE_TYPE_FLOAT: default: { std::ostringstream ss; ss << value; return ss.str(); } } }; try { for (const auto &config_schema : filter->getConfigSchemaVec()) { if (config_schema.name == nullptr || config_schema.name[0] == '\0') { continue; } appendDepthFilterParam( filter_state, getDepthFilterStatusParamName(normalized_filter_name, config_schema.name), format_filter_config_value(config_schema, filter->getConfigValue(config_schema.name))); } } catch (const std::exception &) { // Keep the state without dynamic params if runtime querying fails. } } return filter_state; } DepthFilterState OBCameraNode::buildEnhancedDepthFilterState() const { DepthFilterState filter_state; filter_state.filter_name = "EnhancedDepthFilter"; filter_state.enabled = enable_enhanced_depth_.load(); appendDepthFilterParam(filter_state, "confidence_threshold", std::to_string(enhanced_depth_confidence_threshold_)); return filter_state; } void OBCameraNode::publishDepthFiltersStatus() { if (!depth_filters_status_pub_) { return; } std::vector> depth_filters_snapshot; { std::lock_guard depth_filter_lock(depth_filter_mutex_); depth_filters_snapshot = depth_filter_list_; } auto find_depth_filter = [&depth_filters_snapshot](const std::string &filter_name) -> std::shared_ptr { const auto normalized_name = normalizeDepthFilterName(filter_name); auto it = std::find_if(depth_filters_snapshot.begin(), depth_filters_snapshot.end(), [&normalized_name](const auto &filter) { return normalizeDepthFilterName(filter->type()) == normalized_name || normalizeDepthFilterName(filter->getName()) == normalized_name; }); if (it == depth_filters_snapshot.end()) { return nullptr; } return *it; }; auto sync_filter_enabled = [&find_depth_filter](const std::string &filter_name, bool &cached_state) { auto filter = find_depth_filter(filter_name); if (!filter) { return; } try { cached_state = filter->isEnabled(); } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } }; sync_filter_enabled("DecimationFilter", enable_decimation_filter_); sync_filter_enabled("HDRMerge", enable_hdr_merge_); sync_filter_enabled("SequenceIdFilter", enable_sequence_id_filter_); sync_filter_enabled("SpatialAdvancedFilter", enable_spatial_filter_); sync_filter_enabled("TemporalFilter", enable_temporal_filter_); sync_filter_enabled("HoleFillingFilter", enable_hole_filling_filter_); sync_filter_enabled("EdgeNoiseRemovalFilter", enable_edge_noise_removal_filter_); sync_filter_enabled("DisparityTransform", enable_disparity_to_depth_); sync_filter_enabled("ThresholdFilter", enable_threshold_filter_); sync_filter_enabled("SpatialFastFilter", enable_spatial_fast_filter_); sync_filter_enabled("SpatialModerateFilter", enable_spatial_moderate_filter_); sync_filter_enabled("FalsePositiveFilter", enable_false_positive_filter_); sync_filter_enabled("MgcNoiseRemovalFilter", enable_mgc_noise_removal_filter_); sync_filter_enabled("LutNoiseRemovalFilter", enable_lut_noise_removal_filter_); if (device_->isPropertySupported(OB_PROP_DEPTH_SOFT_FILTER_BOOL, OB_PERMISSION_READ_WRITE)) { try { enable_noise_removal_filter_ = device_->getBoolProperty(OB_PROP_DEPTH_SOFT_FILTER_BOOL); } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } if (device_->isPropertySupported(OB_PROP_DEPTH_MAX_DIFF_INT, OB_PERMISSION_WRITE)) { try { noise_removal_filter_min_diff_ = device_->getIntProperty(OB_PROP_DEPTH_MAX_DIFF_INT); } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } if (device_->isPropertySupported(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT, OB_PERMISSION_WRITE)) { try { noise_removal_filter_max_size_ = device_->getIntProperty(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT); } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } if (device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL, OB_PERMISSION_READ_WRITE)) { try { enable_hardware_noise_removal_filter_ = device_->getBoolProperty(OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL); } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } if (device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT, OB_PERMISSION_READ_WRITE)) { try { hardware_noise_removal_filter_threshold_ = device_->getFloatProperty(OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT); } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } if (device_->isPropertySupported(OB_PROP_DEPTH_OUTLIERS_FILTER_BOOL, OB_PERMISSION_READ_WRITE)) { try { enable_disp_outliers_filter_ = device_->getBoolProperty(OB_PROP_DEPTH_OUTLIERS_FILTER_BOOL); } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } if (device_->isPropertySupported(OB_PROP_DEPTH_OUTLIERS_FILTER_SEARCH_MODE_INT, OB_PERMISSION_READ_WRITE)) { try { disp_outliers_filter_search_mode_ = device_->getIntProperty(OB_PROP_DEPTH_OUTLIERS_FILTER_SEARCH_MODE_INT); } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } if (auto filter = find_depth_filter("DecimationFilter")) { try { decimation_filter_scale_ = static_cast(filter->as()->getScaleValue()); } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } if (auto filter = find_depth_filter("SequenceIdFilter")) { try { sequence_id_filter_id_ = filter->as()->getSelectSequenceId(); } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } if (auto filter = find_depth_filter("ThresholdFilter")) { try { threshold_filter_min_ = static_cast(filter->getConfigValue("min")); threshold_filter_max_ = static_cast(filter->getConfigValue("max")); } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } if (auto filter = find_depth_filter("SpatialAdvancedFilter")) { try { auto params = filter->as()->getFilterParams(); spatial_filter_alpha_ = params.alpha; spatial_filter_diff_threshold_ = params.disp_diff; spatial_filter_magnitude_ = params.magnitude; spatial_filter_radius_ = params.radius; } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } if (auto filter = find_depth_filter("TemporalFilter")) { try { temporal_filter_diff_threshold_ = static_cast(filter->getConfigValue("diff_scale")); temporal_filter_weight_ = static_cast(filter->getConfigValue("weight")); } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } if (auto filter = find_depth_filter("SpatialFastFilter")) { try { auto params = filter->as()->getFilterParams(); spatial_fast_filter_radius_ = params.radius; } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } if (auto filter = find_depth_filter("SpatialModerateFilter")) { try { auto params = filter->as()->getFilterParams(); spatial_moderate_filter_diff_threshold_ = params.disp_diff; spatial_moderate_filter_magnitude_ = params.magnitude; spatial_moderate_filter_radius_ = params.radius; } catch (const std::exception &) { // Keep the cached value if runtime querying fails. } } DepthFiltersStatus msg; msg.header.stamp = node_->now(); msg.header.frame_id = camera_name_; const bool noise_removal_filter_supported = device_->isPropertySupported(OB_PROP_DEPTH_SOFT_FILTER_BOOL, OB_PERMISSION_READ_WRITE) || device_->isPropertySupported(OB_PROP_DEPTH_MAX_DIFF_INT, OB_PERMISSION_WRITE) || device_->isPropertySupported(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT, OB_PERMISSION_WRITE); const bool hardware_noise_removal_filter_supported = device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL, OB_PERMISSION_READ_WRITE) || device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT, OB_PERMISSION_READ_WRITE); const bool disp_outliers_filter_supported = device_->isPropertySupported(OB_PROP_DEPTH_OUTLIERS_FILTER_BOOL, OB_PERMISSION_READ_WRITE) || device_->isPropertySupported(OB_PROP_DEPTH_OUTLIERS_FILTER_SEARCH_MODE_INT, OB_PERMISSION_READ_WRITE); std::vector ordered_filter_names; ordered_filter_names.reserve(depth_filters_snapshot.size() + 3); auto append_unique_filter_name = [&ordered_filter_names](const std::string &filter_name) { if (std::find(ordered_filter_names.begin(), ordered_filter_names.end(), filter_name) == ordered_filter_names.end()) { ordered_filter_names.push_back(filter_name); } }; for (const auto &filter : depth_filters_snapshot) { if (!filter) { continue; } append_unique_filter_name(normalizeDepthFilterName(filter->type())); } if (noise_removal_filter_supported) { append_unique_filter_name("NoiseRemovalFilter"); } if (hardware_noise_removal_filter_supported) { append_unique_filter_name("HardwareNoiseRemovalFilter"); } if (disp_outliers_filter_supported) { append_unique_filter_name("DispOutliersFilter"); } append_unique_filter_name("EnhancedDepthFilter"); msg.filters.reserve(ordered_filter_names.size()); for (const auto &filter_name : ordered_filter_names) { if (filter_name == "EnhancedDepthFilter") { msg.filters.push_back(buildEnhancedDepthFilterState()); continue; } bool enabled = false; auto filter = find_depth_filter(filter_name); if (filter_name == "NoiseRemovalFilter") { enabled = enable_noise_removal_filter_; } else if (filter_name == "HardwareNoiseRemovalFilter") { enabled = enable_hardware_noise_removal_filter_; } else if (filter_name == "DispOutliersFilter") { enabled = enable_disp_outliers_filter_; } if (filter && filter_name != "DispOutliersFilter") { try { enabled = filter->isEnabled(); } catch (const std::exception &) { // Keep default value when runtime querying fails. } } msg.filters.push_back(buildDepthFilterState(filter_name, enabled, filter)); } depth_filters_status_pub_->publish(msg); } void OBCameraNode::publishLrmObstacleDistance() { if (!lrm_obstacle_distance_pub_) { return; } if (lrm_obstacle_distance_pub_->get_subscription_count() == 0) { return; } try { std_msgs::msg::Int32 msg; { std::lock_guard lock(device_lock_); msg.data = device_->getIntProperty(OB_PROP_LDP_MEASURE_DISTANCE_INT); } lrm_obstacle_distance_pub_->publish(msg); } catch (const ob::Error &e) { auto message = orbbec_camera::formatObErrorWithStatus(e); RCLCPP_WARN_THROTTLE(logger_, *node_->get_clock(), 5000, "Failed to publish LRM obstacle distance: %s", message.c_str()); } catch (const std::exception &e) { RCLCPP_WARN_THROTTLE(logger_, *node_->get_clock(), 5000, "Failed to publish LRM obstacle distance: %s", e.what()); } catch (...) { RCLCPP_WARN_THROTTLE(logger_, *node_->get_clock(), 5000, "Failed to publish LRM obstacle distance: unknown error"); } } OBCameraNode::OBCameraNode(rclcpp::Node *node, std::shared_ptr device, std::shared_ptr parameters, bool use_intra_process, bool is_playback_device) : node_(node), device_(std::move(device)), parameters_(std::move(parameters)), logger_(node->get_logger()), use_intra_process_(use_intra_process), is_playback_device_(is_playback_device) { pid_ = device_->getDeviceInfo()->getPid(); RCLCPP_INFO_STREAM(logger_, "OBCameraNode: use_intra_process: " << (use_intra_process ? "ON" : "OFF")); is_running_.store(true); stream_name_[COLOR] = "color"; stream_name_[COLOR_LEFT] = "left_color"; stream_name_[COLOR_RIGHT] = "right_color"; stream_name_[DEPTH] = "depth"; stream_name_[INFRA0] = "ir"; stream_name_[INFRA1] = "left_ir"; stream_name_[INFRA2] = "right_ir"; stream_name_[ACCEL] = "accel"; stream_name_[GYRO] = "gyro"; compression_params_.push_back(cv::IMWRITE_PNG_COMPRESSION); compression_params_.push_back(0); compression_params_.push_back(cv::IMWRITE_PNG_STRATEGY); compression_params_.push_back(cv::IMWRITE_PNG_STRATEGY_DEFAULT); setupDefaultImageFormat(); setupTopics(); if (enable_frame_drop_log_ || !frame_timestamp_csv_file_.empty()) { if (enable_frame_sync_ && (enable_stream_[COLOR] || enable_stream_[DEPTH])) { frame_timestamp_csv_logger_ = std::make_unique( enable_frame_drop_log_, frame_timestamp_csv_file_, FrameTimestampCsvLogger::OutputMode::SYNCED, logger_); if (!frame_timestamp_csv_logger_->enabled()) { frame_timestamp_csv_logger_.reset(); } } else { if (enable_stream_[COLOR]) { color_timestamp_csv_logger_ = std::make_unique( enable_frame_drop_log_, frame_timestamp_csv_file_, FrameTimestampCsvLogger::OutputMode::COLOR, logger_); if (!color_timestamp_csv_logger_->enabled()) { color_timestamp_csv_logger_.reset(); } } if (enable_stream_[DEPTH]) { depth_timestamp_csv_logger_ = std::make_unique( enable_frame_drop_log_, frame_timestamp_csv_file_, FrameTimestampCsvLogger::OutputMode::DEPTH, logger_); if (!depth_timestamp_csv_logger_->enabled()) { depth_timestamp_csv_logger_.reset(); } } } if (!frame_timestamp_csv_file_.empty() && (enable_stream_[ACCEL] || enable_stream_[GYRO])) { if (enable_sync_output_accel_gyro_) { imu_timestamp_csv_logger_ = std::make_unique( frame_timestamp_csv_file_, ImuTimestampCsvLogger::OutputMode::SYNCED, logger_); if (!imu_timestamp_csv_logger_->enabled()) { imu_timestamp_csv_logger_.reset(); } } else { if (enable_stream_[ACCEL]) { accel_timestamp_csv_logger_ = std::make_unique( frame_timestamp_csv_file_, ImuTimestampCsvLogger::OutputMode::ACCEL, logger_); if (!accel_timestamp_csv_logger_->enabled()) { accel_timestamp_csv_logger_.reset(); } } if (enable_stream_[GYRO]) { gyro_timestamp_csv_logger_ = std::make_unique( frame_timestamp_csv_file_, ImuTimestampCsvLogger::OutputMode::GYRO, logger_); if (!gyro_timestamp_csv_logger_->enabled()) { gyro_timestamp_csv_logger_.reset(); } } } } } if (enable_d2c_viewer_) { auto rgb_qos = getRMWQosProfileFromString(image_qos_[COLOR]); auto depth_qos = getRMWQosProfileFromString(image_qos_[DEPTH]); d2c_viewer_ = std::make_unique(node_, rgb_qos, depth_qos, use_intra_process_); } setupImageBuffers(); is_camera_node_initialized_ = true; fps_counter_color_ = std::make_unique("Color", logger_, 1); fps_counter_depth_ = std::make_unique("Depth", logger_, 1); fps_counter_left_ir_ = std::make_unique("Left Ir", logger_, 1); fps_counter_right_ir_ = std::make_unique("Right Ir", logger_, 1); LogLevel log_level = LogLevel::DEBUG; if (show_fps_enable_) { log_level = LogLevel::INFO; } fps_counter_color_->setLogLevel(log_level); fps_counter_depth_->setLogLevel(log_level); fps_counter_left_ir_->setLogLevel(log_level); fps_counter_right_ir_->setLogLevel(log_level); fps_delay_status_color_ = std::make_unique(logger_); fps_delay_status_depth_ = std::make_unique(logger_); } template void OBCameraNode::setAndGetNodeParameter( T ¶m, const std::string ¶m_name, const T &default_value, const rcl_interfaces::msg::ParameterDescriptor ¶meter_descriptor) { try { param = parameters_ ->setParam(param_name, rclcpp::ParameterValue(default_value), std::function(), parameter_descriptor) .get(); } catch (const rclcpp::ParameterTypeException &ex) { RCLCPP_ERROR_STREAM(logger_, "Failed to set parameter: " << param_name << ". " << ex.what()); throw; } } OBCameraNode::~OBCameraNode() noexcept { clean(); } void OBCameraNode::rebootDevice() { RCLCPP_DEBUG_STREAM(logger_, "Cleaning before rebooting device"); malloc_trim(0); clean(); malloc_trim(0); std::lock_guard lock(device_lock_); RCLCPP_INFO_STREAM(logger_, "Rebooting device"); if (device_) { device_->reboot(); } malloc_trim(0); RCLCPP_DEBUG_STREAM(logger_, "Reboot device complete"); } void OBCameraNode::clean() noexcept { if (cleaning_.exchange(true)) { RCLCPP_DEBUG(logger_, "clean() already running, skip re-entry"); return; } // Set running flags to false first to signal all operations to stop is_running_.store(false); is_camera_node_initialized_.store(false); try { if (frame_timestamp_csv_logger_) { frame_timestamp_csv_logger_->shutdown(); frame_timestamp_csv_logger_.reset(); } } catch (...) { RCLCPP_WARN_STREAM(logger_, "Exception while shutting down frame timestamp CSV logger"); } try { if (color_timestamp_csv_logger_) { color_timestamp_csv_logger_->shutdown(); color_timestamp_csv_logger_.reset(); } } catch (...) { RCLCPP_WARN_STREAM(logger_, "Exception while shutting down color timestamp CSV logger"); } try { if (depth_timestamp_csv_logger_) { depth_timestamp_csv_logger_->shutdown(); depth_timestamp_csv_logger_.reset(); } } catch (...) { RCLCPP_WARN_STREAM(logger_, "Exception while shutting down depth timestamp CSV logger"); } try { if (imu_timestamp_csv_logger_) { imu_timestamp_csv_logger_->shutdown(); imu_timestamp_csv_logger_.reset(); } } catch (...) { RCLCPP_WARN_STREAM(logger_, "Exception while shutting down IMU timestamp CSV logger"); } try { if (accel_timestamp_csv_logger_) { accel_timestamp_csv_logger_->shutdown(); accel_timestamp_csv_logger_.reset(); } } catch (...) { RCLCPP_WARN_STREAM(logger_, "Exception while shutting down accel timestamp CSV logger"); } try { if (gyro_timestamp_csv_logger_) { gyro_timestamp_csv_logger_->shutdown(); gyro_timestamp_csv_logger_.reset(); } } catch (...) { RCLCPP_WARN_STREAM(logger_, "Exception while shutting down gyro timestamp CSV logger"); } // Stop diagnostic timer and updater first BEFORE acquiring device_lock to prevent deadlock try { if (diagnostic_timer_) { diagnostic_timer_->cancel(); // Wait for any currently executing timer callbacks to complete { std::unique_lock lk(diagnostic_mutex_); diagnostic_cv_.wait(lk, [this]() { return !diagnostic_running_; }); } diagnostic_timer_.reset(); } if (software_trigger_timer_) { software_trigger_timer_->cancel(); software_trigger_timer_.reset(); } if (lrm_obstacle_distance_timer_) { lrm_obstacle_distance_timer_->cancel(); lrm_obstacle_distance_timer_.reset(); } if (diagnostic_updater_) { diagnostic_updater_.reset(); } } catch (...) { // Ignore exceptions during diagnostic cleanup } // Now acquire the device lock for the rest of the cleanup std::lock_guard lock(device_lock_); RCLCPP_DEBUG_STREAM(logger_, "Do OBCameraNode clean"); RCLCPP_DEBUG_STREAM(logger_, "Stop tf thread"); try { if (tf_thread_ && tf_thread_->joinable()) { tf_cv_.notify_all(); // Wake up tf thread if it's waiting tf_thread_->join(); } } catch (...) { RCLCPP_DEBUG_STREAM(logger_, "Exception while stopping tf thread"); } RCLCPP_DEBUG_STREAM(logger_, "Stop color frame thread"); try { stopColorFrameThreads(); } catch (...) { RCLCPP_DEBUG_STREAM(logger_, "Exception while stopping color frame thread"); } RCLCPP_DEBUG_STREAM(logger_, "stop streams"); try { stopIMU(); stopStreams(); { std::lock_guard lk(frame_info_logged_mutex_); frame_info_logged_.clear(); } } catch (...) { RCLCPP_DEBUG_STREAM(logger_, "Exception while stopping streams"); } // Clean up d2c_viewer_ before cleaning buffers RCLCPP_DEBUG_STREAM(logger_, "Clean d2c_viewer"); try { if (d2c_viewer_) { d2c_viewer_.reset(); } } catch (...) { RCLCPP_DEBUG_STREAM(logger_, "Exception while cleaning up d2c_viewer"); } RCLCPP_DEBUG_STREAM(logger_, "Clean up buffers"); try { delete[] rgb_buffer_; rgb_buffer_ = nullptr; rgb_buffer_size_ = 0; delete[] rgb_buffer_left_; rgb_buffer_left_ = nullptr; rgb_buffer_left_size_ = 0; delete[] rgb_buffer_right_; rgb_buffer_right_ = nullptr; rgb_buffer_right_size_ = 0; if (jpeg_decoder_) { jpeg_decoder_.reset(); } if (jpeg_decoder_left_) { jpeg_decoder_left_.reset(); } if (jpeg_decoder_right_) { jpeg_decoder_right_.reset(); } } catch (...) { RCLCPP_DEBUG_STREAM(logger_, "Exception while cleaning up buffers"); } RCLCPP_DEBUG_STREAM(logger_, "OBCameraNode cleanup complete"); cleaning_.store(false); } void OBCameraNode::setupDevices() { if (!depth_work_mode_.empty() && device_->isPropertySupported(OB_STRUCT_CURRENT_DEPTH_ALG_MODE, OB_PERMISSION_READ_WRITE)) { auto depthModeList = device_->getDepthWorkModeList(); for (uint32_t i = 0; i < depthModeList->getCount(); i++) { RCLCPP_INFO_STREAM(logger_, "depthModeList[" << i << "]: " << (*depthModeList)[i].name); } TRY_EXECUTE_BLOCK(device_->switchDepthWorkMode(depth_work_mode_.c_str())); RCLCPP_INFO_STREAM(logger_, "Set device preset: " << depth_work_mode_); } else if (!device_preset_.empty()) { try { RCLCPP_DEBUG_STREAM(logger_, "Available presets:"); auto preset_list = device_->getAvailablePresetList(); for (uint32_t i = 0; i < preset_list->getCount(); i++) { RCLCPP_DEBUG_STREAM(logger_, "Preset " << i << ": " << preset_list->getName(i)); } TRY_EXECUTE_BLOCK(device_->loadPreset(device_preset_.c_str())); RCLCPP_INFO_STREAM(logger_, "Loaded device preset: " << device_->getCurrentPresetName()); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM( logger_, "Failed to load device preset: " << orbbec_camera::formatObErrorWithStatus(e)); } catch (const std::exception &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to load device preset: " << e.what()); } catch (...) { RCLCPP_ERROR_STREAM(logger_, "Failed to load device preset"); } } if (!preset_resolution_config_.empty()) { OBPresetResolutionConfig presetResolutionConfig; std::istringstream iss(preset_resolution_config_); std::string token; std::vector values; values.reserve(4); while (std::getline(iss, token, ',')) { values.push_back(std::stoi(token)); } if (values.size() >= 4) { presetResolutionConfig.width = values[0]; presetResolutionConfig.height = values[1]; presetResolutionConfig.irDecimationFactor = values[2]; presetResolutionConfig.depthDecimationFactor = values[3]; } else { RCLCPP_WARN_STREAM( logger_, "Invalid preset_resolution_config parameter. " "Expected format: width,height,ir_decimation_factor,depth_decimation_factor"); } RCLCPP_INFO_STREAM( logger_, "Set preset resolution config: " << "width=" << presetResolutionConfig.width << ", height=" << presetResolutionConfig.height << ", ir_decimation=" << presetResolutionConfig.irDecimationFactor << ", depth_decimation=" << presetResolutionConfig.depthDecimationFactor); TRY_EXECUTE_BLOCK(device_->setStructuredData(OB_STRUCT_PRESET_RESOLUTION_CONFIG, (uint8_t *)&presetResolutionConfig, sizeof(presetResolutionConfig))); } auto sensor_list = device_->getSensorList(); for (size_t i = 0; i < sensor_list->getCount(); i++) { auto sensor = sensor_list->getSensor(i); auto profiles = sensor->getStreamProfileList(); for (size_t j = 0; j < profiles->getCount(); j++) { auto profile = profiles->getProfile(j); stream_index_pair sip{profile->getType(), 0}; if (sensors_.find(sip) != sensors_.end()) { continue; } sensors_[sip] = sensor; } } for (const auto &[stream_index, enable] : enable_stream_) { if (enable && sensors_.find(stream_index) == sensors_.end()) { RCLCPP_DEBUG_STREAM(logger_, magic_enum::enum_name(stream_index.first) << " sensor not supported by current device, skipping"); enable_stream_[stream_index] = false; } } auto device_info = device_->getDeviceInfo(); CHECK_NOTNULL(device_info); if (is_playback_device_) { // Everything below this point only tunes real-hardware-only behavior // (heartbeat, USB3 retry, laser, depth limits, multi-device sync, PTP, // etc.). Those SDK device components aren't registered on a playback // device, so querying them throws instead of returning "not supported". // None of it is needed to correctly publish a recorded .bag file. return; } auto should_apply_launch_config = [this](const std::string ¶m_name) { return isLaunchParamProvided(param_name); }; if (should_apply_launch_config("retry_on_usb3_detection_failure") && retry_on_usb3_detection_failure_ && device_->isPropertySupported(OB_PROP_DEVICE_USB3_REPEAT_IDENTIFY_BOOL, OB_PERMISSION_READ_WRITE)) { TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_DEVICE_USB3_REPEAT_IDENTIFY_BOOL, retry_on_usb3_detection_failure_); } if (sync_io_voltage_level_ != -1 && device_->isPropertySupported(OB_PROP_USB_SYNC_VOLTAGE_LEVEL_INT, OB_PERMISSION_READ_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_USB_SYNC_VOLTAGE_LEVEL_INT); if (sync_io_voltage_level_ < range.min || sync_io_voltage_level_ > range.max) { RCLCPP_ERROR_STREAM( logger_, "sync IO voltage level is out of range " << range.min << " - " << range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_USB_SYNC_VOLTAGE_LEVEL_INT, sync_io_voltage_level_); RCLCPP_INFO_STREAM(logger_, "Current sync IO voltage level: " << device_->getIntProperty( OB_PROP_USB_SYNC_VOLTAGE_LEVEL_INT)); } } if (should_apply_launch_config("enable_heartbeat") && device_->isPropertySupported(OB_PROP_HEARTBEAT_BOOL, OB_PERMISSION_READ_WRITE)) { TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_HEARTBEAT_BOOL, enable_heartbeat_); RCLCPP_INFO_STREAM( logger_, "Current heartbeat: " << (device_->getBoolProperty(OB_PROP_HEARTBEAT_BOOL) ? "ON" : "OFF")); } if (should_apply_launch_config("enable_fps_boost") && device_->isPropertySupported(OB_PROP_FPS_BOOST_BOOL, OB_PERMISSION_READ_WRITE)) { TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_FPS_BOOST_BOOL, enable_fps_boost_); RCLCPP_INFO_STREAM( logger_, "Current fps boost: " << (device_->getBoolProperty(OB_PROP_FPS_BOOST_BOOL) ? "ON" : "OFF")); } if (should_apply_launch_config("enable_firmware_log")) { device_->enableFirmwareLog(enable_firmware_log_); RCLCPP_INFO_STREAM(logger_, "Set firmware log to " << (enable_firmware_log_ ? "ON" : "OFF")); } if (max_depth_limit_ > 0 && device_->isPropertySupported(OB_PROP_MAX_DEPTH_INT, OB_PERMISSION_READ_WRITE)) { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_MAX_DEPTH_INT, max_depth_limit_); RCLCPP_INFO_STREAM( logger_, "Current max depth limit: " << device_->getIntProperty(OB_PROP_MAX_DEPTH_INT)); } if (min_depth_limit_ > 0 && device_->isPropertySupported(OB_PROP_MIN_DEPTH_INT, OB_PERMISSION_READ_WRITE)) { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_MIN_DEPTH_INT, min_depth_limit_); RCLCPP_INFO_STREAM( logger_, "Current min depth limit: " << device_->getIntProperty(OB_PROP_MIN_DEPTH_INT)); } if (laser_energy_level_ != -1 && device_->isPropertySupported(OB_PROP_LASER_ENERGY_LEVEL_INT, OB_PERMISSION_READ_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_LASER_ENERGY_LEVEL_INT); if (laser_energy_level_ < range.min || laser_energy_level_ > range.max) { RCLCPP_ERROR_STREAM(logger_, "Laser energy level is out of range " << range.min << " - " << range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_LASER_ENERGY_LEVEL_INT, laser_energy_level_); auto new_laser_energy_level = device_->getIntProperty(OB_PROP_LASER_ENERGY_LEVEL_INT); RCLCPP_INFO_STREAM(logger_, "Current energy level: " << new_laser_energy_level); } } if (depth_registration_ && align_mode_ == "SW") { RCLCPP_DEBUG_STREAM(logger_, "Create align filter"); align_filter_ = std::make_unique(align_target_stream_); RCLCPP_INFO_STREAM(logger_, "set align mode to " << align_mode_); } if (should_apply_launch_config("disparity_to_depth_mode") && !disparity_to_depth_mode_.empty() && sensors_.find(DEPTH) != sensors_.end() && device_->isPropertySupported(OB_PROP_DISPARITY_TO_DEPTH_BOOL, OB_PERMISSION_READ_WRITE) && device_->isPropertySupported(OB_PROP_SDK_DISPARITY_TO_DEPTH_BOOL, OB_PERMISSION_READ_WRITE)) { if (disparity_to_depth_mode_ == "HW") { device_->setBoolProperty(OB_PROP_DISPARITY_TO_DEPTH_BOOL, 1); device_->setBoolProperty(OB_PROP_SDK_DISPARITY_TO_DEPTH_BOOL, 0); RCLCPP_INFO_STREAM(logger_, "Disparity to depth mode: HW"); } else if (disparity_to_depth_mode_ == "SW") { device_->setBoolProperty(OB_PROP_DISPARITY_TO_DEPTH_BOOL, 0); device_->setBoolProperty(OB_PROP_SDK_DISPARITY_TO_DEPTH_BOOL, 1); RCLCPP_INFO_STREAM(logger_, "Disparity to depth mode: SW"); } else if (disparity_to_depth_mode_ == "disable") { device_->setBoolProperty(OB_PROP_DISPARITY_TO_DEPTH_BOOL, 0); device_->setBoolProperty(OB_PROP_SDK_DISPARITY_TO_DEPTH_BOOL, 0); RCLCPP_INFO_STREAM(logger_, "Disparity to depth mode: disabled"); } else { RCLCPP_WARN_STREAM(logger_, "Unknown disparity to depth mode '" << disparity_to_depth_mode_ << "', keeping default settings"); } } try { if (should_apply_launch_config("enable_ldp") && device_->isPropertySupported(OB_PROP_LDP_BOOL, OB_PERMISSION_READ_WRITE)) { if (device_->isPropertySupported(OB_PROP_LASER_CONTROL_INT, OB_PERMISSION_READ_WRITE)) { auto laser_enable = device_->getIntProperty(OB_PROP_LASER_CONTROL_INT); device_->setBoolProperty(OB_PROP_LDP_BOOL, enable_ldp_); device_->setIntProperty(OB_PROP_LASER_CONTROL_INT, laser_enable); } else if (device_->isPropertySupported(OB_PROP_LASER_BOOL, OB_PERMISSION_READ_WRITE)) { if (!enable_ldp_) { auto laser_enable = device_->getIntProperty(OB_PROP_LASER_BOOL); device_->setBoolProperty(OB_PROP_LDP_BOOL, enable_ldp_); std::this_thread::sleep_for(std::chrono::milliseconds(3)); device_->setIntProperty(OB_PROP_LASER_BOOL, laser_enable); } else { device_->setBoolProperty(OB_PROP_LDP_BOOL, enable_ldp_); } } RCLCPP_INFO_STREAM( logger_, "Current LDP: " << (device_->getBoolProperty(OB_PROP_LDP_BOOL) ? "ON" : "OFF")); } } catch (const ob::Error &e) { RCLCPP_WARN_STREAM(logger_, "Skipping LDP configuration: " << orbbec_camera::formatObErrorWithStatus(e)); } catch (const std::exception &e) { RCLCPP_WARN_STREAM(logger_, "Skipping LDP configuration: " << e.what()); } if (ldp_power_level_ != -1 && device_->isPropertySupported(OB_PROP_LASER_POWER_LEVEL_CONTROL_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_LASER_POWER_LEVEL_CONTROL_INT); if (ldp_power_level_ < range.min || ldp_power_level_ > range.max) { RCLCPP_ERROR(logger_, "ldp power level value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_LASER_POWER_LEVEL_CONTROL_INT, ldp_power_level_); RCLCPP_INFO_STREAM(logger_, "Current lrm power level: " << device_->getIntProperty( OB_PROP_LASER_POWER_LEVEL_CONTROL_INT)); } } if (should_apply_launch_config("enable_laser") && device_->isPropertySupported(OB_PROP_LASER_CONTROL_INT, OB_PERMISSION_READ_WRITE)) { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_LASER_CONTROL_INT, enable_laser_); RCLCPP_INFO_STREAM(logger_, "Current G300 laser control: " << (device_->getIntProperty(OB_PROP_LASER_CONTROL_INT) ? "ON" : "OFF")); } if (should_apply_launch_config("enable_laser") && device_->isPropertySupported(OB_PROP_LASER_BOOL, OB_PERMISSION_READ_WRITE)) { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_LASER_BOOL, enable_laser_); RCLCPP_INFO_STREAM( logger_, "Current laser control: " << (device_->getIntProperty(OB_PROP_LASER_BOOL) ? "ON" : "OFF")); } if (!sync_mode_str_.empty()) { auto sync_config = device_->getMultiDeviceSyncConfig(); std::transform(sync_mode_str_.begin(), sync_mode_str_.end(), sync_mode_str_.begin(), ::toupper); sync_mode_ = OBSyncModeFromString(sync_mode_str_); sync_config.syncMode = sync_mode_; sync_config.depthDelayUs = depth_delay_us_; sync_config.colorDelayUs = color_delay_us_; sync_config.trigger2ImageDelayUs = trigger2image_delay_us_; sync_config.triggerOutDelayUs = trigger_out_delay_us_; sync_config.triggerOutEnable = trigger_out_enabled_; sync_config.framesPerTrigger = frames_per_trigger_; TRY_EXECUTE_BLOCK(device_->setMultiDeviceSyncConfig(sync_config)); sync_config = device_->getMultiDeviceSyncConfig(); RCLCPP_INFO_STREAM(logger_, "Current sync mode: " << magic_enum::enum_name(sync_config.syncMode)); if (sync_mode_ == OB_MULTI_DEVICE_SYNC_MODE_SOFTWARE_TRIGGERING) { RCLCPP_INFO_STREAM(logger_, "Frames per trigger: " << sync_config.framesPerTrigger); RCLCPP_INFO_STREAM(logger_, "Software trigger period " << software_trigger_period_.count() << " ms"); software_trigger_timer_ = node_->create_wall_timer(software_trigger_period_, [this]() { if (software_trigger_enabled_) { TRY_EXECUTE_BLOCK(device_->triggerCapture()); } }); } } if (should_apply_launch_config("enable_ptp_config") && device_->isPropertySupported(OB_DEVICE_PTP_CLOCK_SYNC_ENABLE_BOOL, OB_PERMISSION_READ_WRITE)) { device_->setBoolProperty(OB_DEVICE_PTP_CLOCK_SYNC_ENABLE_BOOL, enable_ptp_config_); RCLCPP_INFO_STREAM( logger_, "Current PTP Config: " << (device_->getBoolProperty(OB_DEVICE_PTP_CLOCK_SYNC_ENABLE_BOOL) ? "ON" : "OFF")); } if (device_->isPropertySupported(OB_PROP_DEPTH_PRECISION_LEVEL_INT, OB_PERMISSION_READ_WRITE) && !depth_precision_str_.empty()) { auto default_precision_level = device_->getIntProperty(OB_PROP_DEPTH_PRECISION_LEVEL_INT); if (default_precision_level != depth_precision_) { device_->setIntProperty(OB_PROP_DEPTH_PRECISION_LEVEL_INT, depth_precision_); const auto current_depth_precision = device_->getIntProperty(OB_PROP_DEPTH_PRECISION_LEVEL_INT); RCLCPP_INFO_STREAM(logger_, "Current depth precision: " << depthPrecisionLevelToString(current_depth_precision)); } } else if (device_->isPropertySupported(OB_PROP_DEPTH_UNIT_FLEXIBLE_ADJUSTMENT_FLOAT, OB_PERMISSION_READ_WRITE) && !depth_precision_str_.empty()) { auto depth_unit_flexible_adjustment = depthPrecisionFromString(depth_precision_str_); auto range = device_->getFloatPropertyRange(OB_PROP_DEPTH_UNIT_FLEXIBLE_ADJUSTMENT_FLOAT); RCLCPP_INFO_STREAM(logger_, "Depth unit flexible adjustment range: " << range.min << " - " << range.max); if (depth_unit_flexible_adjustment < range.min || depth_unit_flexible_adjustment > range.max) { RCLCPP_ERROR_STREAM( logger_, "depth unit flexible adjustment value is out of range, please check the value"); } else { TRY_TO_SET_PROPERTY(setFloatProperty, OB_PROP_DEPTH_UNIT_FLEXIBLE_ADJUSTMENT_FLOAT, depth_unit_flexible_adjustment); RCLCPP_INFO_STREAM( logger_, "Current depth unit: " << device_->getFloatProperty(OB_PROP_DEPTH_UNIT_FLEXIBLE_ADJUSTMENT_FLOAT) << "mm"); } } for (const auto &stream_index : IMAGE_STREAMS) { if (enable_stream_[stream_index]) { OBPropertyID mirrorPropertyID = OB_PROP_DEPTH_MIRROR_BOOL; if (stream_index == COLOR) { mirrorPropertyID = OB_PROP_COLOR_MIRROR_BOOL; } else if (stream_index == DEPTH) { mirrorPropertyID = OB_PROP_DEPTH_MIRROR_BOOL; } else if (stream_index == INFRA0) { mirrorPropertyID = OB_PROP_IR_MIRROR_BOOL; } else if (stream_index == INFRA1) { mirrorPropertyID = OB_PROP_IR_MIRROR_BOOL; } else if (stream_index == INFRA2) { mirrorPropertyID = OB_PROP_IR_RIGHT_MIRROR_BOOL; } else if (stream_index == COLOR_LEFT) { mirrorPropertyID = OB_PROP_COLOR_LEFT_MIRROR_BOOL; } else if (stream_index == COLOR_RIGHT) { mirrorPropertyID = OB_PROP_COLOR_RIGHT_MIRROR_BOOL; } if (should_apply_launch_config(stream_name_[stream_index] + "_mirror") && device_->isPropertySupported(mirrorPropertyID, OB_PERMISSION_WRITE)) { TRY_TO_SET_PROPERTY(setBoolProperty, mirrorPropertyID, mirror_stream_[stream_index]); RCLCPP_INFO_STREAM( logger_, "Current " << stream_name_[stream_index] << " mirror: " << (device_->getBoolProperty(mirrorPropertyID) ? "ON" : "OFF")); } OBPropertyID flipPropertyID = OB_PROP_DEPTH_FLIP_BOOL; if (stream_index == COLOR) { flipPropertyID = OB_PROP_COLOR_FLIP_BOOL; } else if (stream_index == DEPTH) { flipPropertyID = OB_PROP_DEPTH_FLIP_BOOL; } else if (stream_index == INFRA0) { flipPropertyID = OB_PROP_IR_FLIP_BOOL; } else if (stream_index == INFRA1) { flipPropertyID = OB_PROP_IR_FLIP_BOOL; } else if (stream_index == INFRA2) { flipPropertyID = OB_PROP_IR_RIGHT_FLIP_BOOL; } else if (stream_index == COLOR_LEFT) { flipPropertyID = OB_PROP_COLOR_LEFT_FLIP_BOOL; } else if (stream_index == COLOR_RIGHT) { flipPropertyID = OB_PROP_COLOR_RIGHT_FLIP_BOOL; } if (should_apply_launch_config(stream_name_[stream_index] + "_flip") && device_->isPropertySupported(flipPropertyID, OB_PERMISSION_WRITE)) { TRY_TO_SET_PROPERTY(setBoolProperty, flipPropertyID, flip_stream_[stream_index]); RCLCPP_INFO_STREAM(logger_, "Current " << stream_name_[stream_index] << " flip: " << (device_->getBoolProperty(flipPropertyID) ? "ON" : "OFF")); } OBPropertyID rotationPropertyID = OB_PROP_DEPTH_ROTATE_INT; if (stream_index == COLOR) { rotationPropertyID = OB_PROP_COLOR_ROTATE_INT; } else if (stream_index == DEPTH) { rotationPropertyID = OB_PROP_DEPTH_ROTATE_INT; } else if (stream_index == INFRA0) { rotationPropertyID = OB_PROP_IR_ROTATE_INT; } else if (stream_index == INFRA1) { rotationPropertyID = OB_PROP_IR_ROTATE_INT; } else if (stream_index == INFRA2) { rotationPropertyID = OB_PROP_IR_RIGHT_ROTATE_INT; } else if (stream_index == COLOR_LEFT) { rotationPropertyID = OB_PROP_COLOR_LEFT_ROTATE_INT; } else if (stream_index == COLOR_RIGHT) { rotationPropertyID = OB_PROP_COLOR_RIGHT_ROTATE_INT; } if (rotation_stream_[stream_index] != -1 && device_->isPropertySupported(rotationPropertyID, OB_PERMISSION_WRITE)) { TRY_TO_SET_PROPERTY(setIntProperty, rotationPropertyID, rotation_stream_[stream_index]); RCLCPP_INFO_STREAM(logger_, "Current " << stream_name_[stream_index] << " rotation: " << device_->getIntProperty(rotationPropertyID)); } } } if (should_apply_launch_config("enable_color_auto_white_balance") && device_->isPropertySupported(OB_PROP_COLOR_AUTO_WHITE_BALANCE_BOOL, OB_PERMISSION_WRITE)) { TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_COLOR_AUTO_WHITE_BALANCE_BOOL, enable_color_auto_white_balance_); RCLCPP_INFO_STREAM( logger_, "Current color auto white balance: " << (device_->getBoolProperty(OB_PROP_COLOR_AUTO_WHITE_BALANCE_BOOL) ? "ON" : "OFF")); } if (should_apply_launch_config("color_preset") && !color_preset_.empty()) { try { if (!device_->isColorPresetSupported()) { RCLCPP_WARN_STREAM(logger_, "Color preset is not supported by this device"); } else { auto color_preset_list = device_->getColorPresetList(); std::string selected_preset; std::ostringstream supported_presets; bool has_supported_preset = false; const uint32_t preset_count = color_preset_list ? color_preset_list->getCount() : 0; for (uint32_t i = 0; i < preset_count; ++i) { const char *preset_name = color_preset_list->getName(i); if (preset_name == nullptr || preset_name[0] == '\0') { continue; } if (has_supported_preset) { supported_presets << ", "; } supported_presets << preset_name; has_supported_preset = true; if (equalsIgnoreCase(color_preset_, preset_name)) { selected_preset = preset_name; } } if (selected_preset.empty()) { RCLCPP_WARN_STREAM(logger_, "Unsupported color_preset: " << color_preset_ << ". Supported values: " << supported_presets.str()); } else { device_->switchColorPreset(selected_preset.c_str()); const char *current_preset = device_->getCurrentColorPresetName(); color_preset_ = current_preset != nullptr ? current_preset : selected_preset; RCLCPP_INFO_STREAM(logger_, "Current color preset: " << color_preset_); } } } catch (const ob::Error &e) { RCLCPP_WARN_STREAM( logger_, "Failed to set color preset: " << orbbec_camera::formatObErrorWithStatus(e)); } catch (const std::exception &e) { RCLCPP_WARN_STREAM(logger_, "Failed to set color preset: " << e.what()); } } if (color_exposure_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_EXPOSURE_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_COLOR_EXPOSURE_INT); if (color_exposure_ < range.min || color_exposure_ > range.max) { RCLCPP_ERROR(logger_, "color exposure value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_EXPOSURE_INT, color_exposure_); RCLCPP_INFO_STREAM(logger_, "Current color exposure: " << device_->getIntProperty(OB_PROP_COLOR_EXPOSURE_INT)); } } if (color_gain_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_GAIN_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_COLOR_GAIN_INT); if (color_gain_ < range.min || color_gain_ > range.max) { RCLCPP_ERROR(logger_, "color gain value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_GAIN_INT, color_gain_); RCLCPP_INFO_STREAM(logger_, "Current color gain: " << device_->getIntProperty(OB_PROP_COLOR_GAIN_INT)); } } if (color_mjpeg_quality_ != -1) { if (!device_->isPropertySupported(OB_PROP_MJPEG_QUALITY_INT, OB_PERMISSION_WRITE)) { RCLCPP_WARN_STREAM(logger_, "color_mjpeg_quality is not supported by this device"); } else { auto range = device_->getIntPropertyRange(OB_PROP_MJPEG_QUALITY_INT); if (color_mjpeg_quality_ < range.min || color_mjpeg_quality_ > range.max) { RCLCPP_ERROR(logger_, "color MJPEG quality value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_MJPEG_QUALITY_INT, color_mjpeg_quality_); RCLCPP_INFO_STREAM(logger_, "Current color MJPEG quality: " << device_->getIntProperty(OB_PROP_MJPEG_QUALITY_INT)); } } } if (should_apply_launch_config("enable_color_auto_exposure_priority") && device_->isPropertySupported(OB_PROP_COLOR_AUTO_EXPOSURE_PRIORITY_INT, OB_PERMISSION_WRITE)) { int set_enable_color_auto_exposure_priority = enable_color_auto_exposure_priority_ ? 1 : 0; TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_AUTO_EXPOSURE_PRIORITY_INT, set_enable_color_auto_exposure_priority); RCLCPP_INFO_STREAM( logger_, "Current color auto exposure priority: " << (device_->getIntProperty(OB_PROP_COLOR_AUTO_EXPOSURE_PRIORITY_INT) ? "ON" : "OFF")); } if (should_apply_launch_config("enable_color_auto_exposure") && device_->isPropertySupported(OB_PROP_COLOR_AUTO_EXPOSURE_BOOL, OB_PERMISSION_WRITE)) { TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_COLOR_AUTO_EXPOSURE_BOOL, enable_color_auto_exposure_); RCLCPP_INFO_STREAM( logger_, "Current color auto exposure: " << (device_->getBoolProperty(OB_PROP_COLOR_AUTO_EXPOSURE_BOOL) ? "ON" : "OFF")); } if (color_white_balance_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_WHITE_BALANCE_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_COLOR_WHITE_BALANCE_INT); if (color_white_balance_ < range.min || color_white_balance_ > range.max) { RCLCPP_ERROR(logger_, "color white balance value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_WHITE_BALANCE_INT, color_white_balance_); RCLCPP_INFO_STREAM(logger_, "Current color white balance: " << device_->getIntProperty(OB_PROP_COLOR_WHITE_BALANCE_INT)); } } if (color_ae_max_exposure_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_AE_MAX_EXPOSURE_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_COLOR_AE_MAX_EXPOSURE_INT); if (color_ae_max_exposure_ < range.min || color_ae_max_exposure_ > range.max) { RCLCPP_ERROR(logger_, "color AE max exposure value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_AE_MAX_EXPOSURE_INT, color_ae_max_exposure_); RCLCPP_INFO_STREAM(logger_, "Current color AE max exposure: " << device_->getIntProperty( OB_PROP_COLOR_AE_MAX_EXPOSURE_INT)); } } if (color_ae_max_gain_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_AE_MAX_GAIN_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_COLOR_AE_MAX_GAIN_INT); if (color_ae_max_gain_ < range.min || color_ae_max_gain_ > range.max) { RCLCPP_ERROR(logger_, "color AE max gain value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_AE_MAX_GAIN_INT, color_ae_max_gain_); RCLCPP_INFO_STREAM(logger_, "Current color AE max gain: " << device_->getIntProperty(OB_PROP_COLOR_AE_MAX_GAIN_INT)); } } if (color_brightness_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_BRIGHTNESS_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_COLOR_BRIGHTNESS_INT); if (color_brightness_ < range.min || color_brightness_ > range.max) { RCLCPP_ERROR(logger_, "color brightness value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_BRIGHTNESS_INT, color_brightness_); RCLCPP_INFO_STREAM(logger_, "Current color brightness: " << device_->getIntProperty(OB_PROP_COLOR_BRIGHTNESS_INT)); } } if (color_roi_brightness_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_ROI_BRIGHTNESS_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_COLOR_ROI_BRIGHTNESS_INT); if (color_roi_brightness_ < range.min || color_roi_brightness_ > range.max) { RCLCPP_ERROR(logger_, "color roi brightness value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_ROI_BRIGHTNESS_INT, color_roi_brightness_); RCLCPP_INFO_STREAM(logger_, "Current color roi brightness: " << device_->getIntProperty(OB_PROP_COLOR_ROI_BRIGHTNESS_INT)); } } if (color_sharpness_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_SHARPNESS_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_COLOR_SHARPNESS_INT); if (color_sharpness_ < range.min || color_sharpness_ > range.max) { RCLCPP_ERROR(logger_, "color sharpness value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_SHARPNESS_INT, color_sharpness_); RCLCPP_INFO_STREAM(logger_, "Current color sharpness: " << device_->getIntProperty(OB_PROP_COLOR_SHARPNESS_INT)); } } if (color_gamma_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_GAMMA_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_COLOR_GAMMA_INT); if (color_gamma_ < range.min || color_gamma_ > range.max) { RCLCPP_ERROR(logger_, "color gamm value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_GAMMA_INT, color_gamma_); RCLCPP_INFO_STREAM( logger_, "Current color gamma: " << device_->getIntProperty(OB_PROP_COLOR_GAMMA_INT)); } } if (color_saturation_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_SATURATION_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_COLOR_SATURATION_INT); if (color_saturation_ < range.min || color_saturation_ > range.max) { RCLCPP_ERROR(logger_, "color saturation value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_SATURATION_INT, color_saturation_); RCLCPP_INFO_STREAM(logger_, "Current color saturation: " << device_->getIntProperty(OB_PROP_COLOR_SATURATION_INT)); } } if (color_contrast_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_CONTRAST_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_COLOR_CONTRAST_INT); if (color_contrast_ < range.min || color_contrast_ > range.max) { RCLCPP_ERROR(logger_, "color contrast value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_CONTRAST_INT, color_contrast_); RCLCPP_INFO_STREAM(logger_, "Current color contrast: " << device_->getIntProperty(OB_PROP_COLOR_CONTRAST_INT)); } } if (color_hue_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_HUE_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_COLOR_HUE_INT); if (color_hue_ < range.min || color_hue_ > range.max) { RCLCPP_ERROR(logger_, "color hue value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_HUE_INT, color_hue_); RCLCPP_INFO_STREAM(logger_, "Current color hue: " << device_->getIntProperty(OB_PROP_COLOR_HUE_INT)); } } if (color_backlight_compensation_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_BACKLIGHT_COMPENSATION_INT, OB_PERMISSION_WRITE)) { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_BACKLIGHT_COMPENSATION_INT, color_backlight_compensation_); RCLCPP_INFO_STREAM(logger_, "Current color backlight compensation: " << device_->getIntProperty( OB_PROP_COLOR_BACKLIGHT_COMPENSATION_INT)); } if (color_denoising_level_ != -1 && device_->isPropertySupported(OB_PROP_COLOR_DENOISING_LEVEL_INT, OB_PERMISSION_WRITE)) { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_DENOISING_LEVEL_INT, color_denoising_level_); RCLCPP_INFO_STREAM(logger_, "Current color denoising level: " << device_->getIntProperty(OB_PROP_COLOR_DENOISING_LEVEL_INT)); } if (should_apply_launch_config("color_anti_flicker") && device_->isPropertySupported(OB_PROP_COLOR_ANTI_FLICKER_BOOL, OB_PERMISSION_WRITE)) { TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_COLOR_ANTI_FLICKER_BOOL, color_anti_flicker_); RCLCPP_INFO_STREAM( logger_, "Current color anti-flicker to " << (device_->getBoolProperty(OB_PROP_COLOR_ANTI_FLICKER_BOOL) ? "ON" : "OFF")); } if (!color_powerline_freq_.empty() && device_->isPropertySupported(OB_PROP_COLOR_POWER_LINE_FREQUENCY_INT, OB_PERMISSION_WRITE)) { if (color_powerline_freq_ == "disable") { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_POWER_LINE_FREQUENCY_INT, 0); } else if (color_powerline_freq_ == "50hz") { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_POWER_LINE_FREQUENCY_INT, 1); } else if (color_powerline_freq_ == "60hz") { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_POWER_LINE_FREQUENCY_INT, 2); } else if (color_powerline_freq_ == "auto") { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_COLOR_POWER_LINE_FREQUENCY_INT, 3); } const auto current_color_powerline_freq = device_->getIntProperty(OB_PROP_COLOR_POWER_LINE_FREQUENCY_INT); RCLCPP_INFO_STREAM(logger_, "Current color powerline freq: " << colorPowerLineFrequencyToString( current_color_powerline_freq)); } if (depth_exposure_ != -1 && device_->isPropertySupported(OB_PROP_DEPTH_EXPOSURE_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_DEPTH_EXPOSURE_INT); if (depth_exposure_ < range.min || depth_exposure_ > range.max) { RCLCPP_ERROR(logger_, "depth exposure value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_DEPTH_EXPOSURE_INT, depth_exposure_); RCLCPP_INFO_STREAM(logger_, "Current depth exposure: " << device_->getIntProperty(OB_PROP_DEPTH_EXPOSURE_INT)); } } if (depth_gain_ != -1 && device_->isPropertySupported(OB_PROP_DEPTH_GAIN_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_DEPTH_GAIN_INT); if (depth_gain_ < range.min || depth_gain_ > range.max) { RCLCPP_ERROR(logger_, "depth gain value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_DEPTH_GAIN_INT, depth_gain_); RCLCPP_INFO_STREAM(logger_, "Current depth gain: " << device_->getIntProperty(OB_PROP_DEPTH_GAIN_INT)); } } if (should_apply_launch_config("enable_depth_auto_exposure_priority") && sensors_.find(DEPTH) != sensors_.end() && device_->isPropertySupported(OB_PROP_DEPTH_AUTO_EXPOSURE_PRIORITY_INT, OB_PERMISSION_WRITE)) { int set_enable_depth_auto_exposure_priority = enable_depth_auto_exposure_priority_ ? 1 : 0; TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_DEPTH_AUTO_EXPOSURE_PRIORITY_INT, set_enable_depth_auto_exposure_priority); RCLCPP_INFO_STREAM( logger_, "Current depth auto exposure priority: " << (device_->getIntProperty(OB_PROP_DEPTH_AUTO_EXPOSURE_PRIORITY_INT) ? "ON" : "OFF")); } if (should_apply_launch_config("enable_ir_auto_exposure") && device_->isPropertySupported(OB_PROP_IR_AUTO_EXPOSURE_BOOL, OB_PERMISSION_WRITE)) { TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_IR_AUTO_EXPOSURE_BOOL, enable_ir_auto_exposure_); RCLCPP_INFO_STREAM( logger_, "Current IR auto exposure: " << (device_->getBoolProperty(OB_PROP_IR_AUTO_EXPOSURE_BOOL) ? "ON" : "OFF")); } if (mean_intensity_set_point_ != -1 && device_->isPropertySupported(OB_PROP_IR_BRIGHTNESS_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_IR_BRIGHTNESS_INT); if (mean_intensity_set_point_ < range.min || mean_intensity_set_point_ > range.max) { RCLCPP_ERROR(logger_, "depth brightness value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_IR_BRIGHTNESS_INT, mean_intensity_set_point_); RCLCPP_INFO_STREAM(logger_, "Current depth brightness: " << device_->getIntProperty(OB_PROP_IR_BRIGHTNESS_INT)); } } // ir ae max if (ir_ae_max_exposure_ != -1 && device_->isPropertySupported(OB_PROP_IR_AE_MAX_EXPOSURE_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_IR_AE_MAX_EXPOSURE_INT); if (ir_ae_max_exposure_ < range.min || ir_ae_max_exposure_ > range.max) { RCLCPP_ERROR(logger_, "IR AE max exposure value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_IR_AE_MAX_EXPOSURE_INT, ir_ae_max_exposure_); RCLCPP_INFO_STREAM(logger_, "Current IR AE max exposure: " << device_->getIntProperty(OB_PROP_IR_AE_MAX_EXPOSURE_INT)); } } // ir brightness if (ir_brightness_ != -1 && device_->isPropertySupported(OB_PROP_IR_BRIGHTNESS_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_IR_BRIGHTNESS_INT); if (ir_brightness_ < range.min || ir_brightness_ > range.max) { RCLCPP_ERROR(logger_, "IR brightness value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_IR_BRIGHTNESS_INT, ir_brightness_); RCLCPP_INFO_STREAM( logger_, "Current IR brightness: " << device_->getIntProperty(OB_PROP_IR_BRIGHTNESS_INT)); } } if (ir_exposure_ != -1 && device_->isPropertySupported(OB_PROP_IR_EXPOSURE_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_IR_EXPOSURE_INT); if (ir_exposure_ < range.min || ir_exposure_ > range.max) { RCLCPP_ERROR(logger_, "ir exposure value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_IR_EXPOSURE_INT, ir_exposure_); RCLCPP_INFO_STREAM( logger_, "Current IR exposure: " << device_->getIntProperty(OB_PROP_IR_EXPOSURE_INT)); } } if (ir_gain_ != -1 && device_->isPropertySupported(OB_PROP_IR_GAIN_INT, OB_PERMISSION_WRITE)) { auto range = device_->getIntPropertyRange(OB_PROP_IR_GAIN_INT); if (ir_gain_ < range.min || ir_gain_ > range.max) { RCLCPP_ERROR(logger_, "ir gain value is out of range[%d,%d], please check the value", range.min, range.max); } else { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_IR_GAIN_INT, ir_gain_); RCLCPP_INFO_STREAM(logger_, "Current IR gain: " << device_->getIntProperty(OB_PROP_IR_GAIN_INT)); } } if (should_apply_launch_config("enable_ir_long_exposure") && device_->isPropertySupported(OB_PROP_IR_LONG_EXPOSURE_BOOL, OB_PERMISSION_WRITE)) { TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_IR_LONG_EXPOSURE_BOOL, enable_ir_long_exposure_); RCLCPP_INFO_STREAM( logger_, "Current IR long exposure: " << (device_->getBoolProperty(OB_PROP_IR_LONG_EXPOSURE_BOOL) ? "ON" : "OFF")); } if (should_apply_launch_config("enable_noise_removal_filter") && enable_noise_removal_filter_ && sensors_.find(DEPTH) != sensors_.end() && device_->isPropertySupported(OB_PROP_DEPTH_MAX_DIFF_INT, OB_PERMISSION_WRITE)) { auto default_noise_removal_filter_min_diff = device_->getIntProperty(OB_PROP_DEPTH_MAX_DIFF_INT); if (noise_removal_filter_min_diff_ != -1 && default_noise_removal_filter_min_diff != noise_removal_filter_min_diff_) { auto range = device_->getIntPropertyRange(OB_PROP_DEPTH_MAX_DIFF_INT); if (noise_removal_filter_min_diff_ < range.min || noise_removal_filter_min_diff_ > range.max) { RCLCPP_ERROR(logger_, "noise removal filter min diff value is out of range[%d,%d], please check " "the value", range.min, range.max); } else { device_->setIntProperty(OB_PROP_DEPTH_MAX_DIFF_INT, noise_removal_filter_min_diff_); } } RCLCPP_INFO_STREAM(logger_, "Current noise_removal_filter_min_diff: " << device_->getIntProperty(OB_PROP_DEPTH_MAX_DIFF_INT)); } if (should_apply_launch_config("enable_noise_removal_filter") && enable_noise_removal_filter_ && sensors_.find(DEPTH) != sensors_.end() && device_->isPropertySupported(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT, OB_PERMISSION_WRITE)) { auto default_noise_removal_filter_max_size = device_->getIntProperty(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT); if (noise_removal_filter_max_size_ != -1 && default_noise_removal_filter_max_size != noise_removal_filter_max_size_) { auto range = device_->getIntPropertyRange(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT); if (noise_removal_filter_max_size_ < range.min || noise_removal_filter_max_size_ > range.max) { RCLCPP_ERROR(logger_, "noise removal filter max size value is out of range[%d,%d], please check " "the value", range.min, range.max); } else { device_->setIntProperty(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT, noise_removal_filter_max_size_); } } RCLCPP_INFO_STREAM(logger_, "Current noise_removal_filter_max_size: " << device_->getIntProperty(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT)); } if (should_apply_launch_config("enable_noise_removal_filter") && sensors_.find(DEPTH) != sensors_.end() && device_->isPropertySupported(OB_PROP_DEPTH_SOFT_FILTER_BOOL, OB_PERMISSION_READ_WRITE)) { device_->setBoolProperty(OB_PROP_DEPTH_SOFT_FILTER_BOOL, enable_noise_removal_filter_); RCLCPP_INFO_STREAM(logger_, "Set noise removal filter to " << (enable_noise_removal_filter_ ? "true" : "false")); } if (should_apply_launch_config("enable_disp_outliers_filter") && sensors_.find(DEPTH) != sensors_.end() && device_->isPropertySupported(OB_PROP_DEPTH_OUTLIERS_FILTER_BOOL, OB_PERMISSION_READ_WRITE)) { device_->setBoolProperty(OB_PROP_DEPTH_OUTLIERS_FILTER_BOOL, enable_disp_outliers_filter_); RCLCPP_INFO_STREAM( logger_, "Set DispOutliersFilter to " << (enable_disp_outliers_filter_ ? "true" : "false")); } if (disp_outliers_filter_search_mode_ != -1 && sensors_.find(DEPTH) != sensors_.end() && device_->isPropertySupported(OB_PROP_DEPTH_OUTLIERS_FILTER_SEARCH_MODE_INT, OB_PERMISSION_READ_WRITE)) { device_->setIntProperty(OB_PROP_DEPTH_OUTLIERS_FILTER_SEARCH_MODE_INT, disp_outliers_filter_search_mode_); RCLCPP_INFO_STREAM( logger_, "Current DispOutliersFilter search mode: " << device_->getIntProperty(OB_PROP_DEPTH_OUTLIERS_FILTER_SEARCH_MODE_INT)); } if (disparity_range_mode_ != -1 && device_->isPropertySupported(OB_PROP_DISP_SEARCH_RANGE_MODE_INT, OB_PERMISSION_WRITE)) { if (disparity_range_mode_ == 64) { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_DISP_SEARCH_RANGE_MODE_INT, 0); } else if (disparity_range_mode_ == 128) { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_DISP_SEARCH_RANGE_MODE_INT, 1); } else if (disparity_range_mode_ == 256) { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_DISP_SEARCH_RANGE_MODE_INT, 2); } else { RCLCPP_ERROR(logger_, "disparity range mode does not support this setting"); } const auto current_disparity_range_mode = device_->getIntProperty(OB_PROP_DISP_SEARCH_RANGE_MODE_INT); RCLCPP_INFO_STREAM(logger_, "Current disparity range mode: " << disparityRangeModeToString(current_disparity_range_mode)); } if (should_apply_launch_config("enable_hardware_noise_removal_filter") && device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL, OB_PERMISSION_READ_WRITE)) { device_->setBoolProperty(OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL, enable_hardware_noise_removal_filter_); RCLCPP_INFO_STREAM( logger_, "Set hardware noise removal filter to " << (device_->getBoolProperty(OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL) ? "true" : "false")); if (device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT, OB_PERMISSION_READ_WRITE)) { if (hardware_noise_removal_filter_threshold_ != -1.0 && enable_hardware_noise_removal_filter_) { device_->setFloatProperty(OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT, hardware_noise_removal_filter_threshold_); RCLCPP_INFO_STREAM(logger_, "Current hardware noise removal filter threshold: " << device_->getFloatProperty( OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT)); } } } if (!exposure_range_mode_.empty() && exposure_range_mode_ != "default" && device_->isPropertySupported(OB_PROP_DEVICE_PERFORMANCE_MODE_INT, OB_PERMISSION_WRITE)) { if (exposure_range_mode_ == "ultimate") { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_DEVICE_PERFORMANCE_MODE_INT, 1); } else if (exposure_range_mode_ == "regular") { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_DEVICE_PERFORMANCE_MODE_INT, 0); } else { RCLCPP_ERROR(logger_, "exposure range mode does not support this setting"); } const auto current_exposure_range_mode = device_->getIntProperty(OB_PROP_DEVICE_PERFORMANCE_MODE_INT); RCLCPP_INFO_STREAM(logger_, "Current exposure range mode: " << exposureRangeModeToString(current_exposure_range_mode)); } if (should_apply_launch_config("enable_accel_data_correction") && device_->isPropertySupported(OB_PROP_SDK_ACCEL_FRAME_TRANSFORMED_BOOL, OB_PERMISSION_WRITE)) { TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_SDK_ACCEL_FRAME_TRANSFORMED_BOOL, enable_accel_data_correction_); RCLCPP_INFO_STREAM( logger_, "Current accel data correction: " << (device_->getBoolProperty(OB_PROP_SDK_ACCEL_FRAME_TRANSFORMED_BOOL) ? "ON" : "OFF")); } if (should_apply_launch_config("enable_gyro_data_correction") && device_->isPropertySupported(OB_PROP_SDK_GYRO_FRAME_TRANSFORMED_BOOL, OB_PERMISSION_WRITE)) { TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_SDK_GYRO_FRAME_TRANSFORMED_BOOL, enable_gyro_data_correction_); RCLCPP_INFO_STREAM( logger_, "Current gyro data correction: " << (device_->getBoolProperty(OB_PROP_SDK_GYRO_FRAME_TRANSFORMED_BOOL) ? "ON" : "OFF")); } if (isGemini335PID(pid_) && !intra_camera_sync_reference_.empty() && device_->isPropertySupported(OB_PROP_INTRA_CAMERA_SYNC_REFERENCE_INT, OB_PERMISSION_WRITE)) { if (intra_camera_sync_reference_ == "Start") { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_INTRA_CAMERA_SYNC_REFERENCE_INT, 0); } else if (intra_camera_sync_reference_ == "Middle") { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_INTRA_CAMERA_SYNC_REFERENCE_INT, 1); } else if (intra_camera_sync_reference_ == "End") { TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_INTRA_CAMERA_SYNC_REFERENCE_INT, 2); } else { RCLCPP_ERROR(logger_, "intra camera sync reference does not support this setting"); } const auto current_intra_camera_sync_reference = device_->getIntProperty(OB_PROP_INTRA_CAMERA_SYNC_REFERENCE_INT); RCLCPP_INFO_STREAM( logger_, "Current intra camera sync reference: " << intraCameraSyncReferenceToString(current_intra_camera_sync_reference)); } if (should_apply_launch_config("ae_strategy") && device_->isPropertySupported(OB_PROP_DEVICE_AE_STRATEGY_INT, OB_PERMISSION_WRITE)) { device_->setIntProperty(OB_PROP_DEVICE_AE_STRATEGY_INT, (ae_strategy_ == "motion" ? 0 : 1)); RCLCPP_INFO_STREAM( logger_, "Current Sports Mode: " << (device_->getIntProperty(OB_PROP_DEVICE_AE_STRATEGY_INT) == 0 ? "ON" : "OFF")); } if (should_apply_launch_config("ae_reference_stream") && (ae_reference_stream_ == "depth" || ae_reference_stream_ == "color") && device_->isPropertySupported(OB_PROP_DEVICE_AE_REFERENCE_INT, OB_PERMISSION_WRITE)) { if (device_->isPropertySupported(OB_PROP_DEVICE_AE_REFERENCE_INT, OB_PERMISSION_WRITE)) { auto ae_reference = ae_reference_stream_ == "depth" ? 0 : 1; device_->setIntProperty(OB_PROP_DEVICE_AE_REFERENCE_INT, ae_reference); auto current_ae_reference = device_->getIntProperty(OB_PROP_DEVICE_AE_REFERENCE_INT); RCLCPP_INFO_STREAM(logger_, "Current AE Reference: " << (current_ae_reference == 0 ? "depthbased" : "colorbased")); } } } bool OBCameraNode::exportConfigJsonToFile(const std::string &file_path, std::string &message) { if (file_path.empty()) { message = "Config json export file path is empty"; RCLCPP_ERROR_STREAM(logger_, message); return false; } try { const auto resolved_file_path = resolveConfigJsonFilePath(file_path); const auto parent_path = resolved_file_path.parent_path(); if (!parent_path.empty()) { std::filesystem::create_directories(parent_path); } const auto resolved_file_path_str = resolved_file_path.string(); syncApplicationSensorConfigForExport(); syncApplicationPointCloudConfigForExport(); syncApplicationHdrMergeConfigForExport(); device_->exportSettingsAsPresetJsonFile(resolved_file_path_str.c_str()); if (!std::filesystem::exists(resolved_file_path)) { message = "Failed to export config json file: file not found after export path=" + resolved_file_path_str; RCLCPP_ERROR_STREAM(logger_, message); return false; } message = "Exported config json file path: " + resolved_file_path_str; RCLCPP_INFO_STREAM(logger_, message); return true; } catch (const ob::Error &e) { message = "Failed to export config json file: " + orbbec_camera::formatObErrorWithStatus(e); RCLCPP_ERROR_STREAM(logger_, message); } catch (const std::exception &e) { message = std::string("Failed to export config json file: ") + e.what(); RCLCPP_ERROR_STREAM(logger_, message); } catch (...) { message = "Failed to export config json file"; RCLCPP_ERROR_STREAM(logger_, message); } return false; } void OBCameraNode::exportConfigJsonIfRequested() { if (export_config_json_file_path_.empty()) { return; } std::string message; exportConfigJsonToFile(export_config_json_file_path_, message); } bool OBCameraNode::isConfigJsonLoaded() const { return config_json_loaded_; } void OBCameraNode::syncApplicationSensorConfigForExport() { if (!device_) { return; } try { if (!ob::ApplicationConfig::isSupported(device_)) { RCLCPP_DEBUG_STREAM(logger_, "Skip exporting application_config sensors: unsupported device"); return; } auto application_config = ob::ApplicationConfig::get(device_); CHECK_NOTNULL(application_config.get()); for (const auto &sensor_config : application_config->sensors()) { if (!sensor_config || !sensor_config->streamProfile()) { continue; } const auto current_profile = sensor_config->streamProfile(); const stream_index_pair stream_index{current_profile->getType(), 0}; const auto stream_name_it = stream_name_.find(stream_index); if (stream_name_it == stream_name_.end()) { RCLCPP_DEBUG_STREAM(logger_, "Skip exporting application_config sensor: unsupported stream type=" << magic_enum::enum_name(current_profile->getType())); continue; } auto export_sensor_config = std::make_shared(sensor_config->sensorType()); const auto enable_stream_it = enable_stream_.find(stream_index); export_sensor_config->enableStream(enable_stream_it != enable_stream_.end() ? enable_stream_it->second : sensor_config->isStreamEnabled()); const auto stream_profile_it = stream_profile_.find(stream_index); export_sensor_config->setStreamProfile(stream_profile_it != stream_profile_.end() && stream_profile_it->second ? stream_profile_it->second : current_profile); const auto enable_undistortion_it = enable_undistortion_.find(stream_index); export_sensor_config->enableUndistortion(enable_undistortion_it != enable_undistortion_.end() ? enable_undistortion_it->second : sensor_config->isUndistortionEnabled()); application_config->setSensor(export_sensor_config); } } catch (const ob::Error &e) { RCLCPP_WARN_STREAM(logger_, "Export application_config sensors sync failed error=\"" << orbbec_camera::formatObErrorWithStatus(e) << "\""); } catch (const std::exception &e) { RCLCPP_WARN_STREAM( logger_, "Export application_config sensors sync failed error=\"" << e.what() << "\""); } catch (...) { RCLCPP_WARN_STREAM(logger_, "Export application_config sensors sync failed"); } } void OBCameraNode::syncApplicationPointCloudConfigForExport() { if (!device_) { return; } try { if (!ob::ApplicationConfig::isSupported(device_)) { RCLCPP_DEBUG_STREAM(logger_, "Skip exporting application_config point_cloud: unsupported device"); return; } auto application_config = ob::ApplicationConfig::get(device_); CHECK_NOTNULL(application_config.get()); auto point_cloud_config = std::make_shared(); point_cloud_config->enable(enable_point_cloud_ || enable_colored_point_cloud_); point_cloud_config->setFormat(enable_colored_point_cloud_ ? OB_FORMAT_RGB_POINT : OB_FORMAT_POINT); point_cloud_config->setDecimationFactor(std::max(1, point_cloud_decimation_filter_factor_)); auto align_mode = ALIGN_DISABLE; if (depth_registration_) { if (align_mode_ == "HW") { align_mode = ALIGN_D2C_HW_MODE; } else if (align_target_stream_ == OB_STREAM_DEPTH) { align_mode = ALIGN_C2D_SW_MODE; } else { align_mode = ALIGN_D2C_SW_MODE; } } point_cloud_config->setAlignMode(align_mode); point_cloud_config->enableFrameSync(enable_frame_sync_); point_cloud_config->setAllFrameTypeRequired(frame_aggregate_mode_ == "full_frame"); point_cloud_config->enableMatchTargetResolution( align_mode == ALIGN_D2C_HW_MODE ? enable_depth_scale_ : true); application_config->setPointCloud(point_cloud_config); } catch (const ob::Error &e) { RCLCPP_WARN_STREAM(logger_, "Export application_config point_cloud sync failed error=\"" << orbbec_camera::formatObErrorWithStatus(e) << "\""); } catch (const std::exception &e) { RCLCPP_WARN_STREAM( logger_, "Export application_config point_cloud sync failed error=\"" << e.what() << "\""); } catch (...) { RCLCPP_WARN_STREAM(logger_, "Export application_config point_cloud sync failed"); } } void OBCameraNode::syncApplicationHdrMergeConfigForExport() { if (!device_) { return; } try { if (!ob::ApplicationConfig::isSupported(device_)) { RCLCPP_DEBUG_STREAM(logger_, "Skip exporting application_config hdr_merge: unsupported device"); return; } auto application_config = ob::ApplicationConfig::get(device_); CHECK_NOTNULL(application_config.get()); auto hdr_merge_config = std::make_shared(); hdr_merge_config->enable(enable_hdr_merge_); hdr_merge_config->enableIR(true); application_config->setHDRMerge(hdr_merge_config); } catch (const ob::Error &e) { RCLCPP_WARN_STREAM(logger_, "Export application_config hdr_merge sync failed error=\"" << orbbec_camera::formatObErrorWithStatus(e) << "\""); } catch (const std::exception &e) { RCLCPP_WARN_STREAM( logger_, "Export application_config hdr_merge sync failed error=\"" << e.what() << "\""); } catch (...) { RCLCPP_WARN_STREAM(logger_, "Export application_config hdr_merge sync failed"); } } void OBCameraNode::loadConfigJson() { if (load_config_json_file_path_.empty()) { return; } const auto resolved_file_path = resolveConfigJsonFilePath(load_config_json_file_path_); const auto resolved_file_path_str = resolved_file_path.string(); std::ifstream load_config_file(resolved_file_path); if (!load_config_file.good()) { RCLCPP_WARN_STREAM(logger_, "Config JSON load skip file=" << resolved_file_path_str << " reason=file_not_found"); return; } try { device_->loadPresetFromJsonFile(resolved_file_path_str.c_str()); config_json_loaded_ = true; RCLCPP_INFO_STREAM(logger_, "Config JSON loaded file=" << resolved_file_path_str); } catch (const ob::Error &e) { config_json_loaded_ = false; RCLCPP_ERROR_STREAM(logger_, "Config JSON load failed file=" << resolved_file_path_str << " error=\"" << orbbec_camera::formatObErrorWithStatus(e) << "\""); } catch (const std::exception &e) { config_json_loaded_ = false; RCLCPP_ERROR_STREAM(logger_, "Config JSON load failed file=" << resolved_file_path_str << " error=\"" << e.what() << "\""); } catch (...) { config_json_loaded_ = false; RCLCPP_ERROR_STREAM(logger_, "Config JSON load failed file=" << resolved_file_path_str); } } void OBCameraNode::captureInitialRosParameters() { initial_ros_params_.clear(); const auto overrides = node_->get_node_parameters_interface()->get_parameter_overrides(); for (const auto ¶m : overrides) { initial_ros_params_.insert(param.first); } } bool OBCameraNode::isLaunchParamProvided(const std::string ¶m_name) const { if (param_name.empty()) { return false; } return initial_ros_params_.find(param_name) != initial_ros_params_.end(); } void OBCameraNode::syncConfigJsonApplicationConfig() { if (!config_json_loaded_ || !configJsonContainsApplicationConfig(load_config_json_file_path_, logger_)) { return; } try { if (!ob::ApplicationConfig::isSupported(device_)) { RCLCPP_WARN_STREAM(logger_, "Config JSON application_config is ignored because this device does not " "support SDK application config"); return; } auto application_config = ob::ApplicationConfig::get(device_); CHECK_NOTNULL(application_config.get()); for (const auto &sensor_config : application_config->sensors()) { if (!sensor_config || !sensor_config->streamProfile()) { continue; } auto profile = sensor_config->streamProfile(); const stream_index_pair stream_index{profile->getType(), 0}; auto stream_name_it = stream_name_.find(stream_index); if (stream_name_it == stream_name_.end()) { RCLCPP_DEBUG_STREAM(logger_, "Config JSON application_config skips unsupported stream type=" << profile->getType()); continue; } const auto &stream_name = stream_name_it->second; if (!isLaunchParamProvided("enable_" + stream_name)) { enable_stream_[stream_index] = sensor_config->isStreamEnabled(); } if (std::find(IMAGE_STREAMS.begin(), IMAGE_STREAMS.end(), stream_index) != IMAGE_STREAMS.end()) { auto video_profile = profile->as(); if (!isLaunchParamProvided(stream_name + "_width")) { width_[stream_index] = static_cast(video_profile->width()); } if (!isLaunchParamProvided(stream_name + "_height")) { height_[stream_index] = static_cast(video_profile->height()); } if (!isLaunchParamProvided(stream_name + "_fps")) { fps_[stream_index] = static_cast(video_profile->fps()); } if (!isLaunchParamProvided(stream_name + "_format")) { format_[stream_index] = video_profile->format(); format_str_[stream_index] = OBFormatToString(format_[stream_index]); } if (!isLaunchParamProvided("enable_" + stream_name + "_undistortion")) { enable_undistortion_[stream_index] = sensor_config->isUndistortionEnabled(); } } else if (stream_index == ACCEL) { auto accel_profile = profile->as(); if (!isLaunchParamProvided("accel_rate")) { imu_rate_[ACCEL] = sampleRateToString(accel_profile->sampleRate()); } if (!isLaunchParamProvided("accel_range")) { imu_range_[ACCEL] = fullAccelScaleRangeToString(accel_profile->fullScaleRange()); } } else if (stream_index == GYRO) { auto gyro_profile = profile->as(); if (!isLaunchParamProvided("gyro_rate")) { imu_rate_[GYRO] = sampleRateToString(gyro_profile->sampleRate()); } if (!isLaunchParamProvided("gyro_range")) { imu_range_[GYRO] = fullGyroScaleRangeToString(gyro_profile->fullScaleRange()); } } } auto point_cloud_config = application_config->pointCloud(); if (point_cloud_config) { const auto point_cloud_enabled = point_cloud_config->isEnabled(); const auto point_cloud_format = point_cloud_config->format(); if (!isLaunchParamProvided("enable_point_cloud")) { enable_point_cloud_ = point_cloud_enabled && point_cloud_format == OB_FORMAT_POINT; } if (!isLaunchParamProvided("enable_colored_point_cloud")) { enable_colored_point_cloud_ = point_cloud_enabled && point_cloud_format == OB_FORMAT_RGB_POINT; } if (!isLaunchParamProvided("point_cloud_decimation_filter_factor")) { point_cloud_decimation_filter_factor_ = point_cloud_config->decimationFactor(); } if (!isLaunchParamProvided("enable_frame_sync")) { enable_frame_sync_ = point_cloud_config->isFrameSyncEnabled(); } if (!isLaunchParamProvided("frame_aggregate_mode")) { frame_aggregate_mode_ = point_cloud_config->isAllFrameTypeRequired() ? "full_frame" : "ANY"; } const auto align_mode = point_cloud_config->alignMode(); if (!isLaunchParamProvided("depth_registration")) { depth_registration_ = align_mode != ALIGN_DISABLE; } if (!isLaunchParamProvided("align_mode")) { if (align_mode == ALIGN_D2C_HW_MODE) { align_mode_ = "HW"; } else if (align_mode == ALIGN_D2C_SW_MODE || align_mode == ALIGN_C2D_SW_MODE) { align_mode_ = "SW"; } } if (!isLaunchParamProvided("align_target_stream")) { if (align_mode == ALIGN_D2C_HW_MODE || align_mode == ALIGN_D2C_SW_MODE) { align_target_stream_ = OB_STREAM_COLOR; } else if (align_mode == ALIGN_C2D_SW_MODE) { align_target_stream_ = OB_STREAM_DEPTH; } } } auto hdr_merge_config = application_config->hdrMerge(); if (hdr_merge_config && !isLaunchParamProvided("enable_hdr_merge")) { enable_hdr_merge_ = hdr_merge_config->isEnabled(); } auto device_decimation_config = application_config->deviceDecimation(); if (device_decimation_config && device_decimation_config->isEnabled() && !isLaunchParamProvided("preset_resolution_config")) { const auto &config = device_decimation_config->presetResolutionConfig(); std::ostringstream ss; ss << config.width << "," << config.height << "," << config.irDecimationFactor << "," << config.depthDecimationFactor; preset_resolution_config_ = ss.str(); } RCLCPP_INFO_STREAM(logger_, "Config JSON application_config synced"); } catch (const ob::Error &e) { RCLCPP_WARN_STREAM(logger_, "Config JSON application_config sync failed error=\"" << orbbec_camera::formatObErrorWithStatus(e) << "\""); } catch (const std::exception &e) { RCLCPP_WARN_STREAM(logger_, "Config JSON application_config sync failed error=\"" << e.what() << "\""); } catch (...) { RCLCPP_WARN_STREAM(logger_, "Config JSON application_config sync failed"); } } void OBCameraNode::syncConfigJsonDeviceSettings() { if (!config_json_loaded_) { return; } auto can_read = [this](OBPropertyID property_id) { return device_->isPropertySupported(property_id, OB_PERMISSION_READ) || device_->isPropertySupported(property_id, OB_PERMISSION_READ_WRITE); }; auto can_write = [this](OBPropertyID property_id) { return device_->isPropertySupported(property_id, OB_PERMISSION_WRITE) || device_->isPropertySupported(property_id, OB_PERMISSION_READ_WRITE); }; auto log_readback = [this](const std::string &scope, const std::string &name, const auto &value) { std::ostringstream ss; ss << std::boolalpha << value; RCLCPP_INFO_STREAM(logger_, "Config final readback [" << scope << "] " << name << "=" << ss.str()); }; auto log_readback_fields = [this](const std::string &scope, const std::string &fields) { RCLCPP_INFO_STREAM(logger_, "Config final readback [" << scope << "] " << fields); }; auto sync_bool = [&](const char *scope, const char *param_name, bool &member, OBPropertyID property_id) { if (!can_read(property_id)) { return; } try { member = device_->getBoolProperty(property_id); log_readback(scope, param_name, member); } catch (const std::exception &e) { RCLCPP_DEBUG_STREAM(logger_, "Config final readback failed [" << scope << "] " << param_name << " error=\"" << e.what() << "\""); } }; auto sync_int = [&](const char *scope, const char *param_name, int &member, OBPropertyID property_id) { if (!can_read(property_id)) { return; } try { member = device_->getIntProperty(property_id); log_readback(scope, param_name, member); } catch (const std::exception &e) { RCLCPP_DEBUG_STREAM(logger_, "Config final readback failed [" << scope << "] " << param_name << " error=\"" << e.what() << "\""); } }; auto sync_float = [&](const char *scope, const char *param_name, float &member, OBPropertyID property_id) { if (!can_read(property_id)) { return; } try { member = device_->getFloatProperty(property_id); log_readback(scope, param_name, member); } catch (const std::exception &e) { RCLCPP_DEBUG_STREAM(logger_, "Config final readback failed [" << scope << "] " << param_name << " error=\"" << e.what() << "\""); } }; auto sync_stream_orientation = [&](const char *param_prefix, const stream_index_pair &stream_index, OBPropertyID flip_property_id, OBPropertyID mirror_property_id, OBPropertyID rotation_property_id) { if (can_read(flip_property_id)) { try { flip_stream_[stream_index] = device_->getBoolProperty(flip_property_id); log_readback(std::string(param_prefix) + ".orientation", "flip", flip_stream_[stream_index]); } catch (const std::exception &) { } } if (can_read(mirror_property_id)) { try { mirror_stream_[stream_index] = device_->getBoolProperty(mirror_property_id); log_readback(std::string(param_prefix) + ".orientation", "mirror", mirror_stream_[stream_index]); } catch (const std::exception &) { } } if (can_read(rotation_property_id)) { try { rotation_stream_[stream_index] = device_->getIntProperty(rotation_property_id); log_readback(std::string(param_prefix) + ".orientation", "rotation", rotation_stream_[stream_index]); } catch (const std::exception &) { } } }; sync_bool("device", "enable_heartbeat", enable_heartbeat_, OB_PROP_HEARTBEAT_BOOL); sync_bool("device", "retry_on_usb3_detection_failure", retry_on_usb3_detection_failure_, OB_PROP_DEVICE_USB3_REPEAT_IDENTIFY_BOOL); sync_bool("device", "enable_ptp_config", enable_ptp_config_, OB_DEVICE_PTP_CLOCK_SYNC_ENABLE_BOOL); try { device_preset_ = device_->getCurrentPresetName(); log_readback("depth", "device_preset", device_preset_); } catch (const std::exception &e) { RCLCPP_DEBUG_STREAM( logger_, "Config final readback failed [depth] device_preset error=\"" << e.what() << "\""); } if (can_read(OB_PROP_DEPTH_AUTO_EXPOSURE_PRIORITY_INT)) { try { enable_depth_auto_exposure_priority_ = device_->getIntProperty(OB_PROP_DEPTH_AUTO_EXPOSURE_PRIORITY_INT) != 0; log_readback("depth", "enable_depth_auto_exposure_priority", enable_depth_auto_exposure_priority_); } catch (const std::exception &) { } } if (can_read(OB_PROP_DEPTH_AUTO_EXPOSURE_BOOL)) { try { enable_ir_auto_exposure_ = device_->getBoolProperty(OB_PROP_DEPTH_AUTO_EXPOSURE_BOOL); log_readback("depth", "enable_ir_auto_exposure", enable_ir_auto_exposure_); } catch (const std::exception &) { } } sync_int("depth", "ir_ae_max_exposure", ir_ae_max_exposure_, OB_PROP_IR_AE_MAX_EXPOSURE_INT); sync_int("depth", "mean_intensity_set_point", mean_intensity_set_point_, OB_PROP_IR_BRIGHTNESS_INT); sync_int("depth", "depth_exposure", depth_exposure_, OB_PROP_DEPTH_EXPOSURE_INT); sync_int("depth", "ir_exposure", ir_exposure_, OB_PROP_IR_EXPOSURE_INT); sync_int("depth", "depth_gain", depth_gain_, OB_PROP_DEPTH_GAIN_INT); sync_int("depth", "ir_gain", ir_gain_, OB_PROP_IR_GAIN_INT); if (can_read(OB_PROP_DEPTH_UNIT_FLEXIBLE_ADJUSTMENT_FLOAT)) { try { const auto depth_unit = device_->getFloatProperty(OB_PROP_DEPTH_UNIT_FLEXIBLE_ADJUSTMENT_FLOAT); depth_precision_str_ = std::to_string(depth_unit) + "mm"; log_readback("depth", "depth_unit", depth_unit); log_readback("depth", "depth_precision", depth_precision_str_); } catch (const std::exception &) { } } if (can_read(OB_PROP_LASER_CONTROL_INT)) { try { enable_laser_ = device_->getIntProperty(OB_PROP_LASER_CONTROL_INT) != 0; log_readback("depth", "enable_laser", enable_laser_); } catch (const std::exception &) { } } else { sync_bool("depth", "enable_laser", enable_laser_, OB_PROP_LASER_BOOL); } sync_int("depth", "laser_energy_level", laser_energy_level_, OB_PROP_LASER_ENERGY_LEVEL_INT); sync_bool("depth", "enable_ldp", enable_ldp_, OB_PROP_LDP_BOOL); if (can_read(OB_STRUCT_DEPTH_AE_ROI)) { try { OBRegionOfInterest config{}; uint32_t data_size = sizeof(config); device_->getStructuredData(OB_STRUCT_DEPTH_AE_ROI, reinterpret_cast(&config), &data_size); depth_ae_roi_left_ = config.x0_left; depth_ae_roi_top_ = config.y0_top; depth_ae_roi_right_ = config.x1_right; depth_ae_roi_bottom_ = config.y1_bottom; std::ostringstream fields; fields << "left=" << depth_ae_roi_left_ << " top=" << depth_ae_roi_top_ << " right=" << depth_ae_roi_right_ << " bottom=" << depth_ae_roi_bottom_; log_readback_fields("depth.ae_roi", fields.str()); } catch (const std::exception &) { } } sync_bool("depth.interleave", "enable", interleave_frame_enable_, OB_PROP_FRAME_INTERLEAVE_ENABLE_BOOL); sync_int("depth.interleave", "skip_index", interleave_skip_index_, OB_PROP_FRAME_INTERLEAVE_CONFIG_INDEX_INT); try { const auto *frame_interleave_name = device_->getCurrentFrameInterleaveName(); if (frame_interleave_name != nullptr) { std::string mode(frame_interleave_name); std::string lower_mode = mode; std::transform(lower_mode.begin(), lower_mode.end(), lower_mode.begin(), ::tolower); if (lower_mode.find("laser") != std::string::npos) { interleave_ae_mode_ = "laser"; } else if (lower_mode.find("hdr") != std::string::npos) { interleave_ae_mode_ = "hdr"; } else { interleave_ae_mode_.clear(); } log_readback("depth.interleave", "ae_mode", interleave_ae_mode_); } } catch (const std::exception &) { } if (!interleave_ae_mode_.empty() && can_write(OB_PROP_FRAME_INTERLEAVE_CONFIG_INDEX_INT)) { int original_interleave_index = interleave_skip_index_; bool has_original_interleave_index = false; if (can_read(OB_PROP_FRAME_INTERLEAVE_CONFIG_INDEX_INT)) { try { original_interleave_index = device_->getIntProperty(OB_PROP_FRAME_INTERLEAVE_CONFIG_INDEX_INT); has_original_interleave_index = true; } catch (const std::exception &) { } } auto read_int_property = [&](OBPropertyID property_id, int &value) { if (!can_read(property_id)) { return; } try { value = device_->getIntProperty(property_id); } catch (const std::exception &) { } }; auto sync_interleave_param = [&](int config_index, int &laser_control, int &depth_exposure, int &depth_gain, int &ir_brightness, int &ir_ae_max_exposure) { try { device_->setIntProperty(OB_PROP_FRAME_INTERLEAVE_CONFIG_INDEX_INT, config_index); read_int_property(OB_PROP_LASER_CONTROL_INT, laser_control); read_int_property(OB_PROP_DEPTH_EXPOSURE_INT, depth_exposure); read_int_property(OB_PROP_DEPTH_GAIN_INT, depth_gain); read_int_property(OB_PROP_IR_BRIGHTNESS_INT, ir_brightness); read_int_property(OB_PROP_IR_AE_MAX_EXPOSURE_INT, ir_ae_max_exposure); std::ostringstream fields; fields << "laser_control=" << laser_control << " depth_exposure=" << depth_exposure << " depth_gain=" << depth_gain << " depth_brightness=" << ir_brightness << " depth_ae_max_exposure=" << ir_ae_max_exposure; log_readback_fields("depth.interleave.params." + std::to_string(config_index), fields.str()); } catch (const std::exception &e) { RCLCPP_DEBUG_STREAM(logger_, "Config final readback failed [depth.interleave.params." << config_index << "] error=\"" << e.what() << "\""); } }; if (interleave_ae_mode_ == "hdr") { sync_interleave_param(0, hdr_index0_laser_control_, hdr_index0_depth_exposure_, hdr_index0_depth_gain_, hdr_index0_ir_brightness_, hdr_index0_ir_ae_max_exposure_); sync_interleave_param(1, hdr_index1_laser_control_, hdr_index1_depth_exposure_, hdr_index1_depth_gain_, hdr_index1_ir_brightness_, hdr_index1_ir_ae_max_exposure_); } else if (interleave_ae_mode_ == "laser") { sync_interleave_param(0, laser_index0_laser_control_, laser_index0_depth_exposure_, laser_index0_depth_gain_, laser_index0_ir_brightness_, laser_index0_ir_ae_max_exposure_); sync_interleave_param(1, laser_index1_laser_control_, laser_index1_depth_exposure_, laser_index1_depth_gain_, laser_index1_ir_brightness_, laser_index1_ir_ae_max_exposure_); } if (has_original_interleave_index) { try { device_->setIntProperty(OB_PROP_FRAME_INTERLEAVE_CONFIG_INDEX_INT, original_interleave_index); } catch (const std::exception &e) { RCLCPP_DEBUG_STREAM(logger_, "Failed to restore frame_interleave.config_index: " << e.what()); } } } try { const bool hw = can_read(OB_PROP_DISPARITY_TO_DEPTH_BOOL) && device_->getBoolProperty(OB_PROP_DISPARITY_TO_DEPTH_BOOL); const bool sw = can_read(OB_PROP_SDK_DISPARITY_TO_DEPTH_BOOL) && device_->getBoolProperty(OB_PROP_SDK_DISPARITY_TO_DEPTH_BOOL); disparity_to_depth_mode_ = hw ? "HW" : (sw ? "SW" : "disable"); log_readback("depth", "disparity_to_depth_mode", disparity_to_depth_mode_); } catch (const std::exception &) { } if (can_read(OB_PROP_DISP_SEARCH_RANGE_MODE_INT)) { try { disparity_range_mode_ = std::stoi( disparityRangeModeToString(device_->getIntProperty(OB_PROP_DISP_SEARCH_RANGE_MODE_INT))); log_readback("depth", "disparity_range_mode", disparity_range_mode_); } catch (const std::exception &) { } } sync_int("depth", "disparity_search_offset", disparity_search_offset_, OB_PROP_DISP_SEARCH_OFFSET_INT); sync_bool("depth", "enable_hardware_noise_removal_filter", enable_hardware_noise_removal_filter_, OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL); sync_float("depth", "hardware_noise_removal_filter_threshold", hardware_noise_removal_filter_threshold_, OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT); sync_bool("depth", "enable_noise_removal_filter", enable_noise_removal_filter_, OB_PROP_DEPTH_SOFT_FILTER_BOOL); sync_int("depth", "noise_removal_filter_min_diff", noise_removal_filter_min_diff_, OB_PROP_DEPTH_MAX_DIFF_INT); sync_int("depth", "noise_removal_filter_max_size", noise_removal_filter_max_size_, OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT); sync_bool("depth", "enable_disp_outliers_filter", enable_disp_outliers_filter_, OB_PROP_DEPTH_OUTLIERS_FILTER_BOOL); sync_int("depth", "disp_outliers_filter_search_mode", disp_outliers_filter_search_mode_, OB_PROP_DEPTH_OUTLIERS_FILTER_SEARCH_MODE_INT); sync_stream_orientation("depth", DEPTH, OB_PROP_DEPTH_FLIP_BOOL, OB_PROP_DEPTH_MIRROR_BOOL, OB_PROP_DEPTH_ROTATE_INT); sync_stream_orientation("color", COLOR, OB_PROP_COLOR_FLIP_BOOL, OB_PROP_COLOR_MIRROR_BOOL, OB_PROP_COLOR_ROTATE_INT); sync_stream_orientation("left_ir", INFRA1, OB_PROP_IR_FLIP_BOOL, OB_PROP_IR_MIRROR_BOOL, OB_PROP_IR_ROTATE_INT); sync_stream_orientation("right_ir", INFRA2, OB_PROP_IR_RIGHT_FLIP_BOOL, OB_PROP_IR_RIGHT_MIRROR_BOOL, OB_PROP_IR_RIGHT_ROTATE_INT); if (can_read(OB_PROP_COLOR_AUTO_EXPOSURE_PRIORITY_INT)) { try { enable_color_auto_exposure_priority_ = device_->getIntProperty(OB_PROP_COLOR_AUTO_EXPOSURE_PRIORITY_INT) != 0; log_readback("color", "enable_color_auto_exposure_priority", enable_color_auto_exposure_priority_); } catch (const std::exception &) { } } sync_bool("color", "enable_color_auto_exposure", enable_color_auto_exposure_, OB_PROP_COLOR_AUTO_EXPOSURE_BOOL); sync_int("color", "color_denoising_level", color_denoising_level_, OB_PROP_COLOR_DENOISING_LEVEL_INT); sync_int("color", "color_ae_max_exposure", color_ae_max_exposure_, OB_PROP_COLOR_AE_MAX_EXPOSURE_INT); sync_int("color", "color_exposure", color_exposure_, OB_PROP_COLOR_EXPOSURE_INT); sync_int("color", "color_gain", color_gain_, OB_PROP_COLOR_GAIN_INT); sync_int("color", "color_mjpeg_quality", color_mjpeg_quality_, OB_PROP_MJPEG_QUALITY_INT); sync_int("color", "color_brightness", color_brightness_, OB_PROP_COLOR_BRIGHTNESS_INT); sync_bool("color", "enable_color_auto_white_balance", enable_color_auto_white_balance_, OB_PROP_COLOR_AUTO_WHITE_BALANCE_BOOL); sync_int("color", "color_white_balance", color_white_balance_, OB_PROP_COLOR_WHITE_BALANCE_INT); sync_int("color", "color_sharpness", color_sharpness_, OB_PROP_COLOR_SHARPNESS_INT); sync_int("color", "color_gamma", color_gamma_, OB_PROP_COLOR_GAMMA_INT); sync_int("color", "color_hue", color_hue_, OB_PROP_COLOR_HUE_INT); sync_int("color", "color_backlight_compensation", color_backlight_compensation_, OB_PROP_COLOR_BACKLIGHT_COMPENSATION_INT); sync_int("color", "color_contrast", color_contrast_, OB_PROP_COLOR_CONTRAST_INT); sync_int("color", "color_saturation", color_saturation_, OB_PROP_COLOR_SATURATION_INT); if (can_read(OB_PROP_COLOR_POWER_LINE_FREQUENCY_INT)) { try { color_powerline_freq_ = colorPowerLineFrequencyToString( device_->getIntProperty(OB_PROP_COLOR_POWER_LINE_FREQUENCY_INT)); log_readback("color", "color_powerline_freq", color_powerline_freq_); } catch (const std::exception &) { } } sync_bool("color", "color_anti_flicker", color_anti_flicker_, OB_PROP_COLOR_ANTI_FLICKER_BOOL); try { if (device_->isColorPresetSupported()) { const char *color_preset = device_->getCurrentColorPresetName(); if (color_preset != nullptr) { color_preset_ = color_preset; log_readback("color", "color_preset", color_preset_); } } } catch (const std::exception &) { } if (can_read(OB_STRUCT_COLOR_AE_ROI)) { try { OBRegionOfInterest config{}; uint32_t data_size = sizeof(config); device_->getStructuredData(OB_STRUCT_COLOR_AE_ROI, reinterpret_cast(&config), &data_size); color_ae_roi_left_ = config.x0_left; color_ae_roi_top_ = config.y0_top; color_ae_roi_right_ = config.x1_right; color_ae_roi_bottom_ = config.y1_bottom; std::ostringstream fields; fields << "left=" << color_ae_roi_left_ << " top=" << color_ae_roi_top_ << " right=" << color_ae_roi_right_ << " bottom=" << color_ae_roi_bottom_; log_readback_fields("color.ae_roi", fields.str()); } catch (const std::exception &) { } } } void OBCameraNode::syncConfigJsonFilterSettings( const std::vector> &filters, const std::string &sensor_name) { if (!config_json_loaded_) { return; } auto filter_scope = [&](const std::string &filter_name) { return "filter." + sensor_name + "." + normalizeDepthFilterName(filter_name); }; auto log_readback = [this](const std::string &scope, const std::string &name, const auto &value) { std::ostringstream ss; ss << std::boolalpha << value; RCLCPP_INFO_STREAM(logger_, "Config final readback [" << scope << "] " << name << "=" << ss.str()); }; auto log_readback_fields = [this](const std::string &scope, const std::string &fields) { RCLCPP_INFO_STREAM(logger_, "Config final readback [" << scope << "] " << fields); }; auto sync_filter_enabled = [&](const std::vector> &filter_list, const std::string &filter_name, const std::string ¶m_name, bool &member) { auto it = std::find_if(filter_list.begin(), filter_list.end(), [&](const auto &filter) { return filter && normalizeDepthFilterName(filter->type()) == normalizeDepthFilterName(filter_name); }); if (it == filter_list.end()) { return std::shared_ptr{}; } try { member = (*it)->isEnabled(); log_readback(filter_scope(filter_name), param_name, member); } catch (const std::exception &) { } return *it; }; if (sensor_name == "depth") { if (auto filter = sync_filter_enabled(filters, "DecimationFilter", "enable_decimation_filter", enable_decimation_filter_)) { try { decimation_filter_scale_ = static_cast(filter->as()->getScaleValue()); log_readback(filter_scope("DecimationFilter"), "scale", decimation_filter_scale_); } catch (const std::exception &) { } } if (auto filter = sync_filter_enabled(filters, "ThresholdFilter", "enable_threshold_filter", enable_threshold_filter_)) { try { threshold_filter_min_ = static_cast(filter->getConfigValue("min")); threshold_filter_max_ = static_cast(filter->getConfigValue("max")); std::ostringstream fields; fields << "min=" << threshold_filter_min_ << " max=" << threshold_filter_max_; log_readback_fields(filter_scope("ThresholdFilter"), fields.str()); } catch (const std::exception &) { } } sync_filter_enabled(filters, "HDRMerge", "enable_hdr_merge", enable_hdr_merge_); if (auto filter = sync_filter_enabled(filters, "SequenceIdFilter", "enable_sequence_id_filter", enable_sequence_id_filter_)) { try { sequence_id_filter_id_ = filter->as()->getSelectSequenceId(); log_readback(filter_scope("SequenceIdFilter"), "id", sequence_id_filter_id_); } catch (const std::exception &) { } } if (auto filter = sync_filter_enabled(filters, "SpatialFastFilter", "enable_spatial_fast_filter", enable_spatial_fast_filter_)) { try { spatial_fast_filter_radius_ = filter->as()->getFilterParams().radius; log_readback(filter_scope("SpatialFastFilter"), "radius", static_cast(spatial_fast_filter_radius_)); } catch (const std::exception &) { } } if (auto filter = sync_filter_enabled(filters, "SpatialModerateFilter", "enable_spatial_moderate_filter", enable_spatial_moderate_filter_)) { try { auto params = filter->as()->getFilterParams(); spatial_moderate_filter_diff_threshold_ = params.disp_diff; spatial_moderate_filter_magnitude_ = params.magnitude; spatial_moderate_filter_radius_ = params.radius; std::ostringstream fields; fields << "diff_threshold=" << spatial_moderate_filter_diff_threshold_ << " magnitude=" << spatial_moderate_filter_magnitude_ << " radius=" << spatial_moderate_filter_radius_; log_readback_fields(filter_scope("SpatialModerateFilter"), fields.str()); } catch (const std::exception &) { } } if (auto filter = sync_filter_enabled(filters, "SpatialAdvancedFilter", "enable_spatial_filter", enable_spatial_filter_)) { try { auto params = filter->as()->getFilterParams(); spatial_filter_alpha_ = params.alpha; spatial_filter_diff_threshold_ = params.disp_diff; spatial_filter_magnitude_ = params.magnitude; spatial_filter_radius_ = params.radius; std::ostringstream fields; fields << "alpha=" << spatial_filter_alpha_ << " diff_threshold=" << spatial_filter_diff_threshold_ << " magnitude=" << spatial_filter_magnitude_ << " radius=" << spatial_filter_radius_; log_readback_fields(filter_scope("SpatialAdvancedFilter"), fields.str()); } catch (const std::exception &) { } } if (auto filter = sync_filter_enabled(filters, "TemporalFilter", "enable_temporal_filter", enable_temporal_filter_)) { try { temporal_filter_diff_threshold_ = static_cast(filter->getConfigValue("diff_scale")); temporal_filter_weight_ = static_cast(filter->getConfigValue("weight")); std::ostringstream fields; fields << "diff_threshold=" << temporal_filter_diff_threshold_ << " weight=" << temporal_filter_weight_; log_readback_fields(filter_scope("TemporalFilter"), fields.str()); } catch (const std::exception &) { } } if (auto filter = sync_filter_enabled(filters, "HoleFillingFilter", "enable_hole_filling_filter", enable_hole_filling_filter_)) { try { hole_filling_filter_mode_ = std::to_string(static_cast(filter->as()->getFilterMode())); log_readback(filter_scope("HoleFillingFilter"), "mode", hole_filling_filter_mode_); } catch (const std::exception &) { } } sync_filter_enabled(filters, "EdgeNoiseRemovalFilter", "enable_edge_noise_removal_filter", enable_edge_noise_removal_filter_); if (auto filter = sync_filter_enabled(filters, "FalsePositiveFilter", "enable_false_positive_filter", enable_false_positive_filter_)) { try { const auto config_schema_vec = filter->getConfigSchemaVec(); for (const auto &config_schema : config_schema_vec) { if (config_schema.name == nullptr || config_schema.name[0] == '\0') { continue; } try { const auto value = filter->getConfigValue(config_schema.name); log_readback(filter_scope("FalsePositiveFilter"), config_schema.name, formatFilterConfigValue(config_schema, value)); } catch (const std::exception &) { } } } catch (const std::exception &) { } } sync_filter_enabled(filters, "DisparityTransform", "enable_disparity_to_depth", enable_disparity_to_depth_); } else if (sensor_name == "color" || sensor_name == "left_color" || sensor_name == "right_color") { bool *enable_member = &enable_color_decimation_filter_; int *scale_member = &color_decimation_filter_scale_; std::string param_name = "enable_color_decimation_filter"; if (sensor_name == "left_color") { enable_member = &enable_left_color_decimation_filter_; scale_member = &left_color_decimation_filter_scale_; param_name = "enable_left_color_decimation_filter"; } else if (sensor_name == "right_color") { enable_member = &enable_right_color_decimation_filter_; scale_member = &right_color_decimation_filter_scale_; param_name = "enable_right_color_decimation_filter"; } if (auto filter = sync_filter_enabled(filters, "DecimationFilter", param_name, *enable_member)) { try { *scale_member = static_cast(filter->as()->getScaleValue()); log_readback(filter_scope("DecimationFilter"), "scale", *scale_member); } catch (const std::exception &) { } } } else if (sensor_name == "left_ir") { if (auto filter = sync_filter_enabled(filters, "SequenceIdFilter", "enable_left_ir_sequence_id_filter", enable_left_ir_sequence_id_filter_)) { try { left_ir_sequence_id_filter_id_ = filter->as()->getSelectSequenceId(); log_readback(filter_scope("SequenceIdFilter"), "id", left_ir_sequence_id_filter_id_); } catch (const std::exception &) { } } } else if (sensor_name == "right_ir") { if (auto filter = sync_filter_enabled(filters, "SequenceIdFilter", "enable_right_ir_sequence_id_filter", enable_right_ir_sequence_id_filter_)) { try { right_ir_sequence_id_filter_id_ = filter->as()->getSelectSequenceId(); log_readback(filter_scope("SequenceIdFilter"), "id", right_ir_sequence_id_filter_id_); } catch (const std::exception &) { } } } } void OBCameraNode::setupColorPostProcessFilter() { if (!enable_stream_[COLOR] && !enable_stream_[COLOR_LEFT] && !enable_stream_[COLOR_RIGHT]) { return; } try { auto color_sensor = device_->getSensor(OB_SENSOR_COLOR); if (color_sensor) { color_filter_list_ = color_sensor->createRecommendedFilters(); } } catch (const std::exception &e) { RCLCPP_DEBUG_STREAM(logger_, "Main color sensor not found, trying left/right color sensors"); auto left_color_sensor = device_->getSensor(OB_SENSOR_COLOR_LEFT); if (left_color_sensor) { left_color_filter_list_ = left_color_sensor->createRecommendedFilters(); } auto right_color_sensor = device_->getSensor(OB_SENSOR_COLOR_RIGHT); if (right_color_sensor) { right_color_filter_list_ = right_color_sensor->createRecommendedFilters(); } } if (color_filter_list_.empty() && left_color_filter_list_.empty() && right_color_filter_list_.empty()) { RCLCPP_DEBUG_STREAM(logger_, "Color sensor filter lists are empty"); } for (size_t i = 0; i < color_filter_list_.size(); i++) { auto filter = color_filter_list_[i]; std::map filter_params = { {"DecimationFilter", enable_color_decimation_filter_}, }; std::map filter_param_names = { {"DecimationFilter", "enable_color_decimation_filter"}, }; std::string filter_name = filter->type(); RCLCPP_DEBUG_STREAM(logger_, "Configuring color filter: " << filter_name); if (filter_params.find(filter_name) != filter_params.end() && isLaunchParamProvided(filter_param_names[filter_name])) { const auto *value = filter_params[filter_name] ? "enabled" : "disabled"; RCLCPP_INFO_STREAM(logger_, "Set color filter " << filter_name << " to " << value); filter->enable(filter_params[filter_name]); } if (filter_name == "DecimationFilter" && enable_color_decimation_filter_) { auto decimation_filter = filter->as(); auto range = decimation_filter->getScaleRange(); if (color_decimation_filter_scale_ != -1 && color_decimation_filter_scale_ <= range.max && color_decimation_filter_scale_ >= range.min) { decimation_filter->setScaleValue(color_decimation_filter_scale_); } if (color_decimation_filter_scale_ != -1 && (color_decimation_filter_scale_ < range.min || color_decimation_filter_scale_ > range.max)) { RCLCPP_ERROR_STREAM(logger_, "Color Decimation filter scale value is out of range " << range.min << " - " << range.max); } RCLCPP_INFO_STREAM(logger_, "Current color decimation filter scale value: " << static_cast(decimation_filter->getScaleValue())); } } for (size_t i = 0; i < left_color_filter_list_.size(); i++) { auto filter = left_color_filter_list_[i]; std::map filter_params = { {"DecimationFilter", enable_left_color_decimation_filter_}, }; std::map filter_param_names = { {"DecimationFilter", "enable_left_color_decimation_filter"}, }; std::string filter_name = filter->type(); RCLCPP_DEBUG_STREAM(logger_, "Configuring left color filter: " << filter_name); if (filter_params.find(filter_name) != filter_params.end() && isLaunchParamProvided(filter_param_names[filter_name])) { const auto *value = filter_params[filter_name] ? "enabled" : "disabled"; RCLCPP_INFO_STREAM(logger_, "Set left color filter " << filter_name << " to " << value); filter->enable(filter_params[filter_name]); } if (filter_name == "DecimationFilter" && enable_left_color_decimation_filter_) { auto decimation_filter = filter->as(); auto range = decimation_filter->getScaleRange(); if (left_color_decimation_filter_scale_ != -1 && left_color_decimation_filter_scale_ <= range.max && left_color_decimation_filter_scale_ >= range.min) { decimation_filter->setScaleValue(left_color_decimation_filter_scale_); } if (left_color_decimation_filter_scale_ != -1 && (left_color_decimation_filter_scale_ < range.min || left_color_decimation_filter_scale_ > range.max)) { RCLCPP_ERROR_STREAM(logger_, "Left Color Decimation filter scale value is out of range " << range.min << " - " << range.max); } RCLCPP_INFO_STREAM(logger_, "Current left color decimation filter scale value: " << static_cast(decimation_filter->getScaleValue())); } } for (size_t i = 0; i < right_color_filter_list_.size(); i++) { auto filter = right_color_filter_list_[i]; std::map filter_params = { {"DecimationFilter", enable_right_color_decimation_filter_}, }; std::map filter_param_names = { {"DecimationFilter", "enable_right_color_decimation_filter"}, }; std::string filter_name = filter->type(); RCLCPP_DEBUG_STREAM(logger_, "Configuring right color filter: " << filter_name); if (filter_params.find(filter_name) != filter_params.end() && isLaunchParamProvided(filter_param_names[filter_name])) { const auto *value = filter_params[filter_name] ? "enabled" : "disabled"; RCLCPP_INFO_STREAM(logger_, "Set right color filter " << filter_name << " to " << value); filter->enable(filter_params[filter_name]); } if (filter_name == "DecimationFilter" && enable_right_color_decimation_filter_) { auto decimation_filter = filter->as(); auto range = decimation_filter->getScaleRange(); if (right_color_decimation_filter_scale_ != -1 && right_color_decimation_filter_scale_ <= range.max && right_color_decimation_filter_scale_ >= range.min) { decimation_filter->setScaleValue(right_color_decimation_filter_scale_); } if (right_color_decimation_filter_scale_ != -1 && (right_color_decimation_filter_scale_ < range.min || right_color_decimation_filter_scale_ > range.max)) { RCLCPP_ERROR_STREAM(logger_, "Right Color Decimation filter scale value is out of range " << range.min << " - " << range.max); } RCLCPP_INFO_STREAM(logger_, "Current right color decimation filter scale value: " << static_cast(decimation_filter->getScaleValue())); } } auto device_info = device_->getDeviceInfo(); CHECK_NOTNULL(device_info); if (pid_ == GEMINI2_PID || pid_ == GEMINI2L_PID) { if (isLaunchParamProvided("enable_color_decimation_filter") && enable_color_decimation_filter_) { auto decimation_filter = std::make_shared(); decimation_filter->enable(true); color_filter_list_.push_back(decimation_filter); auto range = decimation_filter->getScaleRange(); if (color_decimation_filter_scale_ != -1 && color_decimation_filter_scale_ <= range.max && color_decimation_filter_scale_ >= range.min) { decimation_filter->setScaleValue(color_decimation_filter_scale_); } if (color_decimation_filter_scale_ != -1 && (color_decimation_filter_scale_ < range.min || color_decimation_filter_scale_ > range.max)) { RCLCPP_ERROR_STREAM(logger_, "Color Decimation filter scale value is out of range " << range.min << " - " << range.max); } RCLCPP_INFO_STREAM(logger_, "Current color decimation filter scale value: " << static_cast(decimation_filter->getScaleValue())); } } } void OBCameraNode::setupIrPostProcessFilter() { if (!enable_stream_[INFRA0]) { return; } try { auto ir_sensor = device_->getSensor(OB_SENSOR_IR); ir_filter_list_ = ir_sensor->createRecommendedFilters(); if (ir_filter_list_.empty()) { RCLCPP_DEBUG_STREAM(logger_, "IR sensor filter list is empty"); } } catch (const ob::Error &e) { RCLCPP_WARN_STREAM(logger_, "Failed to setup ir filters: " << orbbec_camera::formatObErrorWithStatus(e)); } } void OBCameraNode::setupUndistortionFilters() { auto remove_undistortion_filter = [](std::vector> &filters) { filters.erase(std::remove_if(filters.begin(), filters.end(), [](const std::shared_ptr &filter) { return filter && std::string(filter->type()) == "UnDistortionFilter"; }), filters.end()); }; auto find_or_create_filter = [&](std::vector> &filters, OBStreamType stream_type) -> std::shared_ptr { for (auto &filter : filters) { if (filter && std::string(filter->type()) == "UnDistortionFilter") { auto undistortion_filter = filter->as(); undistortion_filter->setStreamType(stream_type); undistortion_filter->enable(true); return undistortion_filter; } } auto undistortion_filter = std::make_shared(stream_type); undistortion_filter->enable(true); filters.push_back(undistortion_filter); return undistortion_filter; }; auto setup_stream_filter = [&](const stream_index_pair &stream_index, std::vector> &filters) { if (!enable_stream_[stream_index] || !enable_undistortion_[stream_index]) { return; } if (stream_index == LIDAR) { RCLCPP_WARN_STREAM(logger_, "Undistortion is not supported for lidar stream"); return; } if (stream_index == COLOR && shouldUseHwD2CColorUndistortion()) { remove_undistortion_filter(filters); hw_d2c_color_undistortion_filter_ = std::make_shared(OB_STREAM_COLOR); hw_d2c_color_undistortion_filter_->enable(true); RCLCPP_INFO_STREAM(logger_, "Enable color undistortion with HW D2C depth intrinsic projection"); return; } auto undistortion_filter = find_or_create_filter(filters, stream_index.first); undistortion_filter->clearNewCameraMatrix(); RCLCPP_INFO_STREAM(logger_, "Enable " << stream_name_[stream_index] << " undistortion"); }; setup_stream_filter(COLOR, color_filter_list_); setup_stream_filter(COLOR_LEFT, left_color_filter_list_); setup_stream_filter(COLOR_RIGHT, right_color_filter_list_); setup_stream_filter(DEPTH, depth_filter_list_); setup_stream_filter(INFRA0, ir_filter_list_); setup_stream_filter(INFRA1, left_ir_filter_list_); setup_stream_filter(INFRA2, right_ir_filter_list_); } bool OBCameraNode::shouldUseHwD2CColorUndistortion() const { auto color_undistortion = enable_undistortion_.find(COLOR); return color_undistortion != enable_undistortion_.end() && color_undistortion->second && isDabaiASeriesForHwD2C(pid_) && depth_registration_ && align_mode_ == "HW" && align_target_stream_ == OB_STREAM_COLOR && enable_stream_.at(COLOR) && enable_stream_.at(DEPTH); } void OBCameraNode::configureHwD2CColorUndistortion(const std::shared_ptr &depth_frame) { if (!hw_d2c_color_undistortion_filter_ || hw_d2c_color_undistortion_configured_) { return; } if (!depth_frame) { RCLCPP_WARN_ONCE(logger_, "Skip HW D2C color undistortion setup because depth frame is not available"); return; } auto stream_profile = depth_frame->getStreamProfile(); if (!stream_profile) { RCLCPP_WARN_ONCE(logger_, "Skip HW D2C color undistortion setup because depth stream profile is null"); return; } auto video_profile = stream_profile->as(); if (!video_profile) { RCLCPP_WARN_ONCE(logger_, "Skip HW D2C color undistortion setup because depth profile is not video"); return; } hw_d2c_color_undistortion_filter_->setNewCameraMatrix(video_profile->getIntrinsic()); hw_d2c_color_undistortion_configured_ = true; RCLCPP_INFO_STREAM(logger_, "Configured HW D2C color undistortion with depth camera intrinsic"); } void OBCameraNode::applyHwD2CColorUndistortion(std::shared_ptr &frame_set, const std::shared_ptr &depth_frame) { if (!frame_set || !hw_d2c_color_undistortion_filter_) { return; } configureHwD2CColorUndistortion(depth_frame); if (!hw_d2c_color_undistortion_configured_) { return; } auto undistorted_frame = hw_d2c_color_undistortion_filter_->process(frame_set); if (!undistorted_frame) { RCLCPP_WARN_STREAM(logger_, "HW D2C color undistortion filter returned null frame"); return; } auto undistorted_frame_set = undistorted_frame->as(); if (!undistorted_frame_set) { RCLCPP_WARN_STREAM(logger_, "HW D2C color undistortion filter returned non-frameset output"); return; } frame_set = undistorted_frame_set; } void OBCameraNode::setupLeftIrPostProcessFilter() { if (!enable_stream_[INFRA1]) { return; } auto device_info = device_->getDeviceInfo(); CHECK_NOTNULL(device_info); if (isGemini335PID(pid_)) { auto left_ir_sensor = device_->getSensor(OB_SENSOR_IR_LEFT); left_ir_filter_list_ = left_ir_sensor->createRecommendedFilters(); if (left_ir_filter_list_.empty()) { RCLCPP_DEBUG_STREAM(logger_, "Left IR sensor filter list is empty"); return; } for (size_t i = 0; i < left_ir_filter_list_.size(); i++) { auto filter = left_ir_filter_list_[i]; std::map filter_params = { {"SequenceIdFilter", enable_left_ir_sequence_id_filter_}, }; std::map filter_param_names = { {"SequenceIdFilter", "enable_left_ir_sequence_id_filter"}, }; std::string filter_name = filter->type(); RCLCPP_DEBUG_STREAM(logger_, "Configuring left IR filter: " << filter_name); if (filter_params.find(filter_name) != filter_params.end() && isLaunchParamProvided(filter_param_names[filter_name])) { const auto *value = filter_params[filter_name] ? "enabled" : "disabled"; RCLCPP_INFO_STREAM(logger_, "Set left IR filter " << filter_name << " to " << value); filter->enable(filter_params[filter_name]); } if (filter_name == "SequenceIdFilter" && enable_left_ir_sequence_id_filter_) { auto sequenced_filter = filter->as(); if (left_ir_sequence_id_filter_id_ != -1) { sequenced_filter->selectSequenceId(left_ir_sequence_id_filter_id_); } RCLCPP_INFO_STREAM(logger_, "Current left ir SequenceIdFilter ID: " << sequenced_filter->getSelectSequenceId()); } } } } void OBCameraNode::setupRightIrPostProcessFilter() { if (!enable_stream_[INFRA2]) { return; } auto device_info = device_->getDeviceInfo(); CHECK_NOTNULL(device_info); if (isGemini335PID(pid_)) { auto right_ir_sensor = device_->getSensor(OB_SENSOR_IR_RIGHT); right_ir_filter_list_ = right_ir_sensor->createRecommendedFilters(); if (right_ir_filter_list_.empty()) { RCLCPP_DEBUG_STREAM(logger_, "Right IR sensor filter list is empty"); return; } for (size_t i = 0; i < right_ir_filter_list_.size(); i++) { auto filter = right_ir_filter_list_[i]; std::map filter_params = { {"SequenceIdFilter", enable_right_ir_sequence_id_filter_}, }; std::map filter_param_names = { {"SequenceIdFilter", "enable_right_ir_sequence_id_filter"}, }; std::string filter_name = filter->type(); RCLCPP_DEBUG_STREAM(logger_, "Configuring right IR filter: " << filter_name); if (filter_params.find(filter_name) != filter_params.end() && isLaunchParamProvided(filter_param_names[filter_name])) { const auto *value = filter_params[filter_name] ? "enabled" : "disabled"; RCLCPP_INFO_STREAM(logger_, "Set right IR filter " << filter_name << " to " << value); filter->enable(filter_params[filter_name]); } if (filter_name == "SequenceIdFilter" && enable_right_ir_sequence_id_filter_) { auto sequenced_filter = filter->as(); if (right_ir_sequence_id_filter_id_ != -1) { sequenced_filter->selectSequenceId(right_ir_sequence_id_filter_id_); } RCLCPP_INFO_STREAM(logger_, "Current right ir SequenceIdFilter ID: " << sequenced_filter->getSelectSequenceId()); } } } } void OBCameraNode::setupDepthPostProcessFilter() { if (!enable_stream_[DEPTH]) { return; } auto depth_sensor = device_->getSensor(OB_SENSOR_DEPTH); // set depth sensor to filter depth_filter_list_ = depth_sensor->createRecommendedFilters(); if (depth_filter_list_.empty()) { RCLCPP_DEBUG_STREAM(logger_, "Depth sensor filter list is empty"); return; } std::map filter_params = { {"DecimationFilter", enable_decimation_filter_}, {"HDRMerge", enable_hdr_merge_}, {"SequenceIdFilter", enable_sequence_id_filter_}, {"SpatialAdvancedFilter", enable_spatial_filter_}, {"TemporalFilter", enable_temporal_filter_}, {"HoleFillingFilter", enable_hole_filling_filter_}, {"EdgeNoiseRemovalFilter", enable_edge_noise_removal_filter_}, {"DisparityTransform", enable_disparity_to_depth_}, {"ThresholdFilter", enable_threshold_filter_}, {"SpatialFastFilter", enable_spatial_fast_filter_}, {"SpatialModerateFilter", enable_spatial_moderate_filter_}, {"FalsePositiveFilter", enable_false_positive_filter_}, {"MgcNoiseRemovalFilter", enable_mgc_noise_removal_filter_}, {"LutNoiseRemovalFilter", enable_lut_noise_removal_filter_}, }; std::map filter_param_names = { {"DecimationFilter", "enable_decimation_filter"}, {"HDRMerge", "enable_hdr_merge"}, {"SequenceIdFilter", "enable_sequence_id_filter"}, {"SpatialAdvancedFilter", "enable_spatial_filter"}, {"TemporalFilter", "enable_temporal_filter"}, {"HoleFillingFilter", "enable_hole_filling_filter"}, {"EdgeNoiseRemovalFilter", "enable_edge_noise_removal_filter"}, {"DisparityTransform", "enable_disparity_to_depth"}, {"ThresholdFilter", "enable_threshold_filter"}, {"SpatialFastFilter", "enable_spatial_fast_filter"}, {"SpatialModerateFilter", "enable_spatial_moderate_filter"}, {"FalsePositiveFilter", "enable_false_positive_filter"}, {"MgcNoiseRemovalFilter", "enable_mgc_noise_removal_filter"}, {"LutNoiseRemovalFilter", "enable_lut_noise_removal_filter"}, }; for (size_t i = 0; i < depth_filter_list_.size(); i++) { auto filter = depth_filter_list_[i]; std::string filter_name = filter->type(); RCLCPP_DEBUG_STREAM(logger_, "Configuring depth filter: " << filter_name); if (filter_params.find(filter_name) != filter_params.end() && isLaunchParamProvided(filter_param_names[filter_name])) { const auto *value = filter_params[filter_name] ? "enabled" : "disabled"; RCLCPP_INFO_STREAM(logger_, "Set depth filter " << filter_name << " to " << value); filter->enable(filter_params[filter_name]); filter_status_[filter_name] = filter_params[filter_name]; } if (filter_name == "DecimationFilter" && enable_decimation_filter_) { auto decimation_filter = filter->as(); auto range = decimation_filter->getScaleRange(); if (decimation_filter_scale_ != -1 && decimation_filter_scale_ <= range.max && decimation_filter_scale_ >= range.min) { decimation_filter->setScaleValue(decimation_filter_scale_); } if (decimation_filter_scale_ != -1 && (decimation_filter_scale_ < range.min || decimation_filter_scale_ > range.max)) { RCLCPP_ERROR_STREAM(logger_, "Decimation filter scale value is out of range " << range.min << " - " << range.max); } RCLCPP_INFO_STREAM(logger_, "Current decimation filter scale value: " << static_cast(decimation_filter->getScaleValue())); } else if (filter_name == "ThresholdFilter" && enable_threshold_filter_) { auto threshold_filter = filter->as(); if (threshold_filter_min_ != -1 && threshold_filter_max_ != -1) { threshold_filter->setValueRange(threshold_filter_min_, threshold_filter_max_); } RCLCPP_INFO_STREAM(logger_, "Current threshold filter value range: " << static_cast(threshold_filter->getConfigValue("min")) << " - " << static_cast(threshold_filter->getConfigValue("max"))); } else if (filter_name == "SpatialAdvancedFilter" && enable_spatial_filter_) { auto spatial_filter = filter->as(); if (spatial_filter_alpha_ != -1.0 && spatial_filter_magnitude_ != -1 && spatial_filter_radius_ != -1 && spatial_filter_diff_threshold_ != -1) { OBSpatialAdvancedFilterParams params{}; params.alpha = spatial_filter_alpha_; params.magnitude = spatial_filter_magnitude_; params.radius = spatial_filter_radius_; params.disp_diff = spatial_filter_diff_threshold_; spatial_filter->setFilterParams(params); } auto current_params = spatial_filter->getFilterParams(); RCLCPP_INFO_STREAM(logger_, "Current SpatialFilter params: " << "alpha=" << current_params.alpha << ", disp_diff=" << current_params.disp_diff << ", magnitude=" << static_cast(current_params.magnitude) << ", radius=" << current_params.radius); } else if (filter_name == "TemporalFilter" && enable_temporal_filter_) { auto temporal_filter = filter->as(); if (temporal_filter_diff_threshold_ != -1.0 && temporal_filter_weight_ != -1.0) { temporal_filter->setDiffScale(temporal_filter_diff_threshold_); temporal_filter->setWeight(temporal_filter_weight_); } RCLCPP_INFO_STREAM( logger_, "Current TemporalFilter params: " << "diff_scale=" << static_cast(temporal_filter->getConfigValue("diff_scale")) << ", weight=" << static_cast(temporal_filter->getConfigValue("weight"))); } else if (filter_name == "HoleFillingFilter" && enable_hole_filling_filter_ && !hole_filling_filter_mode_.empty()) { auto hole_filling_filter = filter->as(); RCLCPP_INFO_STREAM(logger_, "Default hole filling filter mode: " << hole_filling_filter_mode_); OBHoleFillingMode hole_filling_mode = holeFillingModeFromString(hole_filling_filter_mode_); hole_filling_filter->setFilterMode(hole_filling_mode); RCLCPP_INFO_STREAM(logger_, "Current HoleFillingFilter mode: " << static_cast(hole_filling_filter->getFilterMode())); } else if (filter_name == "SequenceIdFilter" && enable_sequence_id_filter_) { auto sequenced_filter = filter->as(); if (sequence_id_filter_id_ != -1) { sequenced_filter->selectSequenceId(sequence_id_filter_id_); } RCLCPP_INFO_STREAM( logger_, "Current SequenceIdFilter ID: " << sequenced_filter->getSelectSequenceId()); } else if (filter_name == "HDRMerge" && enable_hdr_merge_) { if (hdr_merge_exposure_1_ != -1 && hdr_merge_gain_1_ != -1 && hdr_merge_exposure_2_ != -1 && hdr_merge_gain_2_ != -1) { auto hdr_merge_filter = filter->as(); hdr_merge_filter->enable(true); auto config = OBHdrConfig(); config.enable = true; config.exposure_1 = hdr_merge_exposure_1_; config.gain_1 = hdr_merge_gain_1_; config.exposure_2 = hdr_merge_exposure_2_; config.gain_2 = hdr_merge_gain_2_; device_->setStructuredData(OB_STRUCT_DEPTH_HDR_CONFIG, reinterpret_cast(&config), sizeof(config)); uint32_t config_size = sizeof(config); device_->getStructuredData(OB_STRUCT_DEPTH_HDR_CONFIG, reinterpret_cast(&config), &config_size); RCLCPP_INFO_STREAM( logger_, "Current HDRMerge params: " << "exposure_1=" << config.exposure_1 << ", gain_1=" << config.gain_1 << ", exposure_2=" << config.exposure_2 << ", gain_2=" << config.gain_2); } } else if (filter_name == "SpatialFastFilter" && enable_spatial_fast_filter_) { auto spatial_fast_filter = filter->as(); OBSpatialFastFilterParams params{}; if (spatial_fast_filter_radius_ != -1) { params.radius = spatial_fast_filter_radius_; spatial_fast_filter->setFilterParams(params); } auto current_params = spatial_fast_filter->getFilterParams(); RCLCPP_INFO_STREAM( logger_, "Current SpatialFastFilter radius: " << static_cast(current_params.radius)); } else if (filter_name == "SpatialModerateFilter" && enable_spatial_moderate_filter_) { auto spatial_moderate_filter = filter->as(); OBSpatialModerateFilterParams params{}; if (spatial_moderate_filter_diff_threshold_ != -1 && spatial_moderate_filter_magnitude_ != -1 && spatial_moderate_filter_radius_ != -1) { params.magnitude = spatial_moderate_filter_magnitude_; params.radius = spatial_moderate_filter_radius_; params.disp_diff = spatial_moderate_filter_diff_threshold_; spatial_moderate_filter->setFilterParams(params); } auto current_params = spatial_moderate_filter->getFilterParams(); RCLCPP_INFO_STREAM(logger_, "Current SpatialModerateFilter params: " << "disp_diff=" << current_params.disp_diff << ", magnitude=" << static_cast(current_params.magnitude) << ", radius=" << static_cast(current_params.radius)); } else { RCLCPP_DEBUG_STREAM(logger_, "Skip setting filter: " << filter_name); } } auto device_info = device_->getDeviceInfo(); CHECK_NOTNULL(device_info); if (pid_ == GEMINI2_PID || pid_ == GEMINI2L_PID) { if (enable_decimation_filter_) { auto decimation_filter = std::make_shared(); decimation_filter->enable(true); depth_filter_list_.push_back(decimation_filter); auto range = decimation_filter->getScaleRange(); if (decimation_filter_scale_ != -1 && decimation_filter_scale_ <= range.max && decimation_filter_scale_ >= range.min) { decimation_filter->setScaleValue(decimation_filter_scale_); } if (decimation_filter_scale_ != -1 && (decimation_filter_scale_ < range.min || decimation_filter_scale_ > range.max)) { RCLCPP_ERROR_STREAM(logger_, "Decimation filter scale value is out of range " << range.min << " - " << range.max); } RCLCPP_INFO_STREAM(logger_, "Current decimation filter scale value: " << static_cast(decimation_filter->getScaleValue())); } } set_filter_srv_ = node_->create_service( "set_filter", [this](const std::shared_ptr request, std::shared_ptr response) { setFilterCallback(request, response); }); } void OBCameraNode::selectBaseStream() { if (enable_stream_[DEPTH]) { base_stream_ = DEPTH; } else if (enable_stream_[INFRA0]) { base_stream_ = INFRA0; } else if (enable_stream_[INFRA1]) { base_stream_ = INFRA1; } else if (enable_stream_[INFRA2]) { base_stream_ = INFRA2; } else if (enable_stream_[COLOR_LEFT]) { base_stream_ = COLOR_LEFT; } else if (enable_stream_[COLOR_RIGHT]) { base_stream_ = COLOR_RIGHT; } else if (enable_stream_[COLOR]) { base_stream_ = COLOR; } } void OBCameraNode::printSensorProfiles(const std::shared_ptr &sensor) { auto profiles = sensor->getStreamProfileList(); for (size_t i = 0; i < profiles->getCount(); i++) { auto origin_profile = profiles->getProfile(i); if (sensor->getType() == OB_SENSOR_COLOR) { auto profile = origin_profile->as(); RCLCPP_INFO_STREAM( logger_, "color profile: " << profile->getWidth() << "x" << profile->getHeight() << " " << profile->getFps() << "fps " << profile->getFormat()); } else if (sensor->getType() == OB_SENSOR_DEPTH) { auto profile = origin_profile->as(); RCLCPP_INFO_STREAM( logger_, "depth profile: " << profile->getWidth() << "x" << profile->getHeight() << " " << profile->getFps() << "fps " << profile->getFormat()); } else if (sensor->getType() == OB_SENSOR_IR) { auto profile = origin_profile->as(); RCLCPP_INFO_STREAM(logger_, "ir profile: " << profile->getWidth() << "x" << profile->getHeight() << " " << profile->getFps() << "fps " << profile->getFormat()); } else if (sensor->getType() == OB_SENSOR_ACCEL) { auto profile = origin_profile->as(); RCLCPP_INFO_STREAM(logger_, "accel profile: sampleRate " << profile->getSampleRate() << " full scale_range " << profile->getFullScaleRange()); } else if (sensor->getType() == OB_SENSOR_GYRO) { auto profile = origin_profile->as(); RCLCPP_INFO_STREAM(logger_, "gyro profile: sampleRate " << profile->getSampleRate() << " full scale_range " << profile->getFullScaleRange()); } else { RCLCPP_INFO_STREAM(logger_, "unknown profile: " << magic_enum::enum_name(sensor->getType())); } } } void OBCameraNode::setupProfiles() { // Image stream for (const auto &elem : IMAGE_STREAMS) { if (enable_stream_[elem]) { const auto &sensor = sensors_[elem]; CHECK_NOTNULL(sensor.get()); auto profiles = sensor->getStreamProfileList(); CHECK_NOTNULL(profiles.get()); CHECK(profiles->getCount() > 0); for (size_t i = 0; i < profiles->getCount(); i++) { auto base_profile = profiles->getProfile(i)->as(); if (base_profile == nullptr) { throw std::runtime_error("Failed to get profile " + std::to_string(i)); } auto profile = base_profile->as(); if (profile == nullptr) { throw std::runtime_error("Failed cast profile to VideoStreamProfile"); } RCLCPP_DEBUG_STREAM( logger_, "Sensor profile: " << "stream_type: " << magic_enum::enum_name(profile->getType()) << "Format: " << profile->getFormat() << ", Width: " << profile->getWidth() << ", Height: " << profile->getHeight() << ", FPS: " << profile->getFps()); supported_profiles_[elem].emplace_back(profile); } std::shared_ptr selected_profile; std::shared_ptr default_profile; try { if (width_[elem] == 0 && height_[elem] == 0 && fps_[elem] == 0 && format_[elem] == OB_FORMAT_UNKNOWN) { selected_profile = profiles->getProfile(0)->as(); } else { if (isGemini305SeriesPID(pid_) && elem == DEPTH) { OBHardwareDecimationConfig conf; conf.originWidth = width_[elem]; conf.originHeight = height_[elem]; conf.factor = depth_decimation_factor_; selected_profile = profiles->getVideoStreamProfile(conf, format_[elem], fps_[elem]); } else if (isGemini305SeriesPID(pid_) && elem == INFRA1) { OBHardwareDecimationConfig conf; conf.originWidth = width_[elem]; conf.originHeight = height_[elem]; conf.factor = left_ir_decimation_factor_; selected_profile = profiles->getVideoStreamProfile(conf, format_[elem], fps_[elem]); } else if (isGemini305SeriesPID(pid_) && elem == INFRA2) { OBHardwareDecimationConfig conf; conf.originWidth = width_[elem]; conf.originHeight = height_[elem]; conf.factor = right_ir_decimation_factor_; selected_profile = profiles->getVideoStreamProfile(conf, format_[elem], fps_[elem]); } else { selected_profile = profiles->getVideoStreamProfile(width_[elem], height_[elem], format_[elem], fps_[elem]); } } } catch (const ob::Error &ex) { RCLCPP_ERROR_STREAM(logger_, "Failed to get " << stream_name_[elem] << " profile: " << orbbec_camera::formatObErrorWithStatus(ex)); RCLCPP_ERROR_STREAM( logger_, "Stream: " << magic_enum::enum_name(elem.first) << ", Stream Index: " << elem.second << ", Width: " << width_[elem] << ", Height: " << height_[elem] << ", FPS: " << fps_[elem] << ", Format: " << magic_enum::enum_name(format_[elem])); RCLCPP_ERROR(logger_, "Error: The device might be connected via USB 2.0. Please verify your " "configuration and try again. The current process will now exit."); RCLCPP_INFO_STREAM(logger_, "Available profiles:"); printSensorProfiles(sensor); RCLCPP_ERROR(logger_, "Failed to configure the requested stream profile, exiting."); exit(-1); } if (!selected_profile) { RCLCPP_WARN_STREAM(logger_, "Requested stream configuration is not supported by the device: " << "stream=" << magic_enum::enum_name(elem.first) << ", stream_index=" << elem.second << ", width=" << width_[elem] << ", height=" << height_[elem] << ", fps=" << fps_[elem] << ", format=" << magic_enum::enum_name(format_[elem])); if (default_profile) { RCLCPP_WARN_STREAM(logger_, "Using the default profile instead"); RCLCPP_WARN_STREAM(logger_, "Default profile FPS: " << default_profile->getFps()); selected_profile = default_profile; } else { RCLCPP_ERROR_STREAM(logger_, "No default profile found, disabling stream " << magic_enum::enum_name(elem.first)); enable_stream_[elem] = false; continue; } } CHECK_NOTNULL(selected_profile); stream_profile_[elem] = selected_profile; height_[elem] = static_cast(selected_profile->getHeight()); width_[elem] = static_cast(selected_profile->getWidth()); fps_[elem] = static_cast(selected_profile->getFps()); format_[elem] = selected_profile->getFormat(); updateImageConfig(elem); if (selected_profile->format() == OB_FORMAT_BGRA) { images_[elem] = cv::Mat(height_[elem], width_[elem], CV_8UC4, cv::Scalar(0, 0, 0, 0)); encoding_[elem] = sensor_msgs::image_encodings::BGRA8; unit_step_size_[COLOR] = 4 * sizeof(uint8_t); } else if (selected_profile->format() == OB_FORMAT_RGBA) { images_[elem] = cv::Mat(height_[elem], width_[elem], CV_8UC4, cv::Scalar(0, 0, 0, 0)); encoding_[elem] = sensor_msgs::image_encodings::RGBA8; unit_step_size_[COLOR] = 4 * sizeof(uint8_t); } else { images_[elem] = cv::Mat(height_[elem], width_[elem], image_format_[elem], cv::Scalar(0, 0, 0)); } } } // IMU for (const auto &stream_index : HID_STREAMS) { if (!enable_stream_[stream_index]) { continue; } try { auto profile_list = sensors_[stream_index]->getStreamProfileList(); if (stream_index == ACCEL) { auto full_scale_range = fullAccelScaleRangeFromString(imu_range_[stream_index]); auto sample_rate = sampleRateFromString(imu_rate_[stream_index]); auto profile = profile_list->getAccelStreamProfile(full_scale_range, sample_rate); stream_profile_[stream_index] = profile; } else if (stream_index == GYRO) { auto full_scale_range = fullGyroScaleRangeFromString(imu_range_[stream_index]); auto sample_rate = sampleRateFromString(imu_rate_[stream_index]); auto profile = profile_list->getGyroStreamProfile(full_scale_range, sample_rate); stream_profile_[stream_index] = profile; } RCLCPP_INFO_STREAM(logger_, "stream " << stream_name_[stream_index] << " full scale range " << imu_range_[stream_index] << " sample rate " << imu_rate_[stream_index]); } catch (const ob::Error &e) { RCLCPP_INFO_STREAM(logger_, "Failed to setup << " << stream_name_[stream_index] << " profile: " << orbbec_camera::formatObErrorWithStatus(e)); enable_stream_[stream_index] = false; stream_profile_[stream_index] = nullptr; } } } std::shared_ptr OBCameraNode::selectVideoStreamProfile( const stream_index_pair &stream_index, int width, int height, int fps, OBFormat format) { auto sensor_it = sensors_.find(stream_index); if (sensor_it == sensors_.end() || !sensor_it->second) { throw std::runtime_error("Sensor is not available for stream " + stream_name_[stream_index]); } auto profiles = sensor_it->second->getStreamProfileList(); if (!profiles || profiles->getCount() == 0) { throw std::runtime_error("No stream profiles available for stream " + stream_name_[stream_index]); } std::shared_ptr selected_profile; if (width == 0 && height == 0 && fps == 0) { selected_profile = profiles->getProfile(0)->as(); } else if (isGemini305SeriesPID(pid_) && stream_index == DEPTH) { OBHardwareDecimationConfig conf; conf.originWidth = width; conf.originHeight = height; conf.factor = depth_decimation_factor_; selected_profile = profiles->getVideoStreamProfile(conf, format, fps); } else if (isGemini305SeriesPID(pid_) && stream_index == INFRA1) { OBHardwareDecimationConfig conf; conf.originWidth = width; conf.originHeight = height; conf.factor = left_ir_decimation_factor_; selected_profile = profiles->getVideoStreamProfile(conf, format, fps); } else if (isGemini305SeriesPID(pid_) && stream_index == INFRA2) { OBHardwareDecimationConfig conf; conf.originWidth = width; conf.originHeight = height; conf.factor = right_ir_decimation_factor_; selected_profile = profiles->getVideoStreamProfile(conf, format, fps); } else { selected_profile = profiles->getVideoStreamProfile(width, height, format, fps); } if (!selected_profile) { throw std::runtime_error("Requested stream profile is not supported"); } return selected_profile; } std::optional OBCameraNode::getImageStreamByName( const std::string &stream_name) const { if (stream_name == "color") { return COLOR; } if (stream_name == "left_color") { return COLOR_LEFT; } if (stream_name == "right_color") { return COLOR_RIGHT; } if (stream_name == "depth") { return DEPTH; } if (stream_name == "ir") { return INFRA0; } if (stream_name == "left_ir") { return INFRA1; } if (stream_name == "right_ir") { return INFRA2; } return std::nullopt; } bool OBCameraNode::validateStreamProfileRequest( const std::shared_ptr &request, std::vector &pending_profiles, std::string &message) { pending_profiles.clear(); if (!request || request->profiles.empty()) { message = "profiles is empty"; return false; } std::unordered_set requested_streams; bool has_changes = false; for (const auto &profile : request->profiles) { const auto stream_index = getImageStreamByName(profile.stream_name); if (!stream_index) { message = "Unsupported stream_name: " + profile.stream_name + ". Supported stream_name values: color, left_color, right_color, depth, ir, " "left_ir, right_ir"; return false; } if (!requested_streams.insert(profile.stream_name).second) { message = "Duplicated stream_name: " + profile.stream_name; return false; } if (!enable_stream_[*stream_index]) { message = "Stream is not enabled: " + profile.stream_name; return false; } if (profile.width < 0 || profile.height < 0 || profile.fps < 0) { message = profile.stream_name + " width, height and fps must be non-negative"; return false; } if ((profile.width > 0) != (profile.height > 0)) { message = profile.stream_name + " width and height must be provided together"; return false; } if (profile.width <= 0 && profile.fps <= 0 && profile.format.empty()) { message = profile.stream_name + " must provide resolution, fps or format"; return false; } const int requested_width = profile.width > 0 ? profile.width : width_[*stream_index]; const int requested_height = profile.height > 0 ? profile.height : height_[*stream_index]; const int requested_fps = profile.fps > 0 ? profile.fps : fps_[*stream_index]; if (requested_width <= 0 || requested_height <= 0 || requested_fps <= 0) { message = profile.stream_name + " current width, height and fps must be positive"; return false; } OBFormat requested_format = format_[*stream_index]; if (!profile.format.empty()) { std::string format_name; format_name.reserve(profile.format.size()); std::transform(profile.format.begin(), profile.format.end(), std::back_inserter(format_name), [](unsigned char ch) { return static_cast(std::toupper(ch)); }); if (format_name == "ANY") { requested_format = OB_FORMAT_UNKNOWN; } else { requested_format = OBFormatFromString(format_name); if (requested_format == OB_FORMAT_UNKNOWN) { message = "Unsupported format: " + profile.format; return false; } } } try { auto selected_profile = selectVideoStreamProfile( *stream_index, requested_width, requested_height, requested_fps, requested_format); has_changes = has_changes || static_cast(selected_profile->getWidth()) != width_[*stream_index] || static_cast(selected_profile->getHeight()) != height_[*stream_index] || static_cast(selected_profile->getFps()) != fps_[*stream_index] || selected_profile->getFormat() != format_[*stream_index]; pending_profiles.push_back( {*stream_index, requested_width, requested_height, requested_fps, selected_profile}); } catch (const ob::Error &e) { message = "Unsupported profile for " + profile.stream_name + ": " + orbbec_camera::formatObErrorWithStatus(e); return false; } catch (const std::exception &e) { message = "Unsupported profile for " + profile.stream_name + ": " + e.what(); return false; } } if (!has_changes) { message = "requested stream profiles are already active"; return false; } return true; } bool OBCameraNode::applyStreamProfiles(const std::vector &pending_profiles, std::string &message) { if (pending_profiles.empty()) { message = "profiles is empty"; return false; } std::lock_guard lock(device_lock_); try { const bool restart_pipeline = pipeline_started_.load(); const bool interleave_frame_enable = interleave_frame_enable_; if (restart_pipeline) { stopStreams(); interleave_frame_enable_ = interleave_frame_enable; } stopColorFrameThreads(); clearColorFrameQueues(); bool profile_affects_hw_d2c_color_undistortion = false; for (const auto &pending_profile : pending_profiles) { const auto &stream_index = pending_profile.stream_index; if (stream_index == COLOR || stream_index == DEPTH) { profile_affects_hw_d2c_color_undistortion = true; } auto selected_profile = pending_profile.profile; const auto old_format = format_[stream_index]; stream_profile_[stream_index] = selected_profile; height_[stream_index] = static_cast(selected_profile->getHeight()); width_[stream_index] = static_cast(selected_profile->getWidth()); fps_[stream_index] = static_cast(selected_profile->getFps()); format_[stream_index] = selected_profile->getFormat(); format_str_[stream_index] = OBFormatToString(format_[stream_index]); updateImageConfig(stream_index); if (old_format != format_[stream_index]) { setupImagePublisher(stream_index); } if (selected_profile->format() == OB_FORMAT_BGRA) { images_[stream_index] = cv::Mat(height_[stream_index], width_[stream_index], CV_8UC4, cv::Scalar(0, 0, 0, 0)); encoding_[stream_index] = sensor_msgs::image_encodings::BGRA8; unit_step_size_[stream_index] = 4 * sizeof(uint8_t); } else if (selected_profile->format() == OB_FORMAT_RGBA) { images_[stream_index] = cv::Mat(height_[stream_index], width_[stream_index], CV_8UC4, cv::Scalar(0, 0, 0, 0)); encoding_[stream_index] = sensor_msgs::image_encodings::RGBA8; unit_step_size_[stream_index] = 4 * sizeof(uint8_t); } else { images_[stream_index] = cv::Mat(height_[stream_index], width_[stream_index], image_format_[stream_index], cv::Scalar(0, 0, 0)); } } if (profile_affects_hw_d2c_color_undistortion && shouldUseHwD2CColorUndistortion()) { hw_d2c_color_undistortion_configured_ = false; } setupImageBuffers(); clearColorFrameQueues(); { std::lock_guard frame_info_lock(frame_info_logged_mutex_); frame_info_logged_.clear(); } if (restart_pipeline) { startStreams(); message = "stream profiles updated"; } else { message = "stream profiles updated, changes will take effect when streams are started"; } return true; } catch (const ob::Error &e) { message = orbbec_camera::formatObErrorWithStatus(e); } catch (const std::exception &e) { message = e.what(); } catch (...) { message = "unknown error"; } return false; } void OBCameraNode::clearColorFrameQueues() { { std::lock_guard lock(color_frame_queue_lock_); std::queue> empty; std::swap(color_frame_queue_, empty); } { std::lock_guard lock(left_color_frame_queue_lock_); std::queue> empty; std::swap(left_color_frame_queue_, empty); } { std::lock_guard lock(right_color_frame_queue_lock_); std::queue> empty; std::swap(right_color_frame_queue_, empty); } is_color_frame_decoded_ = false; is_left_color_frame_decoded_ = false; is_right_color_frame_decoded_ = false; } void OBCameraNode::stopColorFrameThreads() { if (!colorFrameThread_ && !leftColorFrameThread_ && !rightColorFrameThread_) { return; } stop_color_frame_threads_.store(true); color_frame_queue_cv_.notify_all(); left_color_frame_queue_cv_.notify_all(); right_color_frame_queue_cv_.notify_all(); if (colorFrameThread_ && colorFrameThread_->joinable()) { colorFrameThread_->join(); } if (leftColorFrameThread_ && leftColorFrameThread_->joinable()) { leftColorFrameThread_->join(); } if (rightColorFrameThread_ && rightColorFrameThread_->joinable()) { rightColorFrameThread_->join(); } colorFrameThread_.reset(); leftColorFrameThread_.reset(); rightColorFrameThread_.reset(); stop_color_frame_threads_.store(false); } void OBCameraNode::setupImageBuffers() { delete[] rgb_buffer_; rgb_buffer_ = nullptr; rgb_buffer_size_ = 0; delete[] rgb_buffer_left_; rgb_buffer_left_ = nullptr; rgb_buffer_left_size_ = 0; delete[] rgb_buffer_right_; rgb_buffer_right_ = nullptr; rgb_buffer_right_size_ = 0; jpeg_decoder_.reset(); jpeg_decoder_left_.reset(); jpeg_decoder_right_.reset(); #if defined(USE_RK_HW_DECODER) if (enable_stream_[COLOR] && width_.count(COLOR) && height_.count(COLOR)) { jpeg_decoder_ = std::make_unique(width_[COLOR], height_[COLOR]); } if (enable_stream_[COLOR_LEFT] && width_.count(COLOR_LEFT) && height_.count(COLOR_LEFT)) { jpeg_decoder_left_ = std::make_unique(width_[COLOR_LEFT], height_[COLOR_LEFT]); } if (enable_stream_[COLOR_RIGHT] && width_.count(COLOR_RIGHT) && height_.count(COLOR_RIGHT)) { jpeg_decoder_right_ = std::make_unique(width_[COLOR_RIGHT], height_[COLOR_RIGHT]); } #elif defined(USE_NV_HW_DECODER) if (enable_stream_[COLOR] && width_.count(COLOR) && height_.count(COLOR)) { jpeg_decoder_ = std::make_unique(width_[COLOR], height_[COLOR]); } if (enable_stream_[COLOR_LEFT] && width_.count(COLOR_LEFT) && height_.count(COLOR_LEFT)) { jpeg_decoder_left_ = std::make_unique(width_[COLOR_LEFT], height_[COLOR_LEFT]); } if (enable_stream_[COLOR_RIGHT] && width_.count(COLOR_RIGHT) && height_.count(COLOR_RIGHT)) { jpeg_decoder_right_ = std::make_unique(width_[COLOR_RIGHT], height_[COLOR_RIGHT]); } #endif if (enable_stream_[COLOR] && width_.count(COLOR) && height_.count(COLOR)) { rgb_buffer_size_ = static_cast(width_[COLOR]) * height_[COLOR] * 4; rgb_buffer_ = new uint8_t[rgb_buffer_size_]; } if (enable_stream_[COLOR_LEFT] && width_.count(COLOR_LEFT) && height_.count(COLOR_LEFT)) { rgb_buffer_left_size_ = static_cast(width_[COLOR_LEFT]) * height_[COLOR_LEFT] * 4; rgb_buffer_left_ = new uint8_t[rgb_buffer_left_size_]; } if (enable_stream_[COLOR_RIGHT] && width_.count(COLOR_RIGHT) && height_.count(COLOR_RIGHT)) { rgb_buffer_right_size_ = static_cast(width_[COLOR_RIGHT]) * height_[COLOR_RIGHT] * 4; rgb_buffer_right_ = new uint8_t[rgb_buffer_right_size_]; } rgb_point_cloud_buffer_size_ = 0; xy_table_data_size_ = 0; if (enable_colored_point_cloud_ && enable_stream_[DEPTH] && enable_stream_[COLOR]) { rgb_point_cloud_buffer_size_ = width_[COLOR] * height_[COLOR] * sizeof(OBColorPoint); xy_table_data_size_ = width_[DEPTH] * height_[DEPTH] * 2; } } void OBCameraNode::updateImageConfig(const stream_index_pair &stream_index) { const auto format = format_[stream_index]; const bool is_depth_stream = stream_index.first == OB_STREAM_DEPTH; const bool is_ir_stream = stream_index.first == OB_STREAM_IR || stream_index.first == OB_STREAM_IR_LEFT || stream_index.first == OB_STREAM_IR_RIGHT; const bool is_color_stream = stream_index == COLOR || stream_index == COLOR_LEFT || stream_index == COLOR_RIGHT; if (format == OB_FORMAT_Y8 || format == OB_FORMAT_GRAY) { image_format_[stream_index] = CV_8UC1; encoding_[stream_index] = is_depth_stream ? sensor_msgs::image_encodings::TYPE_8UC1 : sensor_msgs::image_encodings::MONO8; unit_step_size_[stream_index] = sizeof(uint8_t); } else if (format == OB_FORMAT_Y10 || format == OB_FORMAT_Y11 || format == OB_FORMAT_Y12 || format == OB_FORMAT_Y14 || format == OB_FORMAT_Y16 || format == OB_FORMAT_Z16 || format == OB_FORMAT_RW16) { image_format_[stream_index] = CV_16UC1; encoding_[stream_index] = is_depth_stream ? sensor_msgs::image_encodings::TYPE_16UC1 : sensor_msgs::image_encodings::MONO16; unit_step_size_[stream_index] = sizeof(uint16_t); } else if (format == OB_FORMAT_MJPG || format == OB_FORMAT_MJPEG) { if (is_ir_stream) { image_format_[stream_index] = CV_8UC1; encoding_[stream_index] = sensor_msgs::image_encodings::MONO8; unit_step_size_[stream_index] = sizeof(uint8_t); } else if (is_color_stream) { image_format_[stream_index] = CV_8UC3; encoding_[stream_index] = sensor_msgs::image_encodings::RGB8; unit_step_size_[stream_index] = 3 * sizeof(uint8_t); } } else if (format == OB_FORMAT_BGR) { image_format_[stream_index] = CV_8UC3; encoding_[stream_index] = sensor_msgs::image_encodings::BGR8; unit_step_size_[stream_index] = 3 * sizeof(uint8_t); } else if (format == OB_FORMAT_RGB || format == OB_FORMAT_RGB888) { image_format_[stream_index] = CV_8UC3; encoding_[stream_index] = sensor_msgs::image_encodings::RGB8; unit_step_size_[stream_index] = 3 * sizeof(uint8_t); } else if (format == OB_FORMAT_BGRA) { image_format_[stream_index] = CV_8UC4; encoding_[stream_index] = sensor_msgs::image_encodings::BGRA8; unit_step_size_[stream_index] = 4 * sizeof(uint8_t); } else if (format == OB_FORMAT_RGBA) { image_format_[stream_index] = CV_8UC4; encoding_[stream_index] = sensor_msgs::image_encodings::RGBA8; unit_step_size_[stream_index] = 4 * sizeof(uint8_t); } } int OBCameraNode::init_interleave_hdr_param() { auto set_int_if_provided = [this](OBPropertyID property_id, int value) { if (value != -1) { device_->setIntProperty(property_id, value); } }; device_->setIntProperty(OB_PROP_FRAME_INTERLEAVE_CONFIG_INDEX_INT, 1); if (!isnotLaserDevices(pid_) && hdr_index1_laser_control_ != -1) { device_->setIntProperty(OB_PROP_LASER_CONTROL_INT, hdr_index1_laser_control_); } set_int_if_provided(OB_PROP_DEPTH_EXPOSURE_INT, hdr_index1_depth_exposure_); set_int_if_provided(OB_PROP_IR_EXPOSURE_INT, hdr_index1_depth_exposure_); set_int_if_provided(OB_PROP_DEPTH_GAIN_INT, hdr_index1_depth_gain_); set_int_if_provided(OB_PROP_IR_BRIGHTNESS_INT, hdr_index1_ir_brightness_); set_int_if_provided(OB_PROP_IR_AE_MAX_EXPOSURE_INT, hdr_index1_ir_ae_max_exposure_); // set interleaveae device_->setIntProperty(OB_PROP_FRAME_INTERLEAVE_CONFIG_INDEX_INT, 0); if (!isnotLaserDevices(pid_) && hdr_index0_laser_control_ != -1) { device_->setIntProperty(OB_PROP_LASER_CONTROL_INT, hdr_index0_laser_control_); } set_int_if_provided(OB_PROP_DEPTH_EXPOSURE_INT, hdr_index0_depth_exposure_); set_int_if_provided(OB_PROP_IR_EXPOSURE_INT, hdr_index0_depth_exposure_); set_int_if_provided(OB_PROP_DEPTH_GAIN_INT, hdr_index0_depth_gain_); set_int_if_provided(OB_PROP_IR_BRIGHTNESS_INT, hdr_index0_ir_brightness_); set_int_if_provided(OB_PROP_IR_AE_MAX_EXPOSURE_INT, hdr_index0_ir_ae_max_exposure_); return 0; } int OBCameraNode::init_interleave_laser_param() { auto set_int_if_provided = [this](OBPropertyID property_id, int value) { if (value != -1) { device_->setIntProperty(property_id, value); } }; device_->setIntProperty(OB_PROP_FRAME_INTERLEAVE_CONFIG_INDEX_INT, 1); set_int_if_provided(OB_PROP_LASER_CONTROL_INT, laser_index1_laser_control_); set_int_if_provided(OB_PROP_DEPTH_EXPOSURE_INT, laser_index1_depth_exposure_); set_int_if_provided(OB_PROP_IR_EXPOSURE_INT, laser_index1_depth_exposure_); set_int_if_provided(OB_PROP_DEPTH_GAIN_INT, laser_index1_depth_gain_); set_int_if_provided(OB_PROP_IR_BRIGHTNESS_INT, laser_index1_ir_brightness_); set_int_if_provided(OB_PROP_IR_AE_MAX_EXPOSURE_INT, laser_index1_ir_ae_max_exposure_); // set interleaveae device_->setIntProperty(OB_PROP_FRAME_INTERLEAVE_CONFIG_INDEX_INT, 0); set_int_if_provided(OB_PROP_LASER_CONTROL_INT, laser_index0_laser_control_); set_int_if_provided(OB_PROP_DEPTH_EXPOSURE_INT, laser_index0_depth_exposure_); set_int_if_provided(OB_PROP_IR_EXPOSURE_INT, laser_index0_depth_exposure_); set_int_if_provided(OB_PROP_DEPTH_GAIN_INT, laser_index0_depth_gain_); set_int_if_provided(OB_PROP_IR_BRIGHTNESS_INT, laser_index0_ir_brightness_); set_int_if_provided(OB_PROP_IR_AE_MAX_EXPOSURE_INT, laser_index0_ir_ae_max_exposure_); return 0; } void OBCameraNode::startStreams() { if (pipeline_ != nullptr) { pipeline_.reset(); } pipeline_ = std::make_unique(device_); try { setupPipelineConfig(); pipeline_->start(pipeline_config_, [this](const std::shared_ptr &frame_set) { onNewFrameSetCallback(frame_set); }); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to start pipeline: " << orbbec_camera::formatObErrorWithStatus(e)); RCLCPP_INFO_STREAM(logger_, "try to disable ir stream and try again"); enable_stream_[INFRA0] = false; setupPipelineConfig(); pipeline_->start(pipeline_config_, [this](const std::shared_ptr &frame_set) { onNewFrameSetCallback(frame_set); }); } catch (...) { RCLCPP_ERROR_STREAM(logger_, "Failed to start pipeline"); throw std::runtime_error("Failed to start pipeline"); } if (enable_stream_[COLOR] && !colorFrameThread_) { colorFrameThread_ = std::make_shared([this]() { onNewColorFrameCallback(); }); } if (enable_stream_[COLOR_LEFT] && !leftColorFrameThread_) { leftColorFrameThread_ = std::make_shared([this]() { onNewLeftColorFrameCallback(); }); } if (enable_stream_[COLOR_RIGHT] && !rightColorFrameThread_) { rightColorFrameThread_ = std::make_shared([this]() { onNewRightColorFrameCallback(); }); } if (enable_frame_sync_) { RCLCPP_INFO_STREAM(logger_, "Enable frame sync"); TRY_EXECUTE_BLOCK(pipeline_->enableFrameSync()); } else { RCLCPP_INFO_STREAM(logger_, "Disable frame sync"); TRY_EXECUTE_BLOCK(pipeline_->disableFrameSync()); } std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // set interleave mode if (interleave_ae_mode_ == "hdr" && interleave_frame_enable_) { RCLCPP_INFO_STREAM(logger_, "Set interleave mode to hdr"); device_->loadFrameInterleave("Depth from HDR"); init_interleave_hdr_param(); } else if (interleave_ae_mode_ == "laser" && interleave_frame_enable_) { RCLCPP_INFO_STREAM(logger_, "Set interleave mode to laser"); device_->loadFrameInterleave("Laser On-Off"); init_interleave_laser_param(); } else { RCLCPP_DEBUG_STREAM(logger_, "Set interleave mode to nothing"); } // enable interleave frame if ((interleave_ae_mode_ == "hdr") || (interleave_ae_mode_ == "laser")) { RCLCPP_INFO_STREAM(logger_, "current interleave_ae_mode_: " << interleave_ae_mode_); if (device_->isPropertySupported(OB_PROP_FRAME_INTERLEAVE_ENABLE_BOOL, OB_PERMISSION_WRITE)) { TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_FRAME_INTERLEAVE_ENABLE_BOOL, interleave_frame_enable_); RCLCPP_INFO_STREAM( logger_, "Enable enable_interleave_depth_frame to " << (device_->getBoolProperty(OB_PROP_FRAME_INTERLEAVE_ENABLE_BOOL) ? "true" : "false")); } } // set interleave larse PATTERN_SYNC_DELAY if ((interleave_ae_mode_ == "laser") && interleave_frame_enable_ && device_->isPropertySupported(OB_PROP_FRAME_INTERLEAVE_LASER_PATTERN_SYNC_DELAY_INT, OB_PERMISSION_READ_WRITE) && (sync_mode_str_ == "PRIMARY" || sync_mode_str_ == "SOFTWARE_TRIGGERING")) { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_FRAME_INTERLEAVE_LASER_PATTERN_SYNC_DELAY_INT, 0); RCLCPP_INFO_STREAM(logger_, "Current interleave laser pattern sync delay: " << device_->getIntProperty( OB_PROP_FRAME_INTERLEAVE_LASER_PATTERN_SYNC_DELAY_INT)); } pipeline_started_.store(true); } void OBCameraNode::startIMUSyncStream() { if (imuPipeline_ != nullptr) { imuPipeline_.reset(); } imuPipeline_ = std::make_unique(device_); if (imu_sync_output_start_) { return; } // ACCEL auto accelProfiles = imuPipeline_->getStreamProfileList(OB_SENSOR_ACCEL); auto accel_range = fullAccelScaleRangeFromString(imu_range_[ACCEL]); auto accel_rate = sampleRateFromString(imu_rate_[ACCEL]); auto accelProfile = accelProfiles->getAccelStreamProfile(accel_range, accel_rate); // GYRO auto gyroProfiles = imuPipeline_->getStreamProfileList(OB_SENSOR_GYRO); auto gyro_range = fullGyroScaleRangeFromString(imu_range_[GYRO]); auto gyro_rate = sampleRateFromString(imu_rate_[GYRO]); auto gyroProfile = gyroProfiles->getGyroStreamProfile(gyro_range, gyro_rate); std::shared_ptr imuConfig = std::make_shared(); imuConfig->enableStream(accelProfile); imuConfig->enableStream(gyroProfile); TRY_EXECUTE_BLOCK(imuPipeline_->enableFrameSync()); try { imuPipeline_->start(imuConfig, [&](std::shared_ptr frame) { auto frameSet = frame->as(); auto aFrame = frameSet->getFrame(OB_FRAME_ACCEL); auto gFrame = frameSet->getFrame(OB_FRAME_GYRO); const bool log_imu_timestamps = is_camera_node_initialized_.load() && rclcpp::ok() && imu_timestamp_csv_logger_ && imu_timestamp_csv_logger_->enabled(); const auto arrival_system_us = log_imu_timestamps ? getSystemNowUs() : 0; if (aFrame && gFrame) { onNewIMUFrameSyncOutputCallback(aFrame, gFrame, arrival_system_us); } else if (log_imu_timestamps && (aFrame || gFrame)) { imu_timestamp_csv_logger_->recordFrameSet(aFrame, gFrame, arrival_system_us, std::nullopt); } }); imu_sync_output_start_ = true; RCLCPP_INFO_STREAM( logger_, "start accel stream with range: " << fullAccelScaleRangeToString(accel_range) << ",rate:" << sampleRateToString(accel_rate) << ", and start gyro stream with range:" << fullGyroScaleRangeToString(gyro_range) << ",rate:" << sampleRateToString(gyro_rate)); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM( logger_, "Failed to start IMU sync stream: " << orbbec_camera::formatObErrorWithStatus(e)); imu_sync_output_start_ = false; } catch (...) { RCLCPP_ERROR_STREAM( logger_, "Failed to start IMU stream, please check the imu_rate and imu_range parameters."); imu_sync_output_start_ = false; } } void OBCameraNode::startIMU() { if (enable_sync_output_accel_gyro_) { startIMUSyncStream(); } else { for (const auto &stream_index : HID_STREAMS) { if (enable_stream_[stream_index] && !imu_started_[stream_index]) { auto imu_profile = stream_profile_[stream_index]; CHECK_NOTNULL(imu_profile); RCLCPP_INFO_STREAM(logger_, "start " << stream_name_[stream_index] << " stream"); CHECK_NOTNULL(sensors_[stream_index]); sensors_[stream_index]->start( imu_profile, [this, stream_index](const std::shared_ptr &frame) { onNewIMUFrameCallback(frame, stream_index); }); imu_started_[stream_index] = true; } } } } void OBCameraNode::restartPlaybackStreams() { if (!is_playback_device_ || !is_running_.load()) { return; } try { stopStreams(); startStreams(); stopIMU(); startIMU(); } catch (const ob::Error &e) { RCLCPP_WARN_STREAM(logger_, "Failed to restart playback streams: " << orbbec_camera::formatObErrorWithStatus(e)); } catch (const std::exception &e) { RCLCPP_WARN_STREAM(logger_, "Failed to restart playback streams: " << e.what()); } catch (...) { RCLCPP_WARN_STREAM(logger_, "Failed to restart playback streams"); } } void OBCameraNode::stopStreams() { std::lock_guard lock(device_lock_); if (!pipeline_started_ || !pipeline_) { RCLCPP_DEBUG_STREAM(logger_, "pipeline not started or not exist, skip stop pipeline"); return; } // Mark pipeline as stopping to prevent new operations pipeline_started_.store(false); try { // Check if device is still valid before stopping pipeline if (device_ && pipeline_) { pipeline_->stop(); // disable interleave frame only if device is still connected if ((interleave_ae_mode_ == "hdr") || (interleave_ae_mode_ == "laser")) { try { RCLCPP_DEBUG_STREAM(logger_, "Current interleave AE mode: " << interleave_ae_mode_); if (device_->isPropertySupported(OB_PROP_FRAME_INTERLEAVE_ENABLE_BOOL, OB_PERMISSION_WRITE)) { interleave_frame_enable_ = false; RCLCPP_DEBUG_STREAM(logger_, "Set enable_interleave_depth_frame to " << (interleave_frame_enable_ ? "true" : "false")); TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_FRAME_INTERLEAVE_ENABLE_BOOL, interleave_frame_enable_); } } catch (const ob::Error &e) { RCLCPP_WARN_STREAM(logger_, "Failed to disable interleave frame during shutdown: " << orbbec_camera::formatObErrorWithStatus(e)); } catch (...) { RCLCPP_WARN_STREAM(logger_, "Failed to disable interleave frame during shutdown"); } } } else { RCLCPP_WARN_STREAM(logger_, "Device or pipeline not available during stop - likely disconnected"); } } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to stop pipeline: " << orbbec_camera::formatObErrorWithStatus(e)); } catch (...) { RCLCPP_ERROR_STREAM(logger_, "Failed to stop pipeline"); } } void OBCameraNode::stopIMU() { std::lock_guard lock(device_lock_); if (enable_sync_output_accel_gyro_) { if (!imu_sync_output_start_ || !imuPipeline_) { RCLCPP_DEBUG_STREAM(logger_, "IMU pipeline not started or unavailable, skip stopping IMU pipeline"); return; } try { imuPipeline_->stop(); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM( logger_, "Failed to stop imu pipeline: " << orbbec_camera::formatObErrorWithStatus(e)); } catch (...) { RCLCPP_ERROR_STREAM(logger_, "Failed to stop imu pipeline"); } imu_sync_output_start_.store(false); } else { for (const auto &stream_index : HID_STREAMS) { if (imu_started_[stream_index]) { CHECK(sensors_.count(stream_index)); RCLCPP_DEBUG_STREAM(logger_, "Stop " << stream_name_[stream_index] << " stream"); try { sensors_[stream_index]->stop(); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to stop " << stream_name_[stream_index] << " stream: " << orbbec_camera::formatObErrorWithStatus(e)); } imu_started_[stream_index] = false; } } } } // cs_param_t rd_par = {0, 0}, param = {1, 3000}; //30 int OBCameraNode::openSocSyncPwmTrigger(uint16_t fps) { const char *devicePath = DEVICE_PATH; const int TRIGGER_MODE_ENABLE = 1; const int TRIGGER_MODE_DISABLE = 0; int ret = -1; cs_param_t param = {TRIGGER_MODE_ENABLE, fps}; cs_param_t rd_par = {TRIGGER_MODE_DISABLE, 0}; if (access(devicePath, F_OK) != 0) { std::cerr << "Device node " << devicePath << " does not exist." << std::endl; return ret; } gmsl_trigger_fd_ = open(DEVICE_PATH, O_RDWR); if (gmsl_trigger_fd_ < 0) { perror("open device failed\n"); return gmsl_trigger_fd_; } std::cout << "Written param mode=" << param.mode << ", fps=" << param.fps << std::endl; ret = write(gmsl_trigger_fd_, ¶m, sizeof(param)); if (ret < 0) { perror("write device failed\n"); close(gmsl_trigger_fd_); return ret; } ret = read(gmsl_trigger_fd_, &rd_par, sizeof(rd_par)); if (ret < 0) { perror("read device failed\n"); close(gmsl_trigger_fd_); return ret; } std::cout << "Read param mode=" << rd_par.mode << ", fps=" << rd_par.fps << std::endl; std::cout << "Start hardware triggering..." << std::endl; return 0; } int OBCameraNode::closeSocSyncPwmTrigger() { if (gmsl_trigger_fd_ >= 0) { close(gmsl_trigger_fd_); gmsl_trigger_fd_ = -1; // Reset file descriptors std::cout << "close camSync success" << std::endl; return 0; } return -1; } void OBCameraNode::startGmslTrigger() { if (gmsl_trigger_fps_ > 0 && enable_gmsl_trigger_) { RCLCPP_WARN_STREAM(logger_, "Start HardwareTrigger by soc-trigger-source. gmsl_trigger_fps_: " << gmsl_trigger_fps_); openSocSyncPwmTrigger(gmsl_trigger_fps_); } else { RCLCPP_WARN_STREAM(logger_, "Start HardwareTrigger by soc-trigger-source. gmsl_trigger_fps_ illegal: " << gmsl_trigger_fps_); } } void OBCameraNode::stopGmslTrigger() { closeSocSyncPwmTrigger(); } void OBCameraNode::setupDefaultImageFormat() { format_[DEPTH] = OB_FORMAT_Y16; format_str_[DEPTH] = "Y16"; image_format_[DEPTH] = CV_16UC1; encoding_[DEPTH] = sensor_msgs::image_encodings::TYPE_16UC1; unit_step_size_[DEPTH] = sizeof(uint16_t); format_[INFRA0] = OB_FORMAT_Y16; format_str_[INFRA0] = "Y16"; image_format_[INFRA0] = CV_16UC1; encoding_[INFRA0] = sensor_msgs::image_encodings::MONO16; unit_step_size_[INFRA0] = sizeof(uint16_t); format_[INFRA1] = OB_FORMAT_Y16; format_str_[INFRA1] = "Y16"; image_format_[INFRA1] = CV_16UC1; encoding_[INFRA1] = sensor_msgs::image_encodings::MONO16; unit_step_size_[INFRA1] = sizeof(uint16_t); format_[INFRA2] = OB_FORMAT_Y16; format_str_[INFRA2] = "Y16"; image_format_[INFRA2] = CV_16UC1; encoding_[INFRA2] = sensor_msgs::image_encodings::MONO16; unit_step_size_[INFRA2] = sizeof(uint16_t); image_format_[COLOR] = CV_8UC3; encoding_[COLOR] = sensor_msgs::image_encodings::RGB8; unit_step_size_[COLOR] = 3 * sizeof(uint8_t); image_format_[COLOR_LEFT] = CV_8UC3; encoding_[COLOR_LEFT] = sensor_msgs::image_encodings::RGB8; unit_step_size_[COLOR_LEFT] = 3 * sizeof(uint8_t); image_format_[COLOR_RIGHT] = CV_8UC3; encoding_[COLOR_RIGHT] = sensor_msgs::image_encodings::RGB8; unit_step_size_[COLOR_RIGHT] = 3 * sizeof(uint8_t); } void OBCameraNode::getParameters() { setAndGetNodeParameter(camera_name_, "camera_name", "camera"); camera_link_frame_id_ = camera_name_ + "_link"; for (auto stream_index : IMAGE_STREAMS) { std::string param_name = stream_name_[stream_index] + "_width"; setAndGetNodeParameter(width_[stream_index], param_name, 0); param_name = stream_name_[stream_index] + "_height"; setAndGetNodeParameter(height_[stream_index], param_name, 0); param_name = stream_name_[stream_index] + "_fps"; setAndGetNodeParameter(fps_[stream_index], param_name, 0); param_name = "enable_" + stream_name_[stream_index]; if (stream_index == DEPTH) { setAndGetNodeParameter(enable_stream_[stream_index], param_name, true); } else { setAndGetNodeParameter(enable_stream_[stream_index], param_name, false); } param_name = stream_name_[stream_index] + "_flip"; setAndGetNodeParameter(flip_stream_[stream_index], param_name, false); param_name = stream_name_[stream_index] + "_mirror"; setAndGetNodeParameter(mirror_stream_[stream_index], param_name, false); param_name = stream_name_[stream_index] + "_rotation"; setAndGetNodeParameter(rotation_stream_[stream_index], param_name, -1); param_name = camera_name_ + "_" + stream_name_[stream_index] + "_frame_id"; std::string default_frame_id = camera_name_ + "_" + stream_name_[stream_index] + "_frame"; setAndGetNodeParameter(frame_id_[stream_index], param_name, default_frame_id); std::string default_optical_frame_id = camera_name_ + "_" + stream_name_[stream_index] + "_optical_frame"; param_name = stream_name_[stream_index] + "_optical_frame_id"; setAndGetNodeParameter(optical_frame_id_[stream_index], param_name, default_optical_frame_id); param_name = stream_name_[stream_index] + "_format"; setAndGetNodeParameter(format_str_[stream_index], param_name, format_str_[stream_index]); format_[stream_index] = OBFormatFromString(format_str_[stream_index]); updateImageConfig(stream_index); param_name = stream_name_[stream_index] + "_qos"; setAndGetNodeParameter(image_qos_[stream_index], param_name, "default"); param_name = stream_name_[stream_index] + "_camera_info_qos"; setAndGetNodeParameter(camera_info_qos_[stream_index], param_name, "default"); param_name = "enable_" + stream_name_[stream_index] + "_undistortion"; setAndGetNodeParameter(enable_undistortion_[stream_index], param_name, false); } for (auto stream_index : IMAGE_STREAMS) { depth_aligned_frame_id_[stream_index] = optical_frame_id_[COLOR]; } accel_gyro_frame_id_ = camera_name_ + "_accel_gyro_optical_frame"; setAndGetNodeParameter(enable_sync_output_accel_gyro_, "enable_sync_output_accel_gyro", false); for (const auto &stream_index : HID_STREAMS) { std::string param_name = stream_name_[stream_index] + "_qos"; setAndGetNodeParameter(imu_qos_[stream_index], param_name, "default"); param_name = "enable_" + stream_name_[stream_index]; setAndGetNodeParameter(enable_stream_[stream_index], param_name, false); if (enable_sync_output_accel_gyro_) { enable_stream_[stream_index] = true; } param_name = stream_name_[stream_index] + "_rate"; setAndGetNodeParameter(imu_rate_[stream_index], param_name, ""); param_name = stream_name_[stream_index] + "_range"; setAndGetNodeParameter(imu_range_[stream_index], param_name, ""); param_name = camera_name_ + "_" + stream_name_[stream_index] + "_frame_id"; std::string default_frame_id = camera_name_ + "_" + stream_name_[stream_index] + "_frame"; setAndGetNodeParameter(frame_id_[stream_index], param_name, default_frame_id); std::string default_optical_frame_id = camera_name_ + "_" + stream_name_[stream_index] + "_optical_frame"; param_name = stream_name_[stream_index] + "_optical_frame_id"; setAndGetNodeParameter(optical_frame_id_[stream_index], param_name, default_optical_frame_id); depth_aligned_frame_id_[stream_index] = camera_name_ + "_" + stream_name_[COLOR] + "_optical_frame"; } setAndGetNodeParameter(publish_tf_, "publish_tf", true); setAndGetNodeParameter(tf_publish_rate_, "tf_publish_rate", 0.0); setAndGetNodeParameter(depth_registration_, "depth_registration", false); bool enable_enhanced_depth = false; setAndGetNodeParameter(enable_enhanced_depth, "enable_enhanced_depth", false); enable_enhanced_depth_.store(enable_enhanced_depth); setAndGetNodeParameter(enhanced_depth_model_path_, "enhanced_depth_model_path", ""); setAndGetNodeParameter(enhanced_depth_confidence_threshold_, "enhanced_depth_confidence_threshold", 51); setAndGetNodeParameter(enable_point_cloud_, "enable_point_cloud", false); setAndGetNodeParameter(ir_info_url_, "ir_info_url", ""); setAndGetNodeParameter(color_info_url_, "color_info_url", ""); setAndGetNodeParameter(enable_colored_point_cloud_, "enable_colored_point_cloud", false); setAndGetNodeParameter(enable_point_cloud_, "enable_point_cloud", false); setAndGetNodeParameter(point_cloud_decimation_filter_factor_, "point_cloud_decimation_filter_factor", 1); setAndGetNodeParameter(point_cloud_qos_, "point_cloud_qos", "default"); setAndGetNodeParameter(enable_d2c_viewer_, "enable_d2c_viewer", false); setAndGetNodeParameter(disparity_to_depth_mode_, "disparity_to_depth_mode", ""); disparity_to_depth_mode_ = normalizeClosedSetParameterValue(logger_, "disparity_to_depth_mode", disparity_to_depth_mode_, {"", "HW", "SW", "disable"}, ""); setAndGetNodeParameter(depth_filter_config_, "depth_filter_config", ""); if (!depth_filter_config_.empty()) { enable_depth_filter_ = true; } setAndGetNodeParameter(enable_frame_sync_, "enable_frame_sync", false); setAndGetNodeParameter(enable_color_auto_exposure_priority_, "enable_color_auto_exposure_priority", false); setAndGetNodeParameter(enable_color_auto_exposure_, "enable_color_auto_exposure", true); setAndGetNodeParameter(enable_color_auto_white_balance_, "enable_color_auto_white_balance", true); setAndGetNodeParameter(color_ae_roi_left_, "color_ae_roi_left", -1); setAndGetNodeParameter(color_ae_roi_top_, "color_ae_roi_top", -1); setAndGetNodeParameter(color_ae_roi_right_, "color_ae_roi_right", -1); setAndGetNodeParameter(color_ae_roi_bottom_, "color_ae_roi_bottom", -1); setAndGetNodeParameter(color_exposure_, "color_exposure", -1); setAndGetNodeParameter(color_gain_, "color_gain", -1); setAndGetNodeParameter(color_mjpeg_quality_, "color_mjpeg_quality", -1); setAndGetNodeParameter(color_white_balance_, "color_white_balance", -1); setAndGetNodeParameter(color_ae_max_exposure_, "color_ae_max_exposure", -1); setAndGetNodeParameter(color_ae_max_gain_, "color_ae_max_gain", -1); setAndGetNodeParameter(color_brightness_, "color_brightness", -1); setAndGetNodeParameter(color_roi_brightness_, "color_roi_brightness", -1); setAndGetNodeParameter(color_sharpness_, "color_sharpness", -1); setAndGetNodeParameter(color_gamma_, "color_gamma", -1); setAndGetNodeParameter(color_saturation_, "color_saturation", -1); setAndGetNodeParameter(color_contrast_, "color_contrast", -1); setAndGetNodeParameter(color_hue_, "color_hue", -1); setAndGetNodeParameter(color_backlight_compensation_, "color_backlight_compensation", -1); setAndGetNodeParameter(color_anti_flicker_, "color_anti_flicker", false); setAndGetNodeParameter(color_denoising_level_, "color_denoising_level", -1); setAndGetNodeParameter(color_powerline_freq_, "color_powerline_freq", ""); setAndGetNodeParameter(color_preset_, "color_preset", ""); setAndGetNodeParameter(enable_color_decimation_filter_, "enable_color_decimation_filter", false); setAndGetNodeParameter(color_decimation_filter_scale_, "color_decimation_filter_scale", -1); setAndGetNodeParameter(enable_left_color_decimation_filter_, "enable_left_color_decimation_filter", false); setAndGetNodeParameter(left_color_decimation_filter_scale_, "left_color_decimation_filter_scale", -1); setAndGetNodeParameter(enable_right_color_decimation_filter_, "enable_right_color_decimation_filter", false); setAndGetNodeParameter(right_color_decimation_filter_scale_, "right_color_decimation_filter_scale", -1); setAndGetNodeParameter(enable_depth_auto_exposure_priority_, "enable_depth_auto_exposure_priority", false); setAndGetNodeParameter(depth_ae_roi_left_, "depth_ae_roi_left", -1); setAndGetNodeParameter(depth_ae_roi_top_, "depth_ae_roi_top", -1); setAndGetNodeParameter(depth_ae_roi_right_, "depth_ae_roi_right", -1); setAndGetNodeParameter(depth_ae_roi_bottom_, "depth_ae_roi_bottom", -1); setAndGetNodeParameter(depth_exposure_, "depth_exposure", -1); setAndGetNodeParameter(depth_gain_, "depth_gain", -1); setAndGetNodeParameter(depth_brightness_, "depth_brightness", -1); setAndGetNodeParameter(mean_intensity_set_point_, "mean_intensity_set_point", depth_brightness_); setAndGetNodeParameter(depth_precision_str_, "depth_precision", ""); setAndGetNodeParameter(enable_ir_auto_exposure_, "enable_ir_auto_exposure", true); setAndGetNodeParameter(ir_exposure_, "ir_exposure", -1); setAndGetNodeParameter(ir_gain_, "ir_gain", -1); setAndGetNodeParameter(ir_ae_max_exposure_, "ir_ae_max_exposure", -1); setAndGetNodeParameter(ir_brightness_, "ir_brightness", -1); setAndGetNodeParameter(enable_ir_long_exposure_, "enable_ir_long_exposure", true); setAndGetNodeParameter(enable_right_ir_sequence_id_filter_, "enable_right_ir_sequence_id_filter", false); setAndGetNodeParameter(right_ir_sequence_id_filter_id_, "right_ir_sequence_id_filter_id", -1); setAndGetNodeParameter(enable_left_ir_sequence_id_filter_, "enable_left_ir_sequence_id_filter", false); setAndGetNodeParameter(left_ir_sequence_id_filter_id_, "left_ir_sequence_id_filter_id", -1); setAndGetNodeParameter(preset_resolution_config_, "preset_resolution_config", ""); setAndGetNodeParameter(sync_mode_str_, "sync_mode", ""); setAndGetNodeParameter(depth_delay_us_, "depth_delay_us", 0); setAndGetNodeParameter(color_delay_us_, "color_delay_us", 0); setAndGetNodeParameter(trigger2image_delay_us_, "trigger2image_delay_us", 0); setAndGetNodeParameter(trigger_out_delay_us_, "trigger_out_delay_us", 0); setAndGetNodeParameter(trigger_out_enabled_, "trigger_out_enabled", true); setAndGetNodeParameter(software_trigger_enabled_, "software_trigger_enabled", true); setAndGetNodeParameter(enable_ptp_config_, "enable_ptp_config", false); setAndGetNodeParameter(cloud_frame_id_, "cloud_frame_id", ""); if (enable_colored_point_cloud_ || enable_d2c_viewer_) { depth_registration_ = true; } if (!enable_stream_[COLOR]) { enable_colored_point_cloud_ = false; depth_registration_ = false; } setAndGetNodeParameter(enable_ldp_, "enable_ldp", true); setAndGetNodeParameter(ldp_power_level_, "ldp_power_level", -1); setAndGetNodeParameter(linear_accel_cov_, "linear_accel_cov", 0.0003); setAndGetNodeParameter(angular_vel_cov_, "angular_vel_cov", 0.02); setAndGetNodeParameter(ordered_pc_, "ordered_pc", false); setAndGetNodeParameter(max_save_images_count_, "max_save_images_count", 10); setAndGetNodeParameter(enable_depth_scale_, "enable_depth_scale", true); setAndGetNodeParameter(depth_decimation_factor_, "depth_decimation_factor", 1); setAndGetNodeParameter(left_ir_decimation_factor_, "left_ir_decimation_factor", 1); setAndGetNodeParameter(right_ir_decimation_factor_, "right_ir_decimation_factor", 1); setAndGetNodeParameter(depth_work_mode_, "depth_work_mode", ""); if (isDepthWorkModeDevices(device_->getDeviceInfo()->getPid())) { setAndGetNodeParameter(depth_work_mode_, "device_preset", ""); } else { setAndGetNodeParameter(device_preset_, "device_preset", ""); } setAndGetNodeParameter(enable_decimation_filter_, "enable_decimation_filter", false); setAndGetNodeParameter(enable_hdr_merge_, "enable_hdr_merge", false); setAndGetNodeParameter(enable_sequence_id_filter_, "enable_sequence_id_filter", false); setAndGetNodeParameter(enable_disparity_to_depth_, "enable_disparity_to_depth", true); setAndGetNodeParameter(enable_threshold_filter_, "enable_threshold_filter", false); setAndGetNodeParameter(enable_hardware_noise_removal_filter_, "enable_hardware_noise_removal_filter", true); setAndGetNodeParameter(enable_noise_removal_filter_, "enable_noise_removal_filter", true); setAndGetNodeParameter(enable_spatial_filter_, "enable_spatial_filter", false); setAndGetNodeParameter(enable_temporal_filter_, "enable_temporal_filter", false); setAndGetNodeParameter(enable_hole_filling_filter_, "enable_hole_filling_filter", false); setAndGetNodeParameter(enable_edge_noise_removal_filter_, "enable_edge_noise_removal_filter", false); setAndGetNodeParameter(enable_spatial_fast_filter_, "enable_spatial_fast_filter", false); setAndGetNodeParameter(enable_spatial_moderate_filter_, "enable_spatial_moderate_filter", false); setAndGetNodeParameter(enable_mgc_noise_removal_filter_, "enable_mgc_noise_removal_filter", false); setAndGetNodeParameter(enable_lut_noise_removal_filter_, "enable_lut_noise_removal_filter", false); setAndGetNodeParameter(enable_disp_outliers_filter_, "enable_disp_outliers_filter", false); std::string disp_outliers_filter_search_mode; setAndGetNodeParameter(disp_outliers_filter_search_mode, "disp_outliers_filter_search_mode", ""); std::string disp_outliers_filter_search_mode_message; if (!parseDispOutliersSearchMode(disp_outliers_filter_search_mode, disp_outliers_filter_search_mode_, disp_outliers_filter_search_mode_message, true)) { RCLCPP_WARN_STREAM(logger_, "Invalid disp_outliers_filter_search_mode value " << disp_outliers_filter_search_mode << ". " << disp_outliers_filter_search_mode_message << "; using empty"); disp_outliers_filter_search_mode_ = -1; } setAndGetNodeParameter(decimation_filter_scale_, "decimation_filter_scale", -1); setAndGetNodeParameter(sequence_id_filter_id_, "sequence_id_filter_id", -1); setAndGetNodeParameter(threshold_filter_max_, "threshold_filter_max", -1); setAndGetNodeParameter(threshold_filter_min_, "threshold_filter_min", -1); setAndGetNodeParameter(hardware_noise_removal_filter_threshold_, "hardware_noise_removal_filter_threshold", -1.0); setAndGetNodeParameter(noise_removal_filter_min_diff_, "noise_removal_filter_min_diff", -1); setAndGetNodeParameter(noise_removal_filter_max_size_, "noise_removal_filter_max_size", -1); setAndGetNodeParameter(spatial_filter_alpha_, "spatial_filter_alpha", -1.0); setAndGetNodeParameter(spatial_filter_diff_threshold_, "spatial_filter_diff_threshold", -1); setAndGetNodeParameter(spatial_filter_magnitude_, "spatial_filter_magnitude", -1); setAndGetNodeParameter(spatial_filter_radius_, "spatial_filter_radius", -1); setAndGetNodeParameter(spatial_fast_filter_radius_, "spatial_fast_filter_radius", -1); setAndGetNodeParameter(spatial_moderate_filter_diff_threshold_, "spatial_moderate_filter_diff_threshold", -1); setAndGetNodeParameter(spatial_moderate_filter_magnitude_, "spatial_moderate_filter_magnitude", -1); setAndGetNodeParameter(spatial_moderate_filter_radius_, "spatial_moderate_filter_radius", -1); setAndGetNodeParameter(temporal_filter_diff_threshold_, "temporal_filter_diff_threshold", -1.0); setAndGetNodeParameter(temporal_filter_weight_, "temporal_filter_weight", -1.0); setAndGetNodeParameter(hole_filling_filter_mode_, "hole_filling_filter_mode", ""); setAndGetNodeParameter(enable_false_positive_filter_, "enable_false_positive_filter", false); setAndGetNodeParameter(hdr_merge_exposure_1_, "hdr_merge_exposure_1", -1); setAndGetNodeParameter(hdr_merge_gain_1_, "hdr_merge_gain_1", -1); setAndGetNodeParameter(hdr_merge_exposure_2_, "hdr_merge_exposure_2", -1); setAndGetNodeParameter(hdr_merge_gain_2_, "hdr_merge_gain_2", -1); setAndGetNodeParameter(align_mode_, "align_mode", "HW"); align_mode_ = normalizeClosedSetParameterValue(logger_, "align_mode", align_mode_, {"HW", "SW"}, "HW"); setAndGetNodeParameter(diagnostic_period_, "diagnostic_period", 0.0); setAndGetNodeParameter(enable_laser_, "enable_laser", true); std::string align_target_stream_str_; setAndGetNodeParameter(align_target_stream_str_, "align_target_stream", "COLOR"); align_target_stream_str_ = normalizeClosedSetParameterValue( logger_, "align_target_stream", align_target_stream_str_, {"COLOR", "DEPTH"}, "COLOR"); if (depth_registration_ && align_mode_ == "HW" && align_target_stream_str_ != "COLOR") { RCLCPP_ERROR_STREAM(logger_, "HW D2C only supports COLOR as align_target_stream; got " << align_target_stream_str_ << ". Skip setting and use 'COLOR'. Use align_mode:=SW to align to " "DEPTH."); align_target_stream_str_ = "COLOR"; } align_target_stream_ = obStreamTypeFromString(align_target_stream_str_); setAndGetNodeParameter(retry_on_usb3_detection_failure_, "retry_on_usb3_detection_failure", false); setAndGetNodeParameter(laser_energy_level_, "laser_energy_level", -1); setAndGetNodeParameter(min_depth_limit_, "min_depth_limit", 0); setAndGetNodeParameter(max_depth_limit_, "max_depth_limit", 0); setAndGetNodeParameter(enable_heartbeat_, "enable_heartbeat", false); setAndGetNodeParameter(enable_firmware_log_, "enable_firmware_log", false); setAndGetNodeParameter(enable_fps_boost_, "enable_fps_boost", false); setAndGetNodeParameter(time_domain_, "time_domain", "global"); time_domain_ = normalizeClosedSetParameterValue(logger_, "time_domain", time_domain_, {"global", "device", "system"}, "global"); setAndGetNodeParameter(enable_frame_drop_log_, "enable_frame_drop_log", false); setAndGetNodeParameter(frame_timestamp_csv_file_, "frame_timestamp_csv_file", ""); setAndGetNodeParameter(exposure_range_mode_, "exposure_range_mode", ""); setAndGetNodeParameter(load_config_json_file_path_, "load_config_json_file_path", ""); setAndGetNodeParameter(export_config_json_file_path_, "export_config_json_file_path", ""); setAndGetNodeParameter(enable_accel_data_correction_, "enable_accel_data_correction", true); setAndGetNodeParameter(enable_gyro_data_correction_, "enable_gyro_data_correction", true); auto device_info = device_->getDeviceInfo(); CHECK_NOTNULL(device_info.get()); if (device_preset_ == "Dual Color Streams") { RCLCPP_INFO_STREAM(logger_, "Using Double Color preset, only left and right color streams are enabled."); enable_stream_[COLOR] = false; enable_stream_[DEPTH] = false; enable_stream_[INFRA0] = false; enable_stream_[INFRA1] = false; enable_stream_[INFRA2] = false; enable_stream_[LIDAR] = false; enable_stream_[COLOR_LEFT] = true; enable_stream_[COLOR_RIGHT] = true; enable_point_cloud_ = false; enable_colored_point_cloud_ = false; depth_registration_ = false; enable_d2c_viewer_ = false; enable_depth_filter_ = false; enable_undistortion_[COLOR] = false; } if (isOpenNIDevice(pid_)) { time_domain_ = "system"; } if (time_domain_ == "global" && !is_playback_device_) { device_->enableGlobalTimestamp(true); } setAndGetNodeParameter(frames_per_trigger_, "frames_per_trigger", 1); int software_trigger_period = 33; setAndGetNodeParameter(software_trigger_period, "software_trigger_period", 33); software_trigger_period_ = std::chrono::milliseconds(software_trigger_period); setAndGetNodeParameter(gmsl_trigger_fps_, "gmsl_trigger_fps", 3000); setAndGetNodeParameter(enable_gmsl_trigger_, "enable_gmsl_trigger", false); setAndGetNodeParameter(interleave_ae_mode_, "interleave_ae_mode", ""); setAndGetNodeParameter(interleave_frame_enable_, "interleave_frame_enable", false); setAndGetNodeParameter(interleave_skip_enable_, "interleave_skip_enable", false); setAndGetNodeParameter(interleave_skip_index_, "interleave_skip_index", -1); // hdr and laser interleave params setAndGetNodeParameter(hdr_index1_laser_control_, "hdr_index1_laser_control", -1); setAndGetNodeParameter(hdr_index1_depth_exposure_, "hdr_index1_depth_exposure", -1); setAndGetNodeParameter(hdr_index1_depth_gain_, "hdr_index1_depth_gain", -1); setAndGetNodeParameter(hdr_index1_ir_brightness_, "hdr_index1_ir_brightness", -1); setAndGetNodeParameter(hdr_index1_ir_ae_max_exposure_, "hdr_index1_ir_ae_max_exposure", -1); setAndGetNodeParameter(hdr_index0_laser_control_, "hdr_index0_laser_control", -1); setAndGetNodeParameter(hdr_index0_depth_exposure_, "hdr_index0_depth_exposure", -1); setAndGetNodeParameter(hdr_index0_depth_gain_, "hdr_index0_depth_gain", -1); setAndGetNodeParameter(hdr_index0_ir_brightness_, "hdr_index0_ir_brightness", -1); setAndGetNodeParameter(hdr_index0_ir_ae_max_exposure_, "hdr_index0_ir_ae_max_exposure", -1); setAndGetNodeParameter(laser_index1_laser_control_, "laser_index1_laser_control", -1); setAndGetNodeParameter(laser_index1_depth_exposure_, "laser_index1_depth_exposure", -1); setAndGetNodeParameter(laser_index1_depth_gain_, "laser_index1_depth_gain", -1); setAndGetNodeParameter(laser_index1_ir_brightness_, "laser_index1_ir_brightness", -1); setAndGetNodeParameter(laser_index1_ir_ae_max_exposure_, "laser_index1_ir_ae_max_exposure", -1); setAndGetNodeParameter(laser_index0_laser_control_, "laser_index0_laser_control", -1); setAndGetNodeParameter(laser_index0_depth_exposure_, "laser_index0_depth_exposure", -1); setAndGetNodeParameter(laser_index0_depth_gain_, "laser_index0_depth_gain", -1); setAndGetNodeParameter(laser_index0_ir_brightness_, "laser_index0_ir_brightness", -1); setAndGetNodeParameter(laser_index0_ir_ae_max_exposure_, "laser_index0_ir_ae_max_exposure", -1); setAndGetNodeParameter(disparity_range_mode_, "disparity_range_mode", -1); setAndGetNodeParameter(disparity_search_offset_, "disparity_search_offset", -1); setAndGetNodeParameter(disparity_offset_config_, "disparity_offset_config", false); setAndGetNodeParameter(offset_index0_, "offset_index0", -1); setAndGetNodeParameter(offset_index1_, "offset_index1", -1); setAndGetNodeParameter(sync_io_voltage_level_, "sync_io_voltage_level", -1); setAndGetNodeParameter(frame_aggregate_mode_, "frame_aggregate_mode", "ANY"); frame_aggregate_mode_ = normalizeClosedSetParameterValue(logger_, "frame_aggregate_mode", frame_aggregate_mode_, {"full_frame", "color_frame", "ANY", "disable"}, "ANY"); setAndGetNodeParameter(show_fps_enable_, "show_fps_enable", false); setAndGetNodeParameter(enable_publish_extrinsic_, "enable_publish_extrinsic", false); setAndGetNodeParameter(enable_lrm_obstacle_distance_publish_, "enable_lrm_obstacle_distance_publish", false); setAndGetNodeParameter(lrm_obstacle_distance_publish_rate_, "lrm_obstacle_distance_publish_rate", 10.0); if (enable_lrm_obstacle_distance_publish_ && !enable_ldp_) { RCLCPP_INFO_STREAM(logger_, "enable_lrm_obstacle_distance_publish is true, enabling LDP"); enable_ldp_ = true; } if (lrm_obstacle_distance_publish_rate_ <= 0.0) { RCLCPP_WARN_STREAM(logger_, "Invalid lrm_obstacle_distance_publish_rate " << lrm_obstacle_distance_publish_rate_ << ", using 10.0 Hz instead"); lrm_obstacle_distance_publish_rate_ = 10.0; } setAndGetNodeParameter(intra_camera_sync_reference_, "intra_camera_sync_reference", "Middle"); setAndGetNodeParameter(ae_reference_stream_, "ae_reference_stream", ""); setAndGetNodeParameter(ae_strategy_, "ae_strategy", ""); RCLCPP_INFO_STREAM(logger_, "Current time domain: " << time_domain_); RCLCPP_DEBUG_STREAM(logger_, "hdr_index1_laser_control_ " << hdr_index1_laser_control_ << " hdr_index1_depth_exposure_ " << hdr_index1_depth_exposure_ << " hdr_index1_depth_gain_ " << hdr_index1_depth_gain_ << " hdr_index1_ir_brightness_ " << hdr_index1_ir_brightness_ << " hdr_index1_ir_ae_max_exposure_ " << hdr_index1_ir_ae_max_exposure_ << "\n"); RCLCPP_DEBUG_STREAM(logger_, "hdr_index0_laser_control_ " << hdr_index0_laser_control_ << " hdr_index0_depth_exposure_ " << hdr_index0_depth_exposure_ << " hdr_index0_depth_gain_ " << hdr_index0_depth_gain_ << " hdr_index0_ir_brightness_ " << hdr_index0_ir_brightness_ << " hdr_index0_ir_ae_max_exposure_ " << hdr_index0_ir_ae_max_exposure_ << "\n"); RCLCPP_DEBUG_STREAM(logger_, "laser_index1_laser_control_ " << laser_index1_laser_control_ << " laser_index1_depth_exposure_ " << laser_index1_depth_exposure_ << " laser_index1_depth_gain_ " << laser_index1_depth_gain_ << " laser_index1_ir_brightness_ " << laser_index1_ir_brightness_ << " laser_index1_ir_ae_max_exposure_ " << laser_index1_ir_ae_max_exposure_ << "\n"); RCLCPP_DEBUG_STREAM(logger_, "laser_index0_laser_control_ " << laser_index0_laser_control_ << " laser_index0_depth_exposure_ " << laser_index0_depth_exposure_ << " laser_index0_depth_gain_ " << laser_index0_depth_gain_ << " laser_index0_ir_brightness_ " << laser_index0_ir_brightness_ << " laser_index0_ir_ae_max_exposure_ " << laser_index0_ir_ae_max_exposure_ << "\n"); } void OBCameraNode::setupTopics() { try { captureInitialRosParameters(); getParameters(); loadConfigJson(); syncConfigJsonApplicationConfig(); setupDevices(); setupDepthPostProcessFilter(); setupColorPostProcessFilter(); setupIrPostProcessFilter(); setupRightIrPostProcessFilter(); setupLeftIrPostProcessFilter(); setupUndistortionFilters(); syncConfigJsonDeviceSettings(); syncConfigJsonFilterSettings(depth_filter_list_, "depth"); syncConfigJsonFilterSettings(color_filter_list_, "color"); syncConfigJsonFilterSettings(left_color_filter_list_, "left_color"); syncConfigJsonFilterSettings(right_color_filter_list_, "right_color"); syncConfigJsonFilterSettings(left_ir_filter_list_, "left_ir"); syncConfigJsonFilterSettings(right_ir_filter_list_, "right_ir"); setupProfiles(); if (enable_enhanced_depth_.load()) { std::string message; if (!ensureEnhancedDepthFilter(message)) { throw std::runtime_error(message); } } setupCameraInfo(); selectBaseStream(); setupCameraCtrlServices(); setupPublishers(); setupDiagnosticUpdater(); exportConfigJsonIfRequested(); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to setup topics: " << orbbec_camera::formatObErrorWithStatus(e)); throw std::runtime_error(orbbec_camera::formatObErrorWithStatus(e)); } catch (const std::exception &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to setup topics: " << e.what()); throw std::runtime_error(e.what()); } catch (...) { RCLCPP_ERROR(logger_, "Failed to setup topics"); throw std::runtime_error("Failed to setup topics"); } } void OBCameraNode::onTemperatureUpdate(diagnostic_updater::DiagnosticStatusWrapper &status) { try { // Check to ensure we're not shutting down and device is valid if (!is_running_.load() || !is_camera_node_initialized_.load()) { status.summary(diagnostic_msgs::msg::DiagnosticStatus::STALE, "Device disconnected or shutting down"); return; } // Try to acquire device lock with timeout to avoid blocking during shutdown std::unique_lock lock(device_lock_, std::try_to_lock); if (!lock.owns_lock()) { status.summary(diagnostic_msgs::msg::DiagnosticStatus::STALE, "Device busy or shutting down"); return; } if (!device_) { status.summary(diagnostic_msgs::msg::DiagnosticStatus::STALE, "Device not available"); return; } // Additional safety check - verify device is actually accessible try { auto device_info = device_->getDeviceInfo(); if (!device_info) { status.summary(diagnostic_msgs::msg::DiagnosticStatus::STALE, "Device info not available"); return; } } catch (...) { status.summary(diagnostic_msgs::msg::DiagnosticStatus::STALE, "Device not accessible"); return; } OBDeviceTemperature temperature; uint32_t data_size = sizeof(OBDeviceTemperature); device_->getStructuredData(OB_STRUCT_DEVICE_TEMPERATURE, reinterpret_cast(&temperature), &data_size); status.add("CPU Temperature", temperature.cpuTemp); status.add("IR Temperature", temperature.irTemp); status.add("LDM Temperature", temperature.ldmTemp); status.add("MainBoard Temperature", temperature.mainBoardTemp); status.add("TEC Temperature", temperature.tecTemp); status.add("IMU Temperature", temperature.imuTemp); status.add("RGB Temperature", temperature.rgbTemp); status.add("Left IR Temperature", temperature.irLeftTemp); status.add("Right IR Temperature", temperature.irRightTemp); status.add("Chip Top Temperature", temperature.chipTopTemp); status.add("Chip Bottom Temperature", temperature.chipBottomTemp); status.summary(diagnostic_msgs::msg::DiagnosticStatus::OK, "Temperature is normal"); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM( logger_, "Failed to TemperatureUpdate1: " << orbbec_camera::formatObErrorWithStatus(e)); status.summary(diagnostic_msgs::msg::DiagnosticStatus::ERROR, orbbec_camera::formatObErrorWithStatus(e)); } catch (const std::exception &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to TemperatureUpdate2: " << e.what()); status.summary(diagnostic_msgs::msg::DiagnosticStatus::ERROR, e.what()); } catch (...) { RCLCPP_ERROR(logger_, "Failed to TemperatureUpdate3: Device is deactivated/disconnected!"); status.summary(diagnostic_msgs::msg::DiagnosticStatus::ERROR, "Unknown error"); } } void OBCameraNode::setupDiagnosticUpdater() { if (diagnostic_period_ <= 0.0 || is_playback_device_) { // Temperature reporting requires live hardware monitoring that doesn't // exist for a recorded .bag file; skip to avoid repeated SDK errors. return; } try { RCLCPP_INFO_STREAM(logger_, "Publish diagnostics every " << diagnostic_period_ << " seconds"); auto info = device_->getDeviceInfo(); std::string serial_number = info->getSerialNumber(); diagnostic_updater_ = std::make_unique(node_, 10000.0); diagnostic_updater_->setHardwareID(serial_number); diagnostic_updater_->add("Temperatures", this, &OBCameraNode::onTemperatureUpdate); diagnostic_timer_ = node_->create_wall_timer(std::chrono::seconds(int(diagnostic_period_)), [this]() { try { // Check if we're still running and all components are valid if (!is_running_.load() || !diagnostic_updater_ || !is_camera_node_initialized_.load() || !device_) { return; } // Try to acquire device lock with timeout to avoid blocking during shutdown std::unique_lock lock(device_lock_, std::try_to_lock); if (!lock.owns_lock()) { // Device is busy or shutting down, skip this update return; } // Mark diagnostic as running to prevent concurrent reset/publish races { std::lock_guard lk(diagnostic_mutex_); diagnostic_running_ = true; } try { diagnostic_updater_->force_update(); } catch (...) { std::lock_guard lk(diagnostic_mutex_); diagnostic_running_ = false; diagnostic_cv_.notify_all(); throw; } { std::lock_guard lk(diagnostic_mutex_); diagnostic_running_ = false; } diagnostic_cv_.notify_all(); } catch (const ob::Error &e) { RCLCPP_WARN_STREAM( logger_, "Diagnostic update failed: " << orbbec_camera::formatObErrorWithStatus(e) << " - Device may be disconnected"); // Stop the diagnostic timer if device is having issues try { if (diagnostic_timer_) { diagnostic_timer_->cancel(); diagnostic_timer_.reset(); } } catch (...) { // Ignore cleanup exceptions } } catch (const std::exception &e) { RCLCPP_WARN_STREAM(logger_, "Diagnostic update failed: " << e.what()); } catch (...) { RCLCPP_WARN(logger_, "Diagnostic update failed: Unknown error"); } }); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to setup diagnostic updater: " << orbbec_camera::formatObErrorWithStatus(e)); } catch (const std::exception &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to setup diagnostic updater: " << e.what()); } catch (...) { RCLCPP_ERROR(logger_, "Failed to TemperatureUpdate"); } } void OBCameraNode::setupPipelineConfig() { if (pipeline_config_) { pipeline_config_.reset(); } pipeline_config_ = std::make_shared(); auto device_info = device_->getDeviceInfo(); CHECK_NOTNULL(device_info.get()); if (depth_registration_ && enable_stream_[COLOR] && enable_stream_[DEPTH] && align_mode_ == "HW") { OBAlignMode align_mode = ALIGN_D2C_HW_MODE; RCLCPP_INFO_STREAM(logger_, "set align mode to " << align_mode_); pipeline_config_->setAlignMode(align_mode); RCLCPP_INFO_STREAM(logger_, "enable depth scale " << (enable_depth_scale_ ? "ON" : "OFF")); pipeline_config_->setDepthScaleRequire(enable_depth_scale_); } for (const auto &stream_index : IMAGE_STREAMS) { if (enable_stream_[stream_index]) { RCLCPP_DEBUG_STREAM(logger_, "Enable " << stream_name_[stream_index] << " stream"); auto profile = stream_profile_[stream_index]->as(); if (stream_index == COLOR && align_target_stream_ == OB_STREAM_COLOR && align_filter_) { auto video_profile = profile; align_filter_->setAlignToStreamProfile(video_profile); } if (stream_index == DEPTH && align_target_stream_ == OB_STREAM_DEPTH && align_filter_) { auto video_profile = profile; align_filter_->setAlignToStreamProfile(video_profile); } pipeline_config_->enableStream(stream_profile_[stream_index]); } } if (frame_aggregate_mode_ == "full_frame") { pipeline_config_->setFrameAggregateOutputMode(OB_FRAME_AGGREGATE_OUTPUT_FULL_FRAME_REQUIRE); } else if (frame_aggregate_mode_ == "color_frame") { pipeline_config_->setFrameAggregateOutputMode(OB_FRAME_AGGREGATE_OUTPUT_COLOR_FRAME_REQUIRE); } else if (frame_aggregate_mode_ == "disable") { pipeline_config_->setFrameAggregateOutputMode(OB_FRAME_AGGREGATE_OUTPUT_DISABLE); } else { pipeline_config_->setFrameAggregateOutputMode(OB_FRAME_AGGREGATE_OUTPUT_ANY_SITUATION); } } bool OBCameraNode::validateEnhancedDepthFilterConfig(std::string &message) const { if (!enable_stream_.count(COLOR) || !enable_stream_.at(COLOR) || !enable_stream_.count(DEPTH) || !enable_stream_.at(DEPTH)) { message = "Enhanced depth filter requires color and depth streams"; return false; } if (!depth_registration_) { message = "Enhanced depth filter requires D2C/C2D align mode"; return false; } if (align_mode_ != "HW" && align_mode_ != "SW") { message = "Enhanced depth filter requires D2C/C2D align mode"; return false; } if (align_mode_ == "HW" && align_target_stream_ != OB_STREAM_COLOR) { message = "Enhanced depth filter requires HW D2C align target COLOR"; return false; } if (align_mode_ == "SW" && align_target_stream_ != OB_STREAM_COLOR && align_target_stream_ != OB_STREAM_DEPTH) { message = "Enhanced depth filter requires D2C/C2D align mode"; return false; } auto color_it = stream_profile_.find(COLOR); auto depth_it = stream_profile_.find(DEPTH); if (color_it == stream_profile_.end() || !color_it->second || depth_it == stream_profile_.end() || !depth_it->second) { message = "Enhanced depth filter requires color and depth stream profiles"; return false; } auto color_profile = color_it->second->as(); auto depth_profile = depth_it->second->as(); if (!color_profile || !depth_profile) { message = "Enhanced depth filter requires video stream profiles"; return false; } const bool d2c = align_mode_ == "HW" || align_target_stream_ == OB_STREAM_COLOR; const OBStreamType align_to_stream = d2c ? OB_STREAM_COLOR : OB_STREAM_DEPTH; if (!ob::EnhancedDepthFilter::isSupportedResolution(color_profile->getType(), align_to_stream, color_profile->getWidth(), color_profile->getHeight())) { message = std::string("Enhanced depth filter requires supported target resolutions: ") + kEnhancedDepthSupportedTargetResolutions; return false; } if (!ob::EnhancedDepthFilter::isSupportedResolution(depth_profile->getType(), align_to_stream, depth_profile->getWidth(), depth_profile->getHeight()) || !ob::EnhancedDepthFilter::isSupportedFormat(OB_STREAM_DEPTH, depth_profile->getFormat())) { message = std::string("Enhanced depth filter requires supported target resolutions: ") + kEnhancedDepthSupportedTargetResolutions + " and depth formats: " + kEnhancedDepthSupportedDepthFormats; return false; } if (!isEnhancedDepthColorFormatSupported(color_profile->getFormat())) { message = "Unsupported color stream format for enhanced depth filter. Supported formats are: YUYV " "UYVY MJPG BGR RGBA Y16 Y8 RGB"; return false; } return true; } void OBCameraNode::applyEnhancedDepthConfidenceThreshold() { if (!enhanced_depth_filter_ || enhanced_depth_confidence_threshold_ < 0) { return; } auto range = enhanced_depth_filter_->getConfidenceThresholdRange(); const int confidence_threshold = enhanced_depth_confidence_threshold_; if (confidence_threshold < range.min || confidence_threshold > range.max) { std::ostringstream ss; ss << "Enhanced depth confidence threshold is out of range " << range.min << " - " << range.max; throw std::runtime_error(ss.str()); } enhanced_depth_filter_->setConfidenceThreshold(static_cast(confidence_threshold)); } bool OBCameraNode::ensureEnhancedDepthFilter(std::string &message) { if (!validateEnhancedDepthFilterConfig(message)) { return false; } std::lock_guard lock(enhanced_depth_filter_mutex_); try { if (!enhanced_depth_filter_) { if (enhanced_depth_model_path_.empty()) { message = "Enhanced depth filter requires enhanced_depth_model_path"; return false; } std::ifstream model_file(enhanced_depth_model_path_); if (!model_file.good()) { message = "Enhanced depth model file not found: " + enhanced_depth_model_path_; return false; } if (!device_->isLicenseAuthorizationSupported()) { message = "Enhanced depth filter requires device license authorization support"; return false; } auto license_info = device_->readLicenseInfo(); RCLCPP_INFO_STREAM(logger_, "Enhanced depth license info: " << license_info); if (license_info.empty()) { message = "Enhanced depth filter requires device license info"; return false; } RCLCPP_INFO_STREAM(logger_, "Creating enhanced depth filter with model path: " << enhanced_depth_model_path_); enhanced_depth_filter_ = std::make_shared(device_, enhanced_depth_model_path_); } const bool d2c = align_mode_ == "HW" || align_target_stream_ == OB_STREAM_COLOR; auto target_profile = d2c ? stream_profile_.at(COLOR)->as() : stream_profile_.at(DEPTH)->as(); enhanced_depth_filter_->setResolution(target_profile->getWidth(), target_profile->getHeight()); applyEnhancedDepthConfidenceThreshold(); setupConfidencePublishers(); } catch (const ob::Error &e) { message = "Failed to create enhanced depth filter: " + orbbec_camera::formatObErrorWithStatus(e); return false; } catch (const std::exception &e) { message = "Failed to create enhanced depth filter: " + std::string(e.what()); return false; } return true; } bool OBCameraNode::convertEnhancedDepthColorFrame(const std::shared_ptr &frame_set) { auto color_frame = frame_set ? frame_set->getFrame(OB_FRAME_COLOR) : nullptr; if (!color_frame) { return false; } const auto color_format = color_frame->getFormat(); if (color_format == OB_FORMAT_RGB) { return true; } const auto &format_map = enhancedDepthColorFormatMap(); auto it = format_map.find(color_format); if (it == format_map.end()) { RCLCPP_ERROR_STREAM_THROTTLE( logger_, *node_->get_clock(), 1000, "Unsupported color stream format for enhanced depth filter: " << color_format); return false; } try { enhanced_depth_format_convert_filter_.setFormatConvertType(it->second); auto converted = enhanced_depth_format_convert_filter_.process(color_frame); if (!converted) { RCLCPP_ERROR_THROTTLE(logger_, *node_->get_clock(), 1000, "Enhanced depth color format conversion failed"); return false; } frame_set->pushFrame(converted); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM_THROTTLE(logger_, *node_->get_clock(), 1000, "Enhanced depth color format conversion failed: " << orbbec_camera::formatObErrorWithStatus(e)); return false; } catch (const std::exception &e) { RCLCPP_ERROR_STREAM_THROTTLE(logger_, *node_->get_clock(), 1000, "Enhanced depth color format conversion failed: " << e.what()); return false; } return true; } std::shared_ptr OBCameraNode::processEnhancedDepthFilter( const std::shared_ptr &frame_set) { if (!frame_set) { return frame_set; } { std::lock_guard lock(enhanced_depth_filter_mutex_); if (!enable_enhanced_depth_.load()) { return frame_set; } } std::string message; if (!ensureEnhancedDepthFilter(message)) { RCLCPP_ERROR_STREAM_THROTTLE(logger_, *node_->get_clock(), 1000, message); return frame_set; } auto original_color_frame = frame_set->getFrame(OB_FRAME_COLOR); if (!original_color_frame || !frame_set->getFrame(OB_FRAME_DEPTH)) { RCLCPP_ERROR_THROTTLE(logger_, *node_->get_clock(), 1000, "Enhanced depth filter requires color and depth frames"); return frame_set; } std::shared_ptr filter; { std::lock_guard lock(enhanced_depth_filter_mutex_); if (!enable_enhanced_depth_.load()) { return frame_set; } filter = enhanced_depth_filter_; } try { // Convert color only in the filter input so downstream publishers keep the configured format. auto cloned_frame_set = ob::FrameFactory::createFrameFromOtherFrame(frame_set, false); if (!cloned_frame_set || !cloned_frame_set->is()) { RCLCPP_ERROR_THROTTLE(logger_, *node_->get_clock(), 1000, "Failed to clone frameset for enhanced depth filter"); return frame_set; } auto filter_frame_set = cloned_frame_set->as(); if (!convertEnhancedDepthColorFrame(filter_frame_set)) { return frame_set; } auto processed = filter->process(filter_frame_set); if (!processed || !processed->is()) { RCLCPP_ERROR_THROTTLE(logger_, *node_->get_clock(), 1000, "Enhanced depth filter returned invalid frameset"); return frame_set; } auto processed_frame_set = processed->as(); processed_frame_set->pushFrame(original_color_frame); publishConfidenceFrame(processed_frame_set->getFrame(OB_FRAME_CONFIDENCE)); return processed_frame_set; } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM_THROTTLE( logger_, *node_->get_clock(), 1000, "Enhanced depth filter failed: " << orbbec_camera::formatObErrorWithStatus(e)); } catch (const std::exception &e) { RCLCPP_ERROR_STREAM_THROTTLE(logger_, *node_->get_clock(), 1000, "Enhanced depth filter failed: " << e.what()); } return frame_set; } void OBCameraNode::setupConfidencePublishers() { if (confidence_image_publisher_) { return; } auto image_qos_profile = getRMWQosProfileFromString(image_qos_[DEPTH]); if (use_intra_process_) { image_qos_profile = rmw_qos_profile_default; } confidence_image_publisher_ = node_->create_publisher( "confidence/image_raw", rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(image_qos_profile), image_qos_profile)); } void OBCameraNode::publishConfidenceFrame(const std::shared_ptr &confidence_frame) { if (!confidence_frame || !confidence_frame->is()) { return; } setupConfidencePublishers(); if (!confidence_image_publisher_ || confidence_image_publisher_->get_subscription_count() == 0) { return; } auto video_frame = confidence_frame->as(); const int width = static_cast(video_frame->getWidth()); const int height = static_cast(video_frame->getHeight()); int image_type = CV_8UC1; std::string encoding = sensor_msgs::image_encodings::MONO8; int unit_step_size = sizeof(uint8_t); if (confidence_frame->getFormat() == OB_FORMAT_Y16) { image_type = CV_16UC1; encoding = sensor_msgs::image_encodings::MONO16; unit_step_size = sizeof(uint16_t); } else if (confidence_frame->getFormat() != OB_FORMAT_Y8) { RCLCPP_ERROR_STREAM_THROTTLE( logger_, *node_->get_clock(), 1000, "Unsupported confidence frame format: " << confidence_frame->getFormat()); return; } if (confidence_image_.empty() || confidence_image_.cols != width || confidence_image_.rows != height || confidence_image_.type() != image_type) { confidence_image_.create(height, width, image_type); } memcpy(confidence_image_.data, video_frame->getData(), video_frame->getDataSize()); auto timestamp = fromUsToROSTime(getFrameTimestampUs(confidence_frame)); std::string frame_id = depth_registration_ ? depth_aligned_frame_id_[DEPTH] : optical_frame_id_[DEPTH]; sensor_msgs::msg::Image::UniquePtr image_msg(new sensor_msgs::msg::Image()); cv_bridge::CvImage(std_msgs::msg::Header(), encoding, confidence_image_).toImageMsg(*image_msg); image_msg->header.stamp = timestamp; image_msg->header.frame_id = frame_id; image_msg->is_bigendian = false; image_msg->step = width * unit_step_size; confidence_image_publisher_->publish(std::move(image_msg)); } void OBCameraNode::setupCameraInfo() { const auto create_camera_info_manager = [this](const std::string &camera_name, const std::string &camera_info_url) { #ifdef ORBBEC_CAMERA_INFO_MANAGER_USES_NODE_INTERFACES #ifdef ORBBEC_CAMERA_INFO_MANAGER_USES_RCLCPP_QOS return std::make_unique( node_->get_node_base_interface(), node_->get_node_services_interface(), node_->get_node_logging_interface(), camera_name, camera_info_url, rclcpp::SystemDefaultsQoS()); #else return std::make_unique( node_->get_node_base_interface(), node_->get_node_services_interface(), node_->get_node_logging_interface(), camera_name, camera_info_url, rmw_qos_profile_default); #endif #else return std::make_unique(node_, camera_name, camera_info_url); #endif }; const std::string color_camera_name = camera_name_ + "_color"; if (!color_info_url_.empty()) { color_info_manager_ = create_camera_info_manager(color_camera_name, color_info_url_); } const std::string ir_camera_name = camera_name_ + "_ir"; if (!ir_info_url_.empty()) { ir_info_manager_ = create_camera_info_manager(ir_camera_name, ir_info_url_); } } void OBCameraNode::setupImagePublisher(const stream_index_pair &stream_index) { if (!enable_stream_[stream_index]) { image_publishers_.erase(stream_index); compressed_image_publishers_.erase(stream_index); return; } const std::string topic = stream_name_[stream_index] + "/image_raw"; auto image_qos_profile = getRMWQosProfileFromString(image_qos_[stream_index]); if (use_intra_process_) { image_qos_profile = rmw_qos_profile_default; } const bool is_mjpg_color_stream = (stream_index == COLOR || stream_index == COLOR_LEFT || stream_index == COLOR_RIGHT) && format_[stream_index] == OB_FORMAT_MJPG; if (use_intra_process_ || is_mjpg_color_stream) { image_publishers_[stream_index] = std::make_shared(*node_, topic, image_qos_profile); } else { image_publishers_[stream_index] = std::make_shared(*node_, topic, image_qos_profile); } if (is_mjpg_color_stream) { compressed_image_publishers_[stream_index] = node_->create_publisher( topic + "/compressed", rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(image_qos_profile), image_qos_profile)); } else { compressed_image_publishers_.erase(stream_index); } } void OBCameraNode::setupPublishers() { using PointCloud2 = sensor_msgs::msg::PointCloud2; using CameraInfo = sensor_msgs::msg::CameraInfo; auto point_cloud_qos_profile = getRMWQosProfileFromString(point_cloud_qos_); if (use_intra_process_) { point_cloud_qos_profile = rmw_qos_profile_default; } if (enable_colored_point_cloud_) { depth_registration_cloud_pub_ = node_->create_publisher( "depth_registered/points", rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(point_cloud_qos_profile), point_cloud_qos_profile)); } if (enable_point_cloud_) { depth_cloud_pub_ = node_->create_publisher( "depth/points", rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(point_cloud_qos_profile), point_cloud_qos_profile)); } auto device_info = device_->getDeviceInfo(); CHECK_NOTNULL(device_info.get()); for (const auto &stream_index : IMAGE_STREAMS) { if (!enable_stream_[stream_index]) { continue; } std::string name = stream_name_[stream_index]; setupImagePublisher(stream_index); std::string topic = name + "/camera_info"; auto camera_info_qos = camera_info_qos_[stream_index]; auto camera_info_qos_profile = getRMWQosProfileFromString(camera_info_qos); if (use_intra_process_) { camera_info_qos_profile = rmw_qos_profile_default; } camera_info_publishers_[stream_index] = node_->create_publisher( topic, rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(camera_info_qos_profile), camera_info_qos_profile)); if (isPublishMetaData(pid_)) { metadata_publishers_[stream_index] = node_->create_publisher( name + "/metadata", rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(camera_info_qos_profile), camera_info_qos_profile)); } } syncSoftwareAlignment(); if (enable_sync_output_accel_gyro_) { std::string topic_name = stream_name_[GYRO] + "_" + stream_name_[ACCEL] + "/sample"; auto data_qos = getRMWQosProfileFromString(imu_qos_[GYRO]); if (use_intra_process_) { data_qos = rmw_qos_profile_default; } imu_gyro_accel_publisher_ = node_->create_publisher( topic_name, rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(data_qos), data_qos)); topic_name = stream_name_[GYRO] + "/imu_info"; imu_info_publishers_[GYRO] = node_->create_publisher( topic_name, rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(data_qos), data_qos)); topic_name = stream_name_[ACCEL] + "/imu_info"; imu_info_publishers_[ACCEL] = node_->create_publisher( topic_name, rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(data_qos), data_qos)); } else { for (const auto &stream_index : HID_STREAMS) { if (!enable_stream_[stream_index]) { continue; } std::string data_topic_name = stream_name_[stream_index] + "/sample"; auto data_qos = getRMWQosProfileFromString(imu_qos_[stream_index]); if (use_intra_process_) { data_qos = rmw_qos_profile_default; } imu_publishers_[stream_index] = node_->create_publisher( data_topic_name, rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(data_qos), data_qos)); data_topic_name = stream_name_[stream_index] + "/imu_info"; imu_info_publishers_[stream_index] = node_->create_publisher( data_topic_name, rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(data_qos), data_qos)); } } auto extrinsics_qos = rclcpp::QoS(1).transient_local(); if (use_intra_process_) { extrinsics_qos = rclcpp::QoS(1); } if (enable_stream_[DEPTH] && enable_stream_[INFRA0] && enable_publish_extrinsic_) { depth_to_other_extrinsics_publishers_[INFRA0] = node_->create_publisher("depth_to_ir", extrinsics_qos); } if (enable_stream_[DEPTH] && enable_stream_[COLOR] && enable_publish_extrinsic_) { depth_to_other_extrinsics_publishers_[COLOR] = node_->create_publisher("depth_to_color", extrinsics_qos); } if (enable_stream_[DEPTH] && enable_stream_[INFRA1] && enable_publish_extrinsic_) { depth_to_other_extrinsics_publishers_[INFRA1] = node_->create_publisher("depth_to_left_ir", extrinsics_qos); } if (enable_stream_[DEPTH] && enable_stream_[INFRA2] && enable_publish_extrinsic_) { depth_to_other_extrinsics_publishers_[INFRA2] = node_->create_publisher("depth_to_right_ir", extrinsics_qos); } if (enable_stream_[DEPTH] && enable_stream_[ACCEL] && enable_publish_extrinsic_) { depth_to_other_extrinsics_publishers_[ACCEL] = node_->create_publisher("depth_to_accel", extrinsics_qos); } if (enable_stream_[DEPTH] && enable_stream_[GYRO] && enable_publish_extrinsic_) { depth_to_other_extrinsics_publishers_[GYRO] = node_->create_publisher("depth_to_gyro", extrinsics_qos); } if (enable_stream_[COLOR_LEFT] && enable_stream_[COLOR_RIGHT] && enable_publish_extrinsic_) { depth_to_other_extrinsics_publishers_[COLOR_LEFT] = node_->create_publisher("left_color_to_right_color", extrinsics_qos); } depth_filters_status_pub_ = node_->create_publisher("depth_filters/status", extrinsics_qos); publishDepthFiltersStatus(); if (enable_lrm_obstacle_distance_publish_) { lrm_obstacle_distance_pub_ = node_->create_publisher("lrm/obstacle_distance", rclcpp::QoS(10)); RCLCPP_INFO_STREAM(logger_, "Publishing LRM obstacle distance on lrm/obstacle_distance at " << lrm_obstacle_distance_publish_rate_ << " Hz"); auto publish_period = std::chrono::duration_cast( std::chrono::duration(1.0 / lrm_obstacle_distance_publish_rate_)); if (publish_period < std::chrono::milliseconds(1)) { publish_period = std::chrono::milliseconds(1); } lrm_obstacle_distance_timer_ = node_->create_wall_timer(publish_period, [this]() { publishLrmObstacleDistance(); }); } } void OBCameraNode::syncSoftwareAlignment() { if (depth_registration_ && align_mode_ == "SW") { if (!align_filter_) { align_filter_ = std::make_unique(align_target_stream_); RCLCPP_INFO_STREAM(logger_, "set align mode to " << align_mode_); } if (!depth_unaligned_publisher_) { auto depth_image_qos_profile = getRMWQosProfileFromString(image_qos_[DEPTH]); if (use_intra_process_) { depth_image_qos_profile = rmw_qos_profile_default; } if (use_intra_process_) { depth_unaligned_publisher_ = std::make_shared( *node_, "depth/image_unaligned", depth_image_qos_profile); } else { depth_unaligned_publisher_ = std::make_shared( *node_, "depth/image_unaligned", depth_image_qos_profile); } } return; } align_filter_.reset(); depth_unaligned_publisher_.reset(); } void OBCameraNode::publishPointCloud(const std::shared_ptr &frame_set) { try { if (depth_registration_ || enable_colored_point_cloud_) { if (frame_set->depthFrame() != nullptr && frame_set->colorFrame() != nullptr) { publishColoredPointCloud(frame_set); } } if (enable_point_cloud_ && frame_set->depthFrame() != nullptr) { publishDepthPointCloud(frame_set); } } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, orbbec_camera::formatObErrorWithStatus(e)); } catch (const std::exception &e) { RCLCPP_ERROR_STREAM(logger_, e.what()); } catch (...) { RCLCPP_ERROR_STREAM(logger_, "publishPointCloud with unknown error"); } } void OBCameraNode::publishRawDepthImage(const std::shared_ptr &depth_frame) { if (!depth_frame || !depth_unaligned_publisher_ || !depth_registration_ || depth_unaligned_publisher_->get_subscription_count() == 0) { return; } auto video_frame = depth_frame->as(); if (!video_frame) { return; } int width = static_cast(video_frame->getWidth()); int height = static_cast(video_frame->getHeight()); auto frame_timestamp = getFrameTimestampUs(depth_frame); auto timestamp = fromUsToROSTime(frame_timestamp); std::string frame_id = optical_frame_id_[DEPTH]; cv::Mat depth_image(height, width, image_format_[DEPTH]); memcpy(depth_image.data, video_frame->getData(), video_frame->getDataSize()); auto depth_scale = video_frame->getValueScale(); depth_image.convertTo(depth_image, depth_image.type(), depth_scale); sensor_msgs::msg::Image::UniquePtr image_msg(new sensor_msgs::msg::Image()); cv_bridge::CvImage(std_msgs::msg::Header(), encoding_[DEPTH], depth_image).toImageMsg(*image_msg); image_msg->header.stamp = timestamp; image_msg->is_bigendian = false; image_msg->step = width * unit_step_size_[DEPTH]; image_msg->header.frame_id = frame_id; depth_unaligned_publisher_->publish(std::move(image_msg)); } void OBCameraNode::publishDepthPointCloud(const std::shared_ptr &frame_set) { if (!depth_cloud_pub_ || depth_cloud_pub_->get_subscription_count() == 0 || !enable_point_cloud_) { return; } std::lock_guard point_cloud_msg_lock(point_cloud_mutex_); auto depth_frame = frame_set->depthFrame(); if (!depth_frame) { RCLCPP_ERROR_STREAM(logger_, "depth frame is null"); return; } if (!pipeline_) { RCLCPP_ERROR_STREAM(logger_, "pipeline is null in publishDepthPointCloud"); return; } auto camera_params = pipeline_->getCameraParam(); if (!device_) { RCLCPP_ERROR_STREAM(logger_, "device is null in publishDepthPointCloud"); return; } auto device_info = device_->getDeviceInfo(); if (!device_info || !device_info.get()) { RCLCPP_ERROR_STREAM(logger_, "device_info is null in publishDepthPointCloud"); return; } if (depth_registration_ || pid_ == DABAI_MAX_PID) { camera_params.depthIntrinsic = camera_params.rgbIntrinsic; } depth_point_cloud_filter_.setCameraParam(camera_params); float depth_scale = depth_frame->getValueScale(); depth_point_cloud_filter_.setPositionDataScaled(depth_scale); depth_point_cloud_filter_.setCreatePointFormat(OB_FORMAT_POINT); depth_point_cloud_filter_.setDecimationFactor(point_cloud_decimation_filter_factor_); auto result_frame = depth_point_cloud_filter_.process(depth_frame); if (!result_frame) { RCLCPP_ERROR_STREAM(logger_, "Failed to process depth frame"); return; } auto point_size = result_frame->dataSize() / sizeof(OBPoint); auto *points = static_cast(result_frame->data()); auto width = depth_frame->width() / point_cloud_decimation_filter_factor_; auto height = depth_frame->height() / point_cloud_decimation_filter_factor_; auto point_cloud_msg = std::make_unique(); sensor_msgs::PointCloud2Modifier modifier(*point_cloud_msg); modifier.setPointCloud2FieldsByString(1, "xyz"); modifier.resize(width * height); point_cloud_msg->width = width; point_cloud_msg->height = height; point_cloud_msg->row_step = point_cloud_msg->width * point_cloud_msg->point_step; point_cloud_msg->data.resize(point_cloud_msg->height * point_cloud_msg->row_step); sensor_msgs::PointCloud2Iterator iter_x(*point_cloud_msg, "x"); sensor_msgs::PointCloud2Iterator iter_y(*point_cloud_msg, "y"); sensor_msgs::PointCloud2Iterator iter_z(*point_cloud_msg, "z"); const static float MIN_DISTANCE = 20.0; // 2cm const static float MAX_DISTANCE = 10000.0; // 10m const static float min_depth = MIN_DISTANCE / depth_scale; const static float max_depth = MAX_DISTANCE / depth_scale; size_t valid_count = 0; for (size_t i = 0; i < point_size; i++) { bool valid_point = points[i].z >= min_depth && points[i].z <= max_depth; if (valid_point || ordered_pc_) { *iter_x = static_cast(points[i].x / 1000.0); *iter_y = static_cast(points[i].y / 1000.0); *iter_z = static_cast(points[i].z / 1000.0); ++iter_x, ++iter_y, ++iter_z; valid_count++; } } if (valid_count == 0) { RCLCPP_WARN(logger_, "No valid point in point cloud"); return; } if (!ordered_pc_) { point_cloud_msg->is_dense = true; point_cloud_msg->width = valid_count; point_cloud_msg->height = 1; modifier.resize(valid_count); point_cloud_msg->row_step = point_cloud_msg->width * point_cloud_msg->point_step; } auto frame_timestamp = getFrameTimestampUs(depth_frame); auto timestamp = fromUsToROSTime(frame_timestamp); std::string frame_id = depth_registration_ ? optical_frame_id_[COLOR] : optical_frame_id_[DEPTH]; if (!cloud_frame_id_.empty()) { frame_id = cloud_frame_id_; } point_cloud_msg->header.stamp = timestamp; point_cloud_msg->header.frame_id = frame_id; if (save_point_cloud_) { save_point_cloud_ = false; auto now = std::time(nullptr); std::stringstream ss; ss << std::put_time(std::localtime(&now), "%Y%m%d_%H%M%S"); auto current_path = std::filesystem::current_path().string(); std::string filename = current_path + "/point_cloud/points_" + ss.str() + ".ply"; if (!std::filesystem::exists(current_path + "/point_cloud")) { std::filesystem::create_directory(current_path + "/point_cloud"); } RCLCPP_INFO_STREAM(logger_, "Saving point cloud to " << filename); try { saveDepthPointsToPly(point_cloud_msg, filename); } catch (const std::exception &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to save point cloud: " << e.what()); } } depth_cloud_pub_->publish(std::move(point_cloud_msg)); } void OBCameraNode::publishColoredPointCloud(const std::shared_ptr &frame_set) { if (!depth_registration_cloud_pub_ || depth_registration_cloud_pub_->get_subscription_count() == 0 || !enable_colored_point_cloud_ || !frame_set) { return; } std::lock_guard point_cloud_msg_lock(point_cloud_mutex_); auto depth_frame = frame_set->depthFrame(); auto color_frame = frame_set->colorFrame(); if (!depth_frame || !color_frame) { return; } auto depth_width = depth_frame->getWidth(); auto depth_height = depth_frame->getHeight(); auto color_width = color_frame->getWidth(); auto color_height = color_frame->getHeight(); if (depth_width != color_width || depth_height != color_height) { RCLCPP_DEBUG(logger_, "Depth (%d x %d) and color (%d x %d) frame size mismatch", depth_width, depth_height, color_width, color_height); return; } if (!pipeline_) { RCLCPP_ERROR_STREAM(logger_, "pipeline is null in publishColoredPointCloud"); return; } auto camera_params = pipeline_->getCameraParam(); if (!device_) { RCLCPP_ERROR_STREAM(logger_, "device is null in publishColoredPointCloud"); return; } auto device_info = device_->getDeviceInfo(); if (!device_info || !device_info.get()) { RCLCPP_ERROR_STREAM(logger_, "device_info is null in publishColoredPointCloud"); return; } if (depth_registration_ || pid_ == DABAI_MAX_PID) { camera_params.depthIntrinsic = camera_params.rgbIntrinsic; } color_point_cloud_filter_.setCameraParam(camera_params); auto depth_scale = depth_frame->getValueScale(); color_point_cloud_filter_.setPositionDataScaled(depth_scale); color_point_cloud_filter_.setCreatePointFormat(OB_FORMAT_RGB_POINT); color_point_cloud_filter_.setDecimationFactor(point_cloud_decimation_filter_factor_); auto result_frame = color_point_cloud_filter_.process(frame_set); if (!result_frame) { RCLCPP_ERROR_STREAM(logger_, "Failed to process depth frame"); return; } auto point_size = result_frame->dataSize() / sizeof(OBColorPoint); auto *point_cloud = static_cast(result_frame->data()); auto width = color_frame->getWidth() / point_cloud_decimation_filter_factor_; auto height = color_frame->getHeight() / point_cloud_decimation_filter_factor_; auto point_cloud_msg = std::make_unique(); sensor_msgs::PointCloud2Modifier modifier(*point_cloud_msg); modifier.setPointCloud2FieldsByString(1, "xyz"); modifier.resize(width * height); point_cloud_msg->width = width; point_cloud_msg->height = height; std::string format_str = "rgb"; point_cloud_msg->point_step = addPointField(*point_cloud_msg, format_str, 1, sensor_msgs::msg::PointField::FLOAT32, static_cast(point_cloud_msg->point_step)); point_cloud_msg->row_step = point_cloud_msg->width * point_cloud_msg->point_step; point_cloud_msg->data.resize(point_cloud_msg->height * point_cloud_msg->row_step); sensor_msgs::PointCloud2Iterator iter_x(*point_cloud_msg, "x"); sensor_msgs::PointCloud2Iterator iter_y(*point_cloud_msg, "y"); sensor_msgs::PointCloud2Iterator iter_z(*point_cloud_msg, "z"); sensor_msgs::PointCloud2Iterator iter_r(*point_cloud_msg, "r"); sensor_msgs::PointCloud2Iterator iter_g(*point_cloud_msg, "g"); sensor_msgs::PointCloud2Iterator iter_b(*point_cloud_msg, "b"); size_t valid_count = 0; static const float MIN_DISTANCE = 20.0; static const float MAX_DISTANCE = 10000.0; static float min_depth = MIN_DISTANCE / depth_scale; static float max_depth = MAX_DISTANCE / depth_scale; for (size_t i = 0; i < point_size; i++) { bool valid_point = point_cloud[i].z >= min_depth && point_cloud[i].z <= max_depth; if (valid_point || ordered_pc_) { *iter_x = static_cast(point_cloud[i].x / 1000.0); *iter_y = static_cast(point_cloud[i].y / 1000.0); *iter_z = static_cast(point_cloud[i].z / 1000.0); *iter_r = static_cast(point_cloud[i].r); *iter_g = static_cast(point_cloud[i].g); *iter_b = static_cast(point_cloud[i].b); ++iter_x, ++iter_y, ++iter_z, ++iter_r, ++iter_g, ++iter_b; ++valid_count; } } if (valid_count == 0) { RCLCPP_WARN(logger_, "No valid points in point cloud"); return; } if (!ordered_pc_) { point_cloud_msg->is_dense = true; point_cloud_msg->width = valid_count; point_cloud_msg->height = 1; modifier.resize(valid_count); point_cloud_msg->row_step = point_cloud_msg->width * point_cloud_msg->point_step; } auto frame_timestamp = getFrameTimestampUs(depth_frame); std::string frame_id = optical_frame_id_[COLOR]; if (!cloud_frame_id_.empty()) { frame_id = cloud_frame_id_; } auto timestamp = fromUsToROSTime(frame_timestamp); point_cloud_msg->header.stamp = timestamp; point_cloud_msg->header.frame_id = frame_id; if (save_colored_point_cloud_) { save_colored_point_cloud_ = false; auto now = std::time(nullptr); std::stringstream ss; ss << std::put_time(std::localtime(&now), "%Y%m%d_%H%M%S"); auto current_path = std::filesystem::current_path().string(); std::string filename = current_path + "/point_cloud/colored_points_" + ss.str() + ".ply"; if (!std::filesystem::exists(current_path + "/point_cloud")) { std::filesystem::create_directory(current_path + "/point_cloud"); } RCLCPP_INFO_STREAM(logger_, "Saving point cloud to " << filename); try { saveRGBPointCloudMsgToPly(point_cloud_msg, filename); } catch (const std::exception &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to save point cloud: " << e.what()); } catch (...) { RCLCPP_ERROR(logger_, "Failed to save point cloud"); } } depth_registration_cloud_pub_->publish(std::move(point_cloud_msg)); } std::shared_ptr OBCameraNode::processIrFrameFilter(std::shared_ptr &frame) { if (frame == nullptr || frame->getType() != OB_FRAME_IR) { return nullptr; } for (size_t i = 0; i < ir_filter_list_.size(); i++) { auto filter = ir_filter_list_[i]; CHECK_NOTNULL(filter.get()); if (filter->isEnabled() && frame != nullptr) { frame = filter->process(frame); if (frame == nullptr) { RCLCPP_ERROR_STREAM(logger_, "Ir filter process failed"); break; } } } return frame; } std::shared_ptr OBCameraNode::processRightIrFrameFilter( std::shared_ptr &frame) { if (frame == nullptr || frame->getType() != OB_FRAME_IR_RIGHT) { return nullptr; } for (size_t i = 0; i < right_ir_filter_list_.size(); i++) { auto filter = right_ir_filter_list_[i]; CHECK_NOTNULL(filter.get()); if (filter->isEnabled() && frame != nullptr) { frame = filter->process(frame); if (frame == nullptr) { RCLCPP_ERROR_STREAM(logger_, "Right Ir filter process failed"); break; } } } return frame; } std::shared_ptr OBCameraNode::processLeftIrFrameFilter( std::shared_ptr &frame) { if (frame == nullptr || frame->getType() != OB_FRAME_IR_LEFT) { return nullptr; } for (size_t i = 0; i < left_ir_filter_list_.size(); i++) { auto filter = left_ir_filter_list_[i]; CHECK_NOTNULL(filter.get()); if (filter->isEnabled() && frame != nullptr) { frame = filter->process(frame); if (frame == nullptr) { RCLCPP_ERROR_STREAM(logger_, "Left Ir filter process failed"); break; } } } return frame; } std::shared_ptr OBCameraNode::processColorFrameFilter( std::shared_ptr &frame) { if (frame == nullptr) { return nullptr; } auto frame_type = frame->getType(); if (frame_type == OB_FRAME_COLOR) { for (size_t i = 0; i < color_filter_list_.size(); i++) { auto filter = color_filter_list_[i]; CHECK_NOTNULL(filter.get()); if (filter->isEnabled() && frame != nullptr) { frame = filter->process(frame); if (frame == nullptr) { RCLCPP_ERROR_STREAM(logger_, "Color filter process failed"); break; } } } return frame; } else if (frame_type == OB_FRAME_COLOR_LEFT) { for (size_t i = 0; i < left_color_filter_list_.size(); i++) { auto filter = left_color_filter_list_[i]; CHECK_NOTNULL(filter.get()); if (filter->isEnabled() && frame != nullptr) { frame = filter->process(frame); if (frame == nullptr) { RCLCPP_ERROR_STREAM(logger_, "Left color filter process failed"); break; } } } return frame; } else if (frame_type == OB_FRAME_COLOR_RIGHT) { for (size_t i = 0; i < right_color_filter_list_.size(); i++) { auto filter = right_color_filter_list_[i]; CHECK_NOTNULL(filter.get()); if (filter->isEnabled() && frame != nullptr) { frame = filter->process(frame); if (frame == nullptr) { RCLCPP_ERROR_STREAM(logger_, "Right color filter process failed"); break; } } } return frame; } return nullptr; } std::shared_ptr OBCameraNode::processDepthFrameFilter( std::shared_ptr &frame) { if (frame == nullptr || frame->getType() != OB_FRAME_DEPTH) { return nullptr; } std::lock_guard depth_filter_lock(depth_filter_mutex_); for (size_t i = 0; i < depth_filter_list_.size(); i++) { auto filter = depth_filter_list_[i]; CHECK_NOTNULL(filter.get()); if (filter->isEnabled() && frame != nullptr) { frame = filter->process(frame); if (frame == nullptr) { RCLCPP_WARN_STREAM(logger_, "Depth filter process failed, frame is null"); break; } } } return frame; } void OBCameraNode::setDisparitySearchOffset() { static bool has_run = false; auto config = OBDispOffsetConfig(); if (has_run) { return; } if (device_->isPropertySupported(OB_PROP_DISP_SEARCH_OFFSET_INT, OB_PERMISSION_WRITE)) { if (disparity_search_offset_ >= 0 && disparity_search_offset_ <= 127) { device_->setIntProperty(OB_PROP_DISP_SEARCH_OFFSET_INT, disparity_search_offset_); RCLCPP_INFO_STREAM(logger_, "Set disparity search offset to " << disparity_search_offset_); } if (offset_index0_ >= 0 && offset_index0_ <= 127 && offset_index1_ >= 0 && offset_index1_ <= 127) { config.enable = disparity_offset_config_; config.offset0 = offset_index0_; config.offset1 = offset_index1_; config.reserved = 0; device_->setStructuredData(OB_STRUCT_DISP_OFFSET_CONFIG, reinterpret_cast(&config), sizeof(config)); RCLCPP_INFO_STREAM(logger_, "disparity_offset_config: " << disparity_offset_config_ << " offset_index0:" << offset_index0_ << " offset_index1:" << offset_index1_); } } has_run = true; } void OBCameraNode::setDepthAutoExposureROI() { static bool depth_roi_has_run = false; if (depth_roi_has_run) { return; } if (isGemini305SeriesPID(pid_) && ae_reference_stream_ == "color") { RCLCPP_WARN_STREAM(logger_, "Skip setting depth AE ROI because AE Reference Stream is color"); depth_roi_has_run = true; return; } if (device_->isPropertySupported(OB_STRUCT_DEPTH_AE_ROI, OB_PERMISSION_READ_WRITE)) { auto config = OBRegionOfInterest(); uint32_t data_size = sizeof(config); device_->getStructuredData(OB_STRUCT_DEPTH_AE_ROI, reinterpret_cast(&config), &data_size); if (depth_ae_roi_left_ != -1) { config.x0_left = (depth_ae_roi_left_ < 0) ? 0 : depth_ae_roi_left_; config.x0_left = (depth_ae_roi_left_ > width_[DEPTH] - 1) ? width_[DEPTH] - 1 : config.x0_left; } if (depth_ae_roi_top_ != -1) { config.y0_top = (depth_ae_roi_top_ < 0) ? 0 : depth_ae_roi_top_; config.y0_top = (depth_ae_roi_top_ > height_[DEPTH] - 1) ? height_[DEPTH] - 1 : config.y0_top; } if (depth_ae_roi_right_ != -1) { config.x1_right = (depth_ae_roi_right_ < 0) ? 0 : depth_ae_roi_right_; config.x1_right = (depth_ae_roi_right_ > width_[DEPTH] - 1) ? width_[DEPTH] - 1 : config.x1_right; } if (depth_ae_roi_bottom_ != -1) { config.y1_bottom = (depth_ae_roi_bottom_ < 0) ? 0 : depth_ae_roi_bottom_; config.y1_bottom = (depth_ae_roi_bottom_ > height_[DEPTH] - 1) ? height_[DEPTH] - 1 : config.y1_bottom; } device_->setStructuredData(OB_STRUCT_DEPTH_AE_ROI, reinterpret_cast(&config), sizeof(config)); device_->getStructuredData(OB_STRUCT_DEPTH_AE_ROI, reinterpret_cast(&config), &data_size); RCLCPP_INFO_STREAM(logger_, "Set depth AE ROI to " << config.x0_left << ", " << config.x1_right << ", " << config.y0_top << ", " << config.y1_bottom); } depth_roi_has_run = true; } void OBCameraNode::setColorAutoExposureROI() { static bool color_roi_has_run = false; if (color_roi_has_run) { return; } if (isGemini305SeriesPID(pid_) && ae_reference_stream_ == "depth") { RCLCPP_WARN_STREAM(logger_, "Skip setting color AE ROI because AE Reference Stream is depth"); color_roi_has_run = true; return; } if (device_->isPropertySupported(OB_STRUCT_COLOR_AE_ROI, OB_PERMISSION_READ_WRITE)) { auto config = OBRegionOfInterest(); uint32_t data_size = sizeof(config); device_->getStructuredData(OB_STRUCT_COLOR_AE_ROI, reinterpret_cast(&config), &data_size); if (color_ae_roi_left_ != -1) { config.x0_left = (color_ae_roi_left_ < 0) ? 0 : color_ae_roi_left_; config.x0_left = (color_ae_roi_left_ > width_[COLOR] - 1) ? width_[COLOR] - 1 : config.x0_left; } if (color_ae_roi_top_ != -1) { config.y0_top = (color_ae_roi_top_ < 0) ? 0 : color_ae_roi_top_; config.y0_top = (color_ae_roi_top_ > height_[COLOR] - 1) ? height_[COLOR] - 1 : config.y0_top; } if (color_ae_roi_right_ != -1) { config.x1_right = (color_ae_roi_right_ < 0) ? 0 : color_ae_roi_right_; config.x1_right = (color_ae_roi_right_ > width_[COLOR] - 1) ? width_[COLOR] - 1 : config.x1_right; } if (color_ae_roi_bottom_ != -1) { config.y1_bottom = (color_ae_roi_bottom_ < 0) ? 0 : color_ae_roi_bottom_; config.y1_bottom = (color_ae_roi_bottom_ > height_[COLOR] - 1) ? height_[COLOR] - 1 : config.y1_bottom; } device_->setStructuredData(OB_STRUCT_COLOR_AE_ROI, reinterpret_cast(&config), sizeof(config)); device_->getStructuredData(OB_STRUCT_COLOR_AE_ROI, reinterpret_cast(&config), &data_size); RCLCPP_INFO_STREAM(logger_, "Set color AE ROI to " << config.x0_left << ", " << config.x1_right << ", " << config.y0_top << ", " << config.y1_bottom); } color_roi_has_run = true; } uint64_t OBCameraNode::getFrameTimestampUs(const std::shared_ptr &frame) { if (frame == nullptr) { RCLCPP_WARN(logger_, "getFrameTimestampUs: frame is nullptr, return 0"); return 0; } if (time_domain_ == "device") { return frame->getTimeStampUs(); } else if (time_domain_ == "global") { return frame->getGlobalTimeStampUs(); } else { return frame->getSystemTimeStampUs(); } } void OBCameraNode::onNewFrameSetCallback(std::shared_ptr frame_set) { if (!is_running_.load()) { return; } if (!is_camera_node_initialized_.load()) { return; } if (frame_set == nullptr) { return; } if ((frame_timestamp_csv_logger_ && frame_timestamp_csv_logger_->enabled()) || (color_timestamp_csv_logger_ && color_timestamp_csv_logger_->enabled()) || (depth_timestamp_csv_logger_ && depth_timestamp_csv_logger_->enabled())) { const auto frame_set_arrival_system_us = getSystemNowUs(); const auto frame_set_arrival_steady_us = getSteadyNowUs(); auto final_color_frame = frame_set->getFrame(OB_FRAME_COLOR); auto final_depth_frame = frame_set->getFrame(OB_FRAME_DEPTH); const bool track_color = enable_stream_[COLOR] && static_cast(final_color_frame); const bool track_depth = enable_stream_[DEPTH] && static_cast(final_depth_frame); const bool color_publish_expected = track_color; const bool depth_publish_expected = track_depth; if (frame_timestamp_csv_logger_) { frame_timestamp_csv_logger_->recordFrameSet( final_color_frame, final_depth_frame, frame_set_arrival_system_us, frame_set_arrival_steady_us, track_color, track_depth, color_publish_expected, depth_publish_expected); } else { if (track_color && color_timestamp_csv_logger_) { color_timestamp_csv_logger_->recordStandaloneFrameArrival( OB_STREAM_COLOR, final_color_frame, frame_set_arrival_system_us, frame_set_arrival_steady_us, color_publish_expected); } if (track_depth && depth_timestamp_csv_logger_) { depth_timestamp_csv_logger_->recordStandaloneFrameArrival( OB_STREAM_DEPTH, final_depth_frame, frame_set_arrival_system_us, frame_set_arrival_steady_us, depth_publish_expected); } } } try { if (!tf_published_) { publishStaticTransforms(); tf_published_ = true; } auto device_info = device_->getDeviceInfo(); CHECK_NOTNULL(device_info); auto depth_frame = frame_set->getFrame(OB_FRAME_DEPTH); auto color_frame = frame_set->getFrame(OB_FRAME_COLOR); auto left_ir_frame = frame_set->getFrame(OB_FRAME_IR_LEFT); auto right_ir_frame = frame_set->getFrame(OB_FRAME_IR_RIGHT); auto left_color_frame = frame_set->getFrame(OB_FRAME_COLOR_LEFT); auto right_color_frame = frame_set->getFrame(OB_FRAME_COLOR_RIGHT); auto ir_frame = frame_set->getFrame(OB_FRAME_IR); auto depth_frame_for_hw_d2c_undistortion = depth_frame; if (depth_frame) { setDisparitySearchOffset(); setDepthAutoExposureROI(); depth_frame = processDepthFrameFilter(depth_frame); if (depth_frame) { frame_set->pushFrame(depth_frame); fps_counter_depth_->tick(); } } if (shouldUseHwD2CColorUndistortion() && color_frame) { applyHwD2CColorUndistortion(frame_set, depth_frame_for_hw_d2c_undistortion); depth_frame = frame_set->getFrame(OB_FRAME_DEPTH); color_frame = frame_set->getFrame(OB_FRAME_COLOR); } if (color_frame) { setColorAutoExposureROI(); color_frame = processColorFrameFilter(color_frame); frame_set->pushFrame(color_frame); fps_counter_color_->tick(); } if (left_color_frame) { setColorAutoExposureROI(); left_color_frame = processColorFrameFilter(left_color_frame); frame_set->pushFrame(left_color_frame); } if (right_color_frame) { right_color_frame = processColorFrameFilter(right_color_frame); frame_set->pushFrame(right_color_frame); } if (left_ir_frame) { left_ir_frame = processLeftIrFrameFilter(left_ir_frame); frame_set->pushFrame(left_ir_frame); fps_counter_left_ir_->tick(); } if (right_ir_frame) { right_ir_frame = processRightIrFrameFilter(right_ir_frame); frame_set->pushFrame(right_ir_frame); fps_counter_right_ir_->tick(); } if (ir_frame) { ir_frame = processIrFrameFilter(ir_frame); if (ir_frame) { frame_set->pushFrame(ir_frame); } } if (depth_registration_ && align_filter_ && depth_frame) { publishRawDepthImage(depth_frame); auto target_frame_type = STREAM_TYPE_TO_FRAME_TYPE.at(align_target_stream_); if (!frame_set->getFrame(target_frame_type) || !color_frame) { RCLCPP_DEBUG_STREAM( logger_, "Depth registration requires depth and color frames, skip software alignment"); } else { auto align_color_frame = color_frame; if (align_target_stream_ == OB_STREAM_DEPTH) { ob::FormatConvertFilter align_color_format_convert_filter; switch (color_frame->getFormat()) { case OB_FORMAT_YUYV: align_color_format_convert_filter.setFormatConvertType(FORMAT_YUYV_TO_RGB888); align_color_frame = align_color_format_convert_filter.process(color_frame); break; case OB_FORMAT_UYVY: align_color_format_convert_filter.setFormatConvertType(FORMAT_UYVY_TO_RGB888); align_color_frame = align_color_format_convert_filter.process(color_frame); break; case OB_FORMAT_MJPG: align_color_format_convert_filter.setFormatConvertType(FORMAT_MJPEG_TO_RGB888); align_color_frame = align_color_format_convert_filter.process(color_frame); break; default: break; } if (!align_color_frame) { RCLCPP_ERROR_STREAM(logger_, "Failed to convert color frame for C2D alignment"); } else if (align_color_frame != color_frame) { color_frame = align_color_frame; frame_set->pushFrame(color_frame); } } if (align_color_frame) { if (auto new_frame = align_filter_->process(frame_set)) { auto new_frame_set = new_frame->as(); CHECK_NOTNULL(new_frame_set.get()); frame_set = new_frame_set; depth_frame = frame_set->getFrame(OB_FRAME_DEPTH); color_frame = frame_set->getFrame(OB_FRAME_COLOR); } else { RCLCPP_ERROR(logger_, "Failed to align depth frame to color frame"); return; } } } } else { RCLCPP_DEBUG_ONCE(logger_, "Depth registration is disabled or align filter is null or depth frame is " "null or color frame is null"); } if (enable_enhanced_depth_.load()) { frame_set = processEnhancedDepthFilter(frame_set); depth_frame = frame_set->getFrame(OB_FRAME_DEPTH); color_frame = frame_set->getFrame(OB_FRAME_COLOR); } // Refresh frame from current frameset before logging to reflect post-filter/alignment output. for (const auto &stream_index : IMAGE_STREAMS) { if (!enable_stream_[stream_index]) { continue; } auto frame_type = STREAM_TYPE_TO_FRAME_TYPE.at(stream_index.first); auto updated_frame = frame_set->getFrame(frame_type); if (!updated_frame || !updated_frame->is()) { continue; } auto updated_video = updated_frame->as(); // For D2C, avoid logging an early unaligned depth frame before align target is available. if (stream_index == DEPTH && depth_registration_) { auto align_target_frame_type = STREAM_TYPE_TO_FRAME_TYPE.at(align_target_stream_); auto align_target_frame = frame_set->getFrame(align_target_frame_type); if (!align_target_frame || !align_target_frame->is()) { continue; } auto target_video = align_target_frame->as(); if (updated_video->getWidth() != target_video->getWidth() || updated_video->getHeight() != target_video->getHeight()) { continue; } } logFrameInfoOnce(stream_index, updated_video); } if (enable_stream_[COLOR] && color_frame) { std::unique_lock lock(color_frame_queue_lock_); color_frame_queue_.push(frame_set); color_frame_queue_cv_.notify_all(); } else { publishPointCloud(frame_set); } if (enable_stream_[COLOR_LEFT] && left_color_frame) { std::unique_lock lock(left_color_frame_queue_lock_); left_color_frame_queue_.push(frame_set); left_color_frame_queue_cv_.notify_all(); } if (enable_stream_[COLOR_RIGHT] && right_color_frame) { std::unique_lock lock(right_color_frame_queue_lock_); right_color_frame_queue_.push(frame_set); right_color_frame_queue_cv_.notify_all(); } for (const auto &stream_index : IMAGE_STREAMS) { if (enable_stream_[stream_index]) { auto frame_type = STREAM_TYPE_TO_FRAME_TYPE.at(stream_index.first); if (frame_type == OB_FRAME_COLOR || frame_type == OB_FRAME_COLOR_LEFT || frame_type == OB_FRAME_COLOR_RIGHT) { continue; } auto frame = frame_set->getFrame(frame_type); if (frame == nullptr) { continue; } onNewFrameCallback(frame, stream_index); } } } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM( logger_, "onNewFrameSetCallback error: " << orbbec_camera::formatObErrorWithStatus(e)); } catch (const std::exception &e) { RCLCPP_ERROR_STREAM(logger_, "onNewFrameSetCallback error: " << e.what()); } catch (...) { RCLCPP_ERROR_STREAM(logger_, "onNewFrameSetCallback error: unknown error"); } } void OBCameraNode::logFrameInfoOnce(const stream_index_pair &stream_index, const std::shared_ptr &video_frame) { if (!video_frame) { return; } { std::lock_guard lock(frame_info_logged_mutex_); auto iter = frame_info_logged_.find(stream_index); if (iter != frame_info_logged_.end() && iter->second) { return; } frame_info_logged_[stream_index] = true; } RCLCPP_INFO_STREAM(logger_, stream_name_[stream_index] << " Frame - Width: " << video_frame->getWidth() << " Height: " << video_frame->getHeight() << " fps: " << fps_[stream_index] << " Format: " << video_frame->getFormat()); } void OBCameraNode::onNewColorFrameCallback() { while (enable_stream_[COLOR] && rclcpp::ok() && is_running_.load() && !stop_color_frame_threads_.load()) { std::unique_lock lock(color_frame_queue_lock_); color_frame_queue_cv_.wait(lock, [this]() { return !color_frame_queue_.empty() || !(is_running_.load()) || stop_color_frame_threads_.load(); }); if (!rclcpp::ok() || !is_running_.load() || stop_color_frame_threads_.load()) { break; } std::shared_ptr frameSet = color_frame_queue_.front(); is_color_frame_decoded_ = decodeColorFrameToBuffer(frameSet->colorFrame(), rgb_buffer_); onNewFrameCallback(frameSet->colorFrame(), COLOR); publishPointCloud(frameSet); color_frame_queue_.pop(); } RCLCPP_DEBUG_STREAM(logger_, "Color frame thread exited"); } void OBCameraNode::onNewLeftColorFrameCallback() { while (enable_stream_[COLOR_LEFT] && rclcpp::ok() && is_running_.load() && !stop_color_frame_threads_.load()) { std::unique_lock lock(left_color_frame_queue_lock_); left_color_frame_queue_cv_.wait(lock, [this]() { return !left_color_frame_queue_.empty() || !(is_running_.load()) || stop_color_frame_threads_.load(); }); if (!rclcpp::ok() || !is_running_.load() || stop_color_frame_threads_.load()) { break; } std::shared_ptr frameSet = left_color_frame_queue_.front(); is_left_color_frame_decoded_ = decodeColorFrameToBuffer(frameSet->getFrame(OB_FRAME_COLOR_LEFT), rgb_buffer_left_); onNewFrameCallback(frameSet->getFrame(OB_FRAME_COLOR_LEFT), COLOR_LEFT); left_color_frame_queue_.pop(); } RCLCPP_DEBUG_STREAM(logger_, "Left color frame thread exited"); } void OBCameraNode::onNewRightColorFrameCallback() { while (enable_stream_[COLOR_RIGHT] && rclcpp::ok() && is_running_.load() && !stop_color_frame_threads_.load()) { std::unique_lock lock(right_color_frame_queue_lock_); right_color_frame_queue_cv_.wait(lock, [this]() { return !right_color_frame_queue_.empty() || !(is_running_.load()) || stop_color_frame_threads_.load(); }); if (!rclcpp::ok() || !is_running_.load() || stop_color_frame_threads_.load()) { break; } std::shared_ptr frameSet = right_color_frame_queue_.front(); is_right_color_frame_decoded_ = decodeColorFrameToBuffer(frameSet->getFrame(OB_FRAME_COLOR_RIGHT), rgb_buffer_right_); onNewFrameCallback(frameSet->getFrame(OB_FRAME_COLOR_RIGHT), COLOR_RIGHT); right_color_frame_queue_.pop(); } RCLCPP_DEBUG_STREAM(logger_, "Right color frame thread exited"); } std::shared_ptr OBCameraNode::softwareDecodeColorFrame( const std::shared_ptr &frame, const stream_index_pair &stream_index) { if (frame == nullptr) { return nullptr; } if (frame->getFormat() == OB_FORMAT_RGB || frame->getFormat() == OB_FORMAT_BGR) { return frame; } if (frame->getFormat() == OB_FORMAT_RGBA || frame->getFormat() == OB_FORMAT_BGRA) { return frame; } if (frame->getFormat() == OB_FORMAT_Y16 || frame->getFormat() == OB_FORMAT_Y8) { return frame; } ob::FormatConvertFilter *filter = &format_convert_filter_; if (stream_index == COLOR_LEFT) { filter = &format_convert_filter_left_; } else if (stream_index == COLOR_RIGHT) { filter = &format_convert_filter_right_; } if (!setupFormatConvertType(frame->getFormat(), *filter)) { RCLCPP_ERROR(logger_, "Unsupported color format: %d", frame->getFormat()); return nullptr; } std::shared_ptr color_frame; try { color_frame = filter->process(frame); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Format convert failed: " << orbbec_camera::formatObErrorWithStatus(e)); return nullptr; } catch (const std::exception &e) { RCLCPP_ERROR_STREAM(logger_, "Format convert failed: " << e.what()); return nullptr; } catch (...) { RCLCPP_ERROR(logger_, "Format convert failed: unknown error"); return nullptr; } if (color_frame == nullptr) { RCLCPP_ERROR_SKIPFIRST_THROTTLE(logger_, *(node_->get_clock()), 1000, "Failed to convert frame to RGB format"); return nullptr; } return color_frame; } bool OBCameraNode::isColorFrameDecodeRequired(const std::shared_ptr &frame) const { if (frame == nullptr) { return false; } const auto format = frame->getFormat(); if (format == OB_FORMAT_RGB || format == OB_FORMAT_BGR || format == OB_FORMAT_RGB888 || format == OB_FORMAT_RGBA || format == OB_FORMAT_BGRA || format == OB_FORMAT_Y16 || format == OB_FORMAT_Y8) { return false; } return true; } bool OBCameraNode::decodeColorFrameToBuffer(const std::shared_ptr &frame, uint8_t *buffer) { if (frame == nullptr) { return false; } if (!buffer) { return false; } if (!isColorFrameDecodeRequired(frame)) { return true; } stream_index_pair stream_index = COLOR; switch (frame->getType()) { case OB_FRAME_COLOR: stream_index = COLOR; break; case OB_FRAME_COLOR_LEFT: stream_index = COLOR_LEFT; break; case OB_FRAME_COLOR_RIGHT: stream_index = COLOR_RIGHT; break; default: stream_index = COLOR; break; } bool has_subscriber = false; if (image_publishers_.count(stream_index) && image_publishers_.at(stream_index)) { has_subscriber = image_publishers_.at(stream_index)->get_subscription_count() > 0; } if (save_images_[stream_index]) { has_subscriber = true; } if (frame->getType() == OB_FRAME_COLOR && enable_colored_point_cloud_ && depth_registration_cloud_pub_ && depth_registration_cloud_pub_->get_subscription_count() > 0) { has_subscriber = true; } if (!has_subscriber) { return false; } std::shared_ptr decoder; if (stream_index == COLOR_LEFT) { decoder = jpeg_decoder_left_; } else if (stream_index == COLOR_RIGHT) { decoder = jpeg_decoder_right_; } else { decoder = jpeg_decoder_; } bool is_decoded = false; if (!frame) { return false; } #if defined(USE_RK_HW_DECODER) || defined(USE_NV_HW_DECODER) if (frame && frame->getFormat() != OB_FORMAT_RGB888) { if (frame->getFormat() == OB_FORMAT_MJPG && decoder) { CHECK_NOTNULL(decoder.get()); CHECK_NOTNULL(buffer); auto video_frame = frame->as(); bool ret = false; if (video_frame && width_.count(stream_index) && height_.count(stream_index) && static_cast(video_frame->getWidth()) == width_[stream_index] && static_cast(video_frame->getHeight()) == height_[stream_index]) { ret = decoder->decode(video_frame, buffer); } if (!ret) { RCLCPP_ERROR_STREAM(logger_, "Decode frame failed"); is_decoded = false; } else { is_decoded = true; } } } #endif if (!is_decoded) { auto video_frame = softwareDecodeColorFrame(frame, stream_index); if (!video_frame) { RCLCPP_ERROR_STREAM(logger_, "Failed to convert frame to video frame"); return false; } uint8_t **target_buffer = nullptr; size_t *target_buffer_size = nullptr; if (stream_index == COLOR_LEFT) { target_buffer = &rgb_buffer_left_; target_buffer_size = &rgb_buffer_left_size_; } else if (stream_index == COLOR_RIGHT) { target_buffer = &rgb_buffer_right_; target_buffer_size = &rgb_buffer_right_size_; } else { target_buffer = &rgb_buffer_; target_buffer_size = &rgb_buffer_size_; } if (video_frame->getDataSize() > *target_buffer_size) { delete[] (*target_buffer); *target_buffer_size = video_frame->getDataSize(); *target_buffer = new uint8_t[*target_buffer_size]; buffer = *target_buffer; } CHECK_NOTNULL(buffer); memcpy(buffer, video_frame->getData(), video_frame->getDataSize()); return true; } return true; } std::shared_ptr OBCameraNode::decodeIRMJPGFrame( const std::shared_ptr &frame) { if (frame == nullptr) { return nullptr; } if (frame->getFormat() == OB_FORMAT_MJPEG && (frame->getType() == OB_FRAME_IR || frame->getType() == OB_FRAME_IR_LEFT || frame->getType() == OB_FRAME_IR_RIGHT)) { auto video_frame = frame->as(); cv::Mat mjpgMat(1, video_frame->getDataSize(), CV_8UC1, video_frame->getData()); cv::Mat irRawMat = cv::imdecode(mjpgMat, cv::IMREAD_GRAYSCALE); std::shared_ptr irFrame = ob::FrameFactory::createVideoFrame(video_frame->getType(), video_frame->getFormat(), video_frame->getWidth(), video_frame->getHeight(), 0); uint32_t buffer_size = irRawMat.rows * irRawMat.cols * irRawMat.channels(); if (buffer_size > irFrame->getDataSize()) { RCLCPP_ERROR_STREAM(logger_, "Insufficient buffer size allocation,failed to decode ir mjpg frame!"); return nullptr; } memcpy(irFrame->getData(), irRawMat.data, buffer_size); ob::FrameHelper::setFrameDeviceTimestamp(irFrame, video_frame->getTimeStampUs()); ob::FrameHelper::setFrameDeviceTimestampUs(irFrame, video_frame->getTimeStampUs()); ob::FrameHelper::setFrameSystemTimestamp(irFrame, video_frame->getSystemTimeStampUs()); return irFrame; } return frame; } void OBCameraNode::onNewFrameCallback(const std::shared_ptr &frame, const stream_index_pair &stream_index) { if (frame == nullptr) { return; } FrameTimestampCsvLogger *timestamp_csv_logger = nullptr; if (stream_index == COLOR) { timestamp_csv_logger = frame_timestamp_csv_logger_ ? frame_timestamp_csv_logger_.get() : color_timestamp_csv_logger_.get(); } else if (stream_index == DEPTH) { timestamp_csv_logger = frame_timestamp_csv_logger_ ? frame_timestamp_csv_logger_.get() : depth_timestamp_csv_logger_.get(); } const auto record_image_publish_skipped = [&]() { if (timestamp_csv_logger && timestamp_csv_logger->enabled()) { timestamp_csv_logger->recordImagePublishSkipped(stream_index.first, frame); } }; CHECK_NOTNULL(image_publishers_[stream_index]); const bool has_raw_image_subscriber = image_publishers_[stream_index]->get_subscription_count() > 0; const bool has_compressed_image_subscriber = hasCompressedImageSubscriber(stream_index); bool has_subscriber = has_raw_image_subscriber || has_compressed_image_subscriber || save_images_[stream_index]; has_subscriber = has_subscriber || camera_info_publishers_[stream_index]->get_subscription_count() > 0; has_subscriber = has_subscriber || (metadata_publishers_.count(stream_index) && metadata_publishers_[stream_index]->get_subscription_count() > 0); if (!has_subscriber) { record_image_publish_skipped(); return; } std::shared_ptr video_frame; if (frame->getType() == OB_FRAME_COLOR || frame->getType() == OB_FRAME_COLOR_LEFT || frame->getType() == OB_FRAME_COLOR_RIGHT) { video_frame = frame->as(); } else if (frame->getType() == OB_FRAME_DEPTH) { video_frame = frame->as(); } else if (frame->getType() == OB_FRAME_IR || frame->getType() == OB_FRAME_IR_LEFT || frame->getType() == OB_FRAME_IR_RIGHT) { video_frame = frame->as(); // interleave filter speckle or flood ir if (interleave_frame_enable_ && interleave_skip_enable_) { RCLCPP_DEBUG(logger_, "interleave filter skip interleave_skip_index_: %d", interleave_skip_index_); if (video_frame->getMetadataValue(OB_FRAME_METADATA_TYPE_HDR_SEQUENCE_INDEX) == interleave_skip_index_) { RCLCPP_DEBUG(logger_, "interleave filter skip frame type: %d", frame->getType()); return; } } } else { RCLCPP_ERROR(logger_, "Unsupported frame type: %d", frame->getType()); return; } if (!video_frame) { RCLCPP_ERROR(logger_, "Failed to convert frame to video frame"); record_image_publish_skipped(); return; } int width = static_cast(video_frame->getWidth()); int height = static_cast(video_frame->getHeight()); auto frame_timestamp = getFrameTimestampUs(frame); auto timestamp = fromUsToROSTime(frame_timestamp); if (!device_) { RCLCPP_ERROR_STREAM(logger_, "device is null in onNewFrameCallback"); record_image_publish_skipped(); return; } auto device_info = device_->getDeviceInfo(); if (!device_info || !device_info.get()) { RCLCPP_ERROR_STREAM(logger_, "device_info is null in onNewFrameCallback"); record_image_publish_skipped(); return; } OBCameraIntrinsic intrinsic; OBCameraDistortion distortion; auto stream_profile = frame->getStreamProfile(); CHECK_NOTNULL(stream_profile); auto video_stream_profile = stream_profile->as(); CHECK_NOTNULL(video_stream_profile); intrinsic = video_stream_profile->getIntrinsic(); distortion = video_stream_profile->getDistortion(); if (pid_ == DABAI_MAX_PID) { auto camera_params = pipeline_->getCameraParam(); // use color param intrinsic = camera_params.rgbIntrinsic; distortion = camera_params.rgbDistortion; } std::string frame_id = optical_frame_id_[stream_index]; if (depth_registration_ && stream_index == DEPTH) { frame_id = depth_aligned_frame_id_[stream_index]; } sensor_msgs::msg::CameraInfo camera_info{}; if (color_info_manager_ && color_info_manager_->isCalibrated() && stream_index == COLOR) { camera_info = color_info_manager_->getCameraInfo(); camera_info.header.stamp = timestamp; camera_info.header.frame_id = frame_id; camera_info.width = width; camera_info.height = height; } else if (ir_info_manager_ && ir_info_manager_->isCalibrated() && (stream_index == INFRA1 || stream_index == INFRA2 || stream_index == DEPTH)) { camera_info = ir_info_manager_->getCameraInfo(); camera_info.header.stamp = timestamp; camera_info.header.frame_id = frame_id; camera_info.width = width; camera_info.height = height; } else { camera_info = convertToCameraInfo(intrinsic, distortion, width); camera_info.header.stamp = timestamp; camera_info.header.frame_id = frame_id; camera_info.width = width; camera_info.height = height; } auto &image = images_[stream_index]; if (frame->getType() == OB_FRAME_IR_RIGHT && enable_stream_[INFRA1]) { auto stream_profile = frame->getStreamProfile(); CHECK_NOTNULL(stream_profile); auto video_stream_profile = stream_profile->as(); CHECK_NOTNULL(video_stream_profile); auto left_video_profile = stream_profile_[INFRA1]->as(); CHECK_NOTNULL(left_video_profile); auto ex = video_stream_profile->getExtrinsicTo(left_video_profile); float fx = camera_info.k.at(0); float fy = camera_info.k.at(4); camera_info.p.at(3) = -fx * ex.trans[0] / 1000.0 + 0.0; camera_info.p.at(7) = -fy * ex.trans[1] / 1000.0 + 0.0; } if ((stream_index == COLOR || stream_index == COLOR_LEFT || stream_index == COLOR_RIGHT) && frame->getFormat() == OB_FORMAT_MJPG && has_compressed_image_subscriber) { if (!has_raw_image_subscriber && stream_index == COLOR && timestamp_csv_logger && timestamp_csv_logger->enabled()) { timestamp_csv_logger->recordPreImagePublish(stream_index.first, frame, getSystemNowUs(), getSteadyNowUs()); } publishCompressedColorImage(frame, stream_index, timestamp, frame_id); if (!has_raw_image_subscriber && stream_index == COLOR) { fps_delay_status_color_->tick(frame_timestamp); } } if (!has_raw_image_subscriber && !save_images_[stream_index]) { CHECK(camera_info_publishers_.count(stream_index) > 0); camera_info_publishers_[stream_index]->publish(camera_info); publishMetadata(frame, stream_index, camera_info.header); record_image_publish_skipped(); return; } CHECK_NOTNULL(image_publishers_[stream_index]); if (image.empty() || image.cols != width || image.rows != height) { image.create(height, width, image_format_[stream_index]); } if (isColorFrameDecodeRequired(frame) && (has_raw_image_subscriber || save_images_[stream_index])) { if (frame->getType() == OB_FRAME_COLOR && !is_color_frame_decoded_) { RCLCPP_ERROR(logger_, "color frame is not decoded"); record_image_publish_skipped(); return; } if (frame->getType() == OB_FRAME_COLOR_LEFT && !is_left_color_frame_decoded_) { RCLCPP_ERROR(logger_, "left color frame is not decoded"); return; } if (frame->getType() == OB_FRAME_COLOR_RIGHT && !is_right_color_frame_decoded_) { RCLCPP_ERROR(logger_, "right color frame is not decoded"); return; } if (frame->getType() == OB_FRAME_COLOR) { memcpy(image.data, rgb_buffer_, video_frame->getWidth() * video_frame->getHeight() * 3); } else if (frame->getType() == OB_FRAME_COLOR_LEFT) { memcpy(image.data, rgb_buffer_left_, video_frame->getWidth() * video_frame->getHeight() * 3); } else if (frame->getType() == OB_FRAME_COLOR_RIGHT) { memcpy(image.data, rgb_buffer_right_, video_frame->getWidth() * video_frame->getHeight() * 3); } } else { memcpy(image.data, video_frame->getData(), video_frame->getDataSize()); } CHECK(camera_info_publishers_.count(stream_index) > 0); camera_info_publishers_[stream_index]->publish(camera_info); publishMetadata(frame, stream_index, camera_info.header); if (!has_raw_image_subscriber && !save_images_[stream_index]) { return; } if (stream_index == DEPTH) { auto depth_scale = video_frame->as()->getValueScale(); image = image * depth_scale; } CHECK(image_publishers_.count(stream_index) > 0); if (has_raw_image_subscriber || save_images_[stream_index]) { sensor_msgs::msg::Image::UniquePtr image_msg(new sensor_msgs::msg::Image()); cv_bridge::CvImage(std_msgs::msg::Header(), encoding_[stream_index], image) .toImageMsg(*image_msg); CHECK_NOTNULL(image_msg.get()); image_msg->header.stamp = timestamp; image_msg->is_bigendian = false; image_msg->step = width * unit_step_size_[stream_index]; image_msg->header.frame_id = frame_id; saveImageToFile(stream_index, image, *image_msg); if (!has_raw_image_subscriber) { record_image_publish_skipped(); return; } if (timestamp_csv_logger && timestamp_csv_logger->enabled()) { timestamp_csv_logger->recordPreImagePublish(stream_index.first, frame, getSystemNowUs(), getSteadyNowUs()); } if (stream_index == COLOR) { fps_delay_status_color_->tick(frame_timestamp); } else if (stream_index == DEPTH) { fps_delay_status_depth_->tick(frame_timestamp); } image_publishers_[stream_index]->publish(std::move(image_msg)); } } bool OBCameraNode::hasCompressedImageSubscriber(const stream_index_pair &stream_index) const { auto it = compressed_image_publishers_.find(stream_index); return it != compressed_image_publishers_.end() && it->second && it->second->get_subscription_count() > 0; } void OBCameraNode::publishCompressedColorImage(const std::shared_ptr &frame, const stream_index_pair &stream_index, const rclcpp::Time ×tamp, const std::string &frame_id) { auto it = compressed_image_publishers_.find(stream_index); if (it == compressed_image_publishers_.end() || !it->second) { return; } sensor_msgs::msg::CompressedImage msg; msg.header.stamp = timestamp; msg.header.frame_id = frame_id; msg.format = encoding_[stream_index] + "; jpeg compressed " + encoding_[stream_index]; const auto *data = static_cast(frame->getData()); msg.data.assign(data, data + frame->getDataSize()); it->second->publish(std::move(msg)); } void OBCameraNode::publishMetadata(const std::shared_ptr &frame, const stream_index_pair &stream_index, const std_msgs::msg::Header &header) { if (metadata_publishers_.count(stream_index) == 0) { return; } auto metadata_publisher = metadata_publishers_[stream_index]; if (metadata_publisher->get_subscription_count() == 0) { return; } orbbec_camera_msgs::msg::Metadata metadata_msg; metadata_msg.header = header; nlohmann::json json_data; for (int i = 0; i < OB_FRAME_METADATA_TYPE_COUNT; i++) { auto meta_data_type = static_cast(i); std::string field_name = metaDataTypeToString(meta_data_type); if (!frame->hasMetadata(meta_data_type)) { continue; } int64_t value = frame->getMetadataValue(meta_data_type); json_data[field_name] = value; } metadata_msg.json_data = json_data.dump(2); metadata_publisher->publish(metadata_msg); } void OBCameraNode::saveImageToFile(const stream_index_pair &stream_index, const cv::Mat &image, const sensor_msgs::msg::Image &image_msg) { if (save_images_[stream_index]) { auto now = std::chrono::system_clock::now(); auto in_time_t = std::chrono::system_clock::to_time_t(now); auto us = std::chrono::duration_cast(now.time_since_epoch()) % 1000000; std::stringstream ss; ss << std::put_time(std::localtime(&in_time_t), "%Y%m%d_%H%M%S"); ss << "_" << std::setw(6) << std::setfill('0') << us.count(); auto current_path = std::filesystem::current_path().string(); auto fps = fps_[stream_index]; int index = save_images_count_[stream_index]; std::string file_suffix = stream_index == COLOR ? ".png" : ".raw"; std::string filename = current_path + "/image/" + stream_name_[stream_index] + "_" + std::to_string(image_msg.width) + "x" + std::to_string(image_msg.height) + "_" + std::to_string(fps) + "hz_" + ss.str() + "_" + std::to_string(index) + file_suffix; if (!std::filesystem::exists(current_path + "/image")) { std::filesystem::create_directory(current_path + "/image"); } RCLCPP_INFO_STREAM(logger_, "Saving image to " << filename); if (stream_index.first == OB_STREAM_COLOR) { auto image_to_save = cv_bridge::toCvCopy(image_msg, sensor_msgs::image_encodings::BGR8)->image; cv::imwrite(filename, image_to_save); } else if (stream_index.first == OB_STREAM_IR || stream_index.first == OB_STREAM_IR_LEFT || stream_index.first == OB_STREAM_IR_RIGHT || stream_index.first == OB_STREAM_DEPTH) { std::ofstream ofs(filename, std::ios::out | std::ios::binary); if (!ofs.is_open()) { RCLCPP_ERROR_STREAM(logger_, "Failed to open file: " << filename); return; } if (image.isContinuous()) { ofs.write(reinterpret_cast(image.data), image.total() * image.elemSize()); } else { int rows = image.rows; int cols = image.cols * image.channels(); for (int r = 0; r < rows; ++r) { ofs.write(reinterpret_cast(image.ptr(r)), cols); } } ofs.close(); } else { RCLCPP_ERROR_STREAM(logger_, "Unsupported stream type: " << stream_index.first); } if (++save_images_count_[stream_index] >= max_save_images_count_) { save_images_[stream_index] = false; } } } void OBCameraNode::onNewIMUFrameSyncOutputCallback(const std::shared_ptr &accelframe, const std::shared_ptr &gryoframe, int64_t arrival_system_us) { if (!is_camera_node_initialized_.load() || !rclcpp::ok()) { return; } const auto record_timestamps = [&](std::optional publish_system_us) { if (arrival_system_us != 0 && imu_timestamp_csv_logger_ && imu_timestamp_csv_logger_->enabled()) { imu_timestamp_csv_logger_->recordFrameSet(accelframe, gryoframe, arrival_system_us, publish_system_us); } }; if (!imu_gyro_accel_publisher_) { record_timestamps(std::nullopt); RCLCPP_ERROR_STREAM(logger_, "stream Accel Gryo publisher not initialized"); return; } bool has_subscriber = imu_gyro_accel_publisher_->get_subscription_count() > 0; has_subscriber = has_subscriber || imu_info_publishers_[GYRO]->get_subscription_count() > 0; has_subscriber = has_subscriber || imu_info_publishers_[ACCEL]->get_subscription_count() > 0; if (!has_subscriber) { record_timestamps(std::nullopt); return; } auto imu_msg = sensor_msgs::msg::Imu(); setDefaultIMUMessage(imu_msg); imu_msg.header.frame_id = optical_frame_id_[GYRO]; auto frame_timestamp = getFrameTimestampUs(accelframe); auto timestamp = fromUsToROSTime(frame_timestamp); imu_msg.header.stamp = timestamp; auto gyro_info = createIMUInfo(GYRO); gyro_info.header = imu_msg.header; imu_info_publishers_[GYRO]->publish(gyro_info); auto accel_info = createIMUInfo(ACCEL); imu_msg.header.frame_id = optical_frame_id_[ACCEL]; accel_info.header = imu_msg.header; imu_info_publishers_[ACCEL]->publish(accel_info); imu_msg.header.frame_id = accel_gyro_frame_id_; auto gyro_frame = gryoframe->as(); auto gyroData = gyro_frame->getValue(); imu_msg.angular_velocity.x = gyroData.x - gyro_info.bias[0]; imu_msg.angular_velocity.y = gyroData.y - gyro_info.bias[1]; imu_msg.angular_velocity.z = gyroData.z - gyro_info.bias[2]; auto accel_frame = accelframe->as(); auto accelData = accel_frame->getValue(); imu_msg.linear_acceleration.x = accelData.x - accel_info.bias[0]; imu_msg.linear_acceleration.y = accelData.y - accel_info.bias[1]; imu_msg.linear_acceleration.z = accelData.z - accel_info.bias[2]; const auto publish_system_us = getSystemNowUs(); imu_gyro_accel_publisher_->publish(imu_msg); record_timestamps(publish_system_us); } void OBCameraNode::onNewIMUFrameCallback(const std::shared_ptr &frame, const stream_index_pair &stream_index) { if (!is_camera_node_initialized_.load() || !rclcpp::ok()) { return; } auto *timestamp_csv_logger = stream_index == ACCEL ? accel_timestamp_csv_logger_.get() : gyro_timestamp_csv_logger_.get(); const bool log_imu_timestamps = timestamp_csv_logger && timestamp_csv_logger->enabled(); const auto arrival_system_us = log_imu_timestamps ? getSystemNowUs() : 0; const auto record_timestamps = [&](std::optional publish_system_us) { if (log_imu_timestamps) { timestamp_csv_logger->recordStandaloneFrame(stream_index.first, frame, arrival_system_us, publish_system_us); } }; if (!imu_publishers_.count(stream_index)) { record_timestamps(std::nullopt); RCLCPP_ERROR_STREAM(logger_, "stream " << stream_name_[stream_index] << " publisher not initialized"); return; } bool has_subscriber = imu_publishers_[stream_index]->get_subscription_count() > 0; has_subscriber = has_subscriber || imu_info_publishers_[stream_index]->get_subscription_count() > 0; if (!has_subscriber) { record_timestamps(std::nullopt); return; } auto imu_msg = sensor_msgs::msg::Imu(); setDefaultIMUMessage(imu_msg); imu_msg.header.frame_id = optical_frame_id_[stream_index]; auto timestamp = fromUsToROSTime(frame->getTimeStampUs()); imu_msg.header.stamp = timestamp; auto imu_info = createIMUInfo(stream_index); imu_info.header = imu_msg.header; imu_info_publishers_[stream_index]->publish(imu_info); if (frame->getType() == OB_FRAME_GYRO) { auto gyro_frame = frame->as(); auto data = gyro_frame->getValue(); imu_msg.angular_velocity.x = data.x - imu_info.bias[0]; imu_msg.angular_velocity.y = data.y - imu_info.bias[1]; imu_msg.angular_velocity.z = data.z - imu_info.bias[2]; } else if (frame->getType() == OB_FRAME_ACCEL) { auto accel_frame = frame->as(); auto data = accel_frame->getValue(); imu_msg.linear_acceleration.x = data.x - imu_info.bias[0]; imu_msg.linear_acceleration.y = data.y - imu_info.bias[1]; imu_msg.linear_acceleration.z = data.z - imu_info.bias[2]; } else { record_timestamps(std::nullopt); RCLCPP_ERROR(logger_, "Unsupported IMU frame type"); return; } const auto publish_system_us = getSystemNowUs(); imu_publishers_[stream_index]->publish(imu_msg); record_timestamps(publish_system_us); } void OBCameraNode::setDefaultIMUMessage(sensor_msgs::msg::Imu &imu_msg) { imu_msg.header.frame_id = "imu_link"; imu_msg.orientation.x = 0.0; imu_msg.orientation.y = 0.0; imu_msg.orientation.z = 0.0; imu_msg.orientation.w = 1.0; imu_msg.orientation_covariance = {-1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; imu_msg.linear_acceleration_covariance = { linear_accel_cov_, 0.0, 0.0, 0.0, linear_accel_cov_, 0.0, 0.0, 0.0, linear_accel_cov_}; imu_msg.angular_velocity_covariance = { angular_vel_cov_, 0.0, 0.0, 0.0, angular_vel_cov_, 0.0, 0.0, 0.0, angular_vel_cov_}; } sensor_msgs::msg::Imu OBCameraNode::createUnitIMUMessage(const IMUData &accel_data, const IMUData &gyro_data) { sensor_msgs::msg::Imu imu_msg; rclcpp::Time timestamp(gyro_data.timestamp_); imu_msg.header.stamp = timestamp; imu_msg.angular_velocity.x = gyro_data.data_.x(); imu_msg.angular_velocity.y = gyro_data.data_.y(); imu_msg.angular_velocity.z = gyro_data.data_.z(); imu_msg.linear_acceleration.x = accel_data.data_.x(); imu_msg.linear_acceleration.y = accel_data.data_.y(); imu_msg.linear_acceleration.z = accel_data.data_.z(); return imu_msg; } std::optional OBCameraNode::findDefaultCameraParam() { auto camera_params = device_->getCalibrationCameraParamList(); for (size_t i = 0; i < camera_params->count(); i++) { auto param = camera_params->getCameraParam(i); int depth_w = param.depthIntrinsic.width; int depth_h = param.depthIntrinsic.height; int color_w = param.rgbIntrinsic.width; int color_h = param.rgbIntrinsic.height; if ((depth_w * height_[DEPTH] == depth_h * width_[DEPTH]) && (color_w * height_[COLOR] == color_h * width_[COLOR])) { return param; } } return {}; } std::optional OBCameraNode::getDepthCameraParam() { auto camera_params = device_->getCalibrationCameraParamList(); for (size_t i = 0; i < camera_params->count(); i++) { auto param = camera_params->getCameraParam(i); int depth_w = param.depthIntrinsic.width; int depth_h = param.depthIntrinsic.height; if (depth_w == width_[DEPTH] && depth_h == height_[DEPTH]) { RCLCPP_INFO_STREAM(logger_, "getCameraDepthParam w: " << depth_w << ",h:" << depth_h); return param; } } for (size_t i = 0; i < camera_params->count(); i++) { auto param = camera_params->getCameraParam(i); int depth_w = param.depthIntrinsic.width; int depth_h = param.depthIntrinsic.height; if (depth_w * height_[DEPTH] == depth_h * width_[DEPTH]) { RCLCPP_INFO_STREAM(logger_, "getCameraDepthParam w: " << depth_w << ",h:" << depth_h); return param; } } return {}; } std::optional OBCameraNode::getColorCameraParam() { auto camera_params = device_->getCalibrationCameraParamList(); for (size_t i = 0; i < camera_params->count(); i++) { auto param = camera_params->getCameraParam(i); int color_w = param.rgbIntrinsic.width; int color_h = param.rgbIntrinsic.height; if (color_w == width_[COLOR] && color_h == height_[COLOR]) { RCLCPP_INFO_STREAM(logger_, "getColorCameraParam w: " << color_w << ",h:" << color_h); return param; } } for (size_t i = 0; i < camera_params->count(); i++) { auto param = camera_params->getCameraParam(i); int color_w = param.rgbIntrinsic.width; int color_h = param.rgbIntrinsic.height; if (color_w * height_[COLOR] == color_h * width_[COLOR]) { RCLCPP_INFO_STREAM(logger_, "getColorCameraParam w: " << color_w << ",h:" << color_h); return param; } } return {}; } void OBCameraNode::publishStaticTF(const rclcpp::Time &t, const tf2::Vector3 &trans, const tf2::Quaternion &q, const std::string &from, const std::string &to) { geometry_msgs::msg::TransformStamped msg; msg.header.stamp = t; msg.header.frame_id = from; msg.child_frame_id = to; msg.transform.translation.x = trans[2] / 1000.0; msg.transform.translation.y = -trans[0] / 1000.0; msg.transform.translation.z = -trans[1] / 1000.0; msg.transform.rotation.x = q.getX(); msg.transform.rotation.y = q.getY(); msg.transform.rotation.z = q.getZ(); msg.transform.rotation.w = q.getW(); static_tf_msgs_.push_back(msg); } void OBCameraNode::calcAndPublishStaticTransform() { tf2::Quaternion quaternion_optical, zero_rot; zero_rot.setRPY(0.0, 0.0, 0.0); quaternion_optical.setRPY(-M_PI / 2, 0.0, -M_PI / 2); tf2::Vector3 zero_trans(0, 0, 0); auto base_stream_profile = stream_profile_[base_stream_]; auto device_info = device_->getDeviceInfo(); CHECK_NOTNULL(device_info); if (!base_stream_profile) { RCLCPP_ERROR_STREAM(logger_, "Failed to get base stream profile"); return; } CHECK_NOTNULL(base_stream_profile.get()); for (const auto &item : stream_profile_) { auto stream_index = item.first; auto stream_profile = item.second; if (!stream_profile) { continue; } OBExtrinsic ex; try { ex = stream_profile->getExtrinsicTo(base_stream_profile); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to get " << stream_name_[stream_index] << " extrinsic: " << orbbec_camera::formatObErrorWithStatus(e)); ex = OBExtrinsic({{1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 0, 0}}); } auto Q = rotationMatrixToQuaternion(ex.rot); Q = quaternion_optical * Q * quaternion_optical.inverse(); tf2::Vector3 trans(ex.trans[0], ex.trans[1], ex.trans[2]); auto timestamp = node_->now(); if (stream_index.first != base_stream_.first) { if (stream_index.first == OB_STREAM_IR_RIGHT && base_stream_.first == OB_STREAM_DEPTH) { trans[0] = std::abs(trans[0]); // because left and right ir calibration is error } publishStaticTF(timestamp, trans, Q, frame_id_[base_stream_], frame_id_[stream_index]); } publishStaticTF(timestamp, zero_trans, quaternion_optical, frame_id_[stream_index], optical_frame_id_[stream_index]); RCLCPP_DEBUG_STREAM(logger_, "Publishing static transform from " << stream_name_[stream_index] << " to " << stream_name_[base_stream_]); RCLCPP_DEBUG_STREAM(logger_, "Translation " << trans[0] << ", " << trans[1] << ", " << trans[2]); RCLCPP_DEBUG_STREAM(logger_, "Rotation " << Q.getX() << ", " << Q.getY() << ", " << Q.getZ() << ", " << Q.getW()); } if ((pid_ == FEMTO_BOLT_PID || pid_ == FEMTO_MEGA_PID) && enable_stream_[DEPTH] && enable_stream_[COLOR]) { // calc depth to color CHECK_NOTNULL(stream_profile_[COLOR]); auto depth_to_color_extrinsics = base_stream_profile->getExtrinsicTo(stream_profile_[COLOR]); auto Q = rotationMatrixToQuaternion(depth_to_color_extrinsics.rot); Q = quaternion_optical * Q * quaternion_optical.inverse(); publishStaticTF(node_->now(), zero_trans, Q, camera_link_frame_id_, frame_id_[base_stream_]); } else { publishStaticTF(node_->now(), zero_trans, zero_rot, camera_link_frame_id_, frame_id_[base_stream_]); } if (enable_stream_[DEPTH] && enable_stream_[COLOR] && enable_publish_extrinsic_) { static const char *frame_id = "depth_to_color_extrinsics"; OBExtrinsic ex; try { ex = base_stream_profile->getExtrinsicTo(stream_profile_[COLOR]); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to get " << frame_id << " extrinsic: " << orbbec_camera::formatObErrorWithStatus(e)); ex = OBExtrinsic({{1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 0, 0}}); } depth_to_other_extrinsics_[COLOR] = ex; auto ex_msg = obExtrinsicsToMsg(ex, frame_id); CHECK_NOTNULL(depth_to_other_extrinsics_publishers_[COLOR]); depth_to_other_extrinsics_publishers_[COLOR]->publish(ex_msg); } if (enable_stream_[DEPTH] && enable_stream_[INFRA0] && enable_publish_extrinsic_) { static const char *frame_id = "depth_to_ir_extrinsics"; OBExtrinsic ex; try { ex = base_stream_profile->getExtrinsicTo(stream_profile_[INFRA0]); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to get " << frame_id << " extrinsic: " << orbbec_camera::formatObErrorWithStatus(e)); ex = OBExtrinsic({{1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 0, 0}}); } depth_to_other_extrinsics_[INFRA0] = ex; auto ex_msg = obExtrinsicsToMsg(ex, frame_id); CHECK_NOTNULL(depth_to_other_extrinsics_publishers_[INFRA0]); depth_to_other_extrinsics_publishers_[INFRA0]->publish(ex_msg); } if (enable_stream_[DEPTH] && enable_stream_[INFRA1] && enable_publish_extrinsic_) { static const char *frame_id = "depth_to_left_ir_extrinsics"; OBExtrinsic ex; try { ex = base_stream_profile->getExtrinsicTo(stream_profile_[INFRA1]); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to get " << frame_id << " extrinsic: " << orbbec_camera::formatObErrorWithStatus(e)); ex = OBExtrinsic({{1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 0, 0}}); } depth_to_other_extrinsics_[INFRA1] = ex; auto ex_msg = obExtrinsicsToMsg(ex, frame_id); CHECK_NOTNULL(depth_to_other_extrinsics_publishers_[INFRA1]); depth_to_other_extrinsics_publishers_[INFRA1]->publish(ex_msg); } if (enable_stream_[DEPTH] && enable_stream_[INFRA2] && enable_publish_extrinsic_) { static const char *frame_id = "depth_to_right_ir_extrinsics"; OBExtrinsic ex; try { ex = base_stream_profile->getExtrinsicTo(stream_profile_[INFRA2]); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to get " << frame_id << " extrinsic: " << orbbec_camera::formatObErrorWithStatus(e)); ex = OBExtrinsic({{1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 0, 0}}); } ex.trans[0] = -std::abs(ex.trans[0]); depth_to_other_extrinsics_[INFRA2] = ex; auto ex_msg = obExtrinsicsToMsg(ex, frame_id); CHECK_NOTNULL(depth_to_other_extrinsics_publishers_[INFRA2]); depth_to_other_extrinsics_publishers_[INFRA2]->publish(ex_msg); } if (enable_stream_[DEPTH] && enable_stream_[ACCEL] && enable_publish_extrinsic_) { static const char *frame_id = "depth_to_accel_extrinsics"; OBExtrinsic ex; try { ex = base_stream_profile->getExtrinsicTo(stream_profile_[ACCEL]); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to get " << frame_id << " extrinsic: " << orbbec_camera::formatObErrorWithStatus(e)); ex = OBExtrinsic({{1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 0, 0}}); } depth_to_other_extrinsics_[ACCEL] = ex; auto ex_msg = obExtrinsicsToMsg(ex, frame_id); CHECK_NOTNULL(depth_to_other_extrinsics_publishers_[ACCEL]); depth_to_other_extrinsics_publishers_[ACCEL]->publish(ex_msg); } if (enable_stream_[DEPTH] && enable_stream_[GYRO] && enable_publish_extrinsic_) { static const char *frame_id = "depth_to_gyro_extrinsics"; OBExtrinsic ex; try { ex = base_stream_profile->getExtrinsicTo(stream_profile_[GYRO]); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to get " << frame_id << " extrinsic: " << orbbec_camera::formatObErrorWithStatus(e)); ex = OBExtrinsic({{1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 0, 0}}); } depth_to_other_extrinsics_[GYRO] = ex; auto ex_msg = obExtrinsicsToMsg(ex, frame_id); CHECK_NOTNULL(depth_to_other_extrinsics_publishers_[GYRO]); depth_to_other_extrinsics_publishers_[GYRO]->publish(ex_msg); } if (enable_stream_[COLOR_LEFT] && enable_stream_[COLOR_RIGHT] && enable_publish_extrinsic_) { static const char *frame_id = "left_color_to_right_color_extrinsics"; OBExtrinsic ex; try { ex = stream_profile_[COLOR_LEFT]->getExtrinsicTo(stream_profile_[COLOR_RIGHT]); } catch (const ob::Error &e) { RCLCPP_ERROR_STREAM(logger_, "Failed to get " << frame_id << " extrinsic: " << orbbec_camera::formatObErrorWithStatus(e)); ex = OBExtrinsic({{1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 0, 0}}); } depth_to_other_extrinsics_[COLOR_LEFT] = ex; auto ex_msg = obExtrinsicsToMsg(ex, frame_id); CHECK_NOTNULL(depth_to_other_extrinsics_publishers_[COLOR_LEFT]); depth_to_other_extrinsics_publishers_[COLOR_LEFT]->publish(ex_msg); } if (enable_sync_output_accel_gyro_) { tf2::Quaternion zero_rot; zero_rot.setRPY(0.0, 0.0, 0.0); tf2::Vector3 zero_trans(0, 0, 0); publishStaticTF(node_->now(), zero_trans, zero_rot, optical_frame_id_[GYRO], accel_gyro_frame_id_); } } void OBCameraNode::publishStaticTransforms() { if (!publish_tf_) { return; } static_tf_broadcaster_ = std::make_shared(*node_); dynamic_tf_broadcaster_ = std::make_shared(*node_); calcAndPublishStaticTransform(); if (tf_publish_rate_ > 0) { tf_thread_ = std::make_shared([this]() { publishDynamicTransforms(); }); } else { static_tf_broadcaster_->sendTransform(static_tf_msgs_); } } void OBCameraNode::publishDynamicTransforms() { RCLCPP_WARN(logger_, "Publishing dynamic camera transforms (/tf) at %g Hz", tf_publish_rate_); std::mutex mu; std::unique_lock lock(mu); while (rclcpp::ok() && is_running_) { tf_cv_.wait_for(lock, std::chrono::milliseconds((int)(1000.0 / tf_publish_rate_)), [this] { return (!(is_running_)); }); { rclcpp::Time t = node_->now(); for (auto &msg : static_tf_msgs_) { msg.header.stamp = t; } dynamic_tf_broadcaster_->sendTransform(static_tf_msgs_); } } } template T lerp(const T &a, const T &b, const double t) { return a * (1.0 - t) + b * t; } void OBCameraNode::FillImuDataLinearInterpolation(const IMUData &imu_data, std::deque &imu_msgs) { imu_history_.push_back(imu_data); stream_index_pair steam_index(imu_data.stream_); imu_msgs.clear(); std::deque gyros_data; IMUData accel0, accel1, current_imu; while (!imu_history_.empty()) { current_imu = imu_history_.front(); imu_history_.pop_front(); if (accel0.isSet() && current_imu.stream_ == ACCEL) { accel0 = current_imu; } else if (accel0.isSet() && current_imu.stream_ == ACCEL) { accel1 = current_imu; const double dt = accel1.timestamp_ - accel0.timestamp_; while (!gyros_data.empty()) { auto current_gyro = gyros_data.front(); gyros_data.pop_front(); const double alpha = (current_gyro.timestamp_ - accel0.timestamp_) / dt; IMUData current_accel(ACCEL, lerp(accel0.data_, accel1.data_, alpha), current_gyro.timestamp_); imu_msgs.push_back((createUnitIMUMessage(current_accel, current_gyro))); } accel0 = accel1; } else if (accel0.isSet() && current_imu.timestamp_ >= accel0.timestamp_ && current_imu.stream_ == GYRO) { gyros_data.push_back(current_imu); } } imu_history_.push_back(current_imu); } void OBCameraNode::FillImuDataCopy(const IMUData &imu_data, std::deque &imu_msgs) { stream_index_pair steam_index(imu_data.stream_); if (steam_index == ACCEL) { accel_data_ = imu_data; return; } if (accel_data_.isSet()) { return; } imu_msgs.push_back(createUnitIMUMessage(accel_data_, imu_data)); } bool OBCameraNode::setupFormatConvertType(OBFormat format) { return setupFormatConvertType(format, format_convert_filter_); } bool OBCameraNode::setupFormatConvertType(OBFormat format, ob::FormatConvertFilter &filter) { switch (format) { case OB_FORMAT_RGB888: return true; case OB_FORMAT_I420: filter.setFormatConvertType(FORMAT_I420_TO_RGB888); break; case OB_FORMAT_MJPG: filter.setFormatConvertType(FORMAT_MJPEG_TO_RGB888); break; case OB_FORMAT_YUYV: filter.setFormatConvertType(FORMAT_YUYV_TO_RGB888); break; case OB_FORMAT_NV21: filter.setFormatConvertType(FORMAT_NV21_TO_RGB888); break; case OB_FORMAT_NV12: filter.setFormatConvertType(FORMAT_NV12_TO_RGB888); break; case OB_FORMAT_UYVY: filter.setFormatConvertType(FORMAT_UYVY_TO_RGB888); break; default: return false; } return true; } bool OBCameraNode::isGemini335PID(uint32_t pid) { return pid == GEMINI_335_PID || pid == GEMINI_330_PID || pid == GEMINI_336_PID || pid == GEMINI_335L_PID || pid == GEMINI_330L_PID || pid == GEMINI_336L_PID || pid == GEMINI_335LG_PID || pid == GEMINI_336LG_PID || pid == GEMINI_335LE_PID || pid == GEMINI_336LE_PID || pid == CUSTOM_ADVANTECH_GEMINI_336_PID || pid == CUSTOM_ADVANTECH_GEMINI_336L_PID || pid == GEMINI_338_PID || pid == GEMINI_338L_PID || pid == GEMINI_338LE_PID || pid == GEMINI_338LG_PID; } bool OBCameraNode::isGemini435LePID(uint32_t pid) { return pid == GEMINI_435Le_PID; } bool OBCameraNode::isPublishMetaData(uint32_t pid) { return isGemini335PID(pid) || isGemini435LePID(pid) || isGemini305SeriesPID(pid); } bool OBCameraNode::isDabaiASeriesForHwD2C(uint32_t pid) { return pid == DABAI_A_PID || pid == DABAI_AL_PID || pid == GEMINI_345_PID || pid == GEMINI_345LG_PID; } bool OBCameraNode::isDepthWorkModeDevices(uint32_t pid) { return pid == GEMINI_435Le_PID; } bool OBCameraNode::isnotLaserDevices(uint32_t pid) { return isGemini305SeriesPID(pid); } orbbec_camera_msgs::msg::IMUInfo OBCameraNode::createIMUInfo( const stream_index_pair &stream_index) { orbbec_camera_msgs::msg::IMUInfo imu_info; imu_info.header.frame_id = optical_frame_id_[stream_index]; imu_info.header.stamp = node_->now(); if (stream_index == GYRO) { auto gyro_profile = stream_profile_[stream_index]->as(); auto gyro_intrinsics = gyro_profile->getIntrinsic(); imu_info.noise_density = gyro_intrinsics.noiseDensity; imu_info.random_walk = gyro_intrinsics.randomWalk; imu_info.reference_temperature = gyro_intrinsics.referenceTemp; imu_info.bias = {gyro_intrinsics.bias[0], gyro_intrinsics.bias[1], gyro_intrinsics.bias[2]}; imu_info.scale_misalignment = { gyro_intrinsics.scaleMisalignment[0], gyro_intrinsics.scaleMisalignment[1], gyro_intrinsics.scaleMisalignment[2], gyro_intrinsics.scaleMisalignment[3], gyro_intrinsics.scaleMisalignment[4], gyro_intrinsics.scaleMisalignment[5], gyro_intrinsics.scaleMisalignment[6], gyro_intrinsics.scaleMisalignment[7], gyro_intrinsics.scaleMisalignment[8]}; imu_info.temperature_slope = { gyro_intrinsics.tempSlope[0], gyro_intrinsics.tempSlope[1], gyro_intrinsics.tempSlope[2], gyro_intrinsics.tempSlope[3], gyro_intrinsics.tempSlope[4], gyro_intrinsics.tempSlope[5], gyro_intrinsics.tempSlope[6], gyro_intrinsics.tempSlope[7], gyro_intrinsics.tempSlope[8]}; } else if (stream_index == ACCEL) { auto accel_profile = stream_profile_[stream_index]->as(); auto accel_intrinsics = accel_profile->getIntrinsic(); imu_info.noise_density = accel_intrinsics.noiseDensity; imu_info.random_walk = accel_intrinsics.randomWalk; imu_info.reference_temperature = accel_intrinsics.referenceTemp; imu_info.bias = {accel_intrinsics.bias[0], accel_intrinsics.bias[1], accel_intrinsics.bias[2]}; imu_info.gravity = {accel_intrinsics.gravity[0], accel_intrinsics.gravity[1], accel_intrinsics.gravity[2]}; imu_info.scale_misalignment = { accel_intrinsics.scaleMisalignment[0], accel_intrinsics.scaleMisalignment[1], accel_intrinsics.scaleMisalignment[2], accel_intrinsics.scaleMisalignment[3], accel_intrinsics.scaleMisalignment[4], accel_intrinsics.scaleMisalignment[5], accel_intrinsics.scaleMisalignment[6], accel_intrinsics.scaleMisalignment[7], accel_intrinsics.scaleMisalignment[8]}; imu_info.temperature_slope = {accel_intrinsics.tempSlope[0], accel_intrinsics.tempSlope[1], accel_intrinsics.tempSlope[2], accel_intrinsics.tempSlope[3], accel_intrinsics.tempSlope[4], accel_intrinsics.tempSlope[5], accel_intrinsics.tempSlope[6], accel_intrinsics.tempSlope[7], accel_intrinsics.tempSlope[8]}; } return imu_info; } void OBCameraNode::updateDepthFilterEnabledCache(const std::string &filter_name, bool enabled) { const auto normalized_filter_name = normalizeDepthFilterName(filter_name); if (normalized_filter_name == "DecimationFilter") { enable_decimation_filter_ = enabled; } else if (normalized_filter_name == "HDRMerge") { enable_hdr_merge_ = enabled; } else if (normalized_filter_name == "SequenceIdFilter") { enable_sequence_id_filter_ = enabled; } else if (normalized_filter_name == "ThresholdFilter") { enable_threshold_filter_ = enabled; } else if (normalized_filter_name == "SpatialAdvancedFilter") { enable_spatial_filter_ = enabled; } else if (normalized_filter_name == "TemporalFilter") { enable_temporal_filter_ = enabled; } else if (normalized_filter_name == "HoleFillingFilter") { enable_hole_filling_filter_ = enabled; } else if (normalized_filter_name == "EdgeNoiseRemovalFilter") { enable_edge_noise_removal_filter_ = enabled; } else if (normalized_filter_name == "SpatialFastFilter") { enable_spatial_fast_filter_ = enabled; } else if (normalized_filter_name == "SpatialModerateFilter") { enable_spatial_moderate_filter_ = enabled; } else if (normalized_filter_name == "FalsePositiveFilter") { enable_false_positive_filter_ = enabled; } else if (normalized_filter_name == "MgcNoiseRemovalFilter") { enable_mgc_noise_removal_filter_ = enabled; } else if (normalized_filter_name == "LutNoiseRemovalFilter") { enable_lut_noise_removal_filter_ = enabled; } else if (normalized_filter_name == "NoiseRemovalFilter") { enable_noise_removal_filter_ = enabled; } else if (normalized_filter_name == "HardwareNoiseRemovalFilter") { enable_hardware_noise_removal_filter_ = enabled; } else if (normalized_filter_name == "DispOutliersFilter") { enable_disp_outliers_filter_ = enabled; } } bool OBCameraNode::applyNamedDepthFilterConfig( const std::string &filter_name, bool enabled, const std::vector ¶ms, std::string &message) { const auto normalized_filter_name = normalizeDepthFilterName(filter_name); std::unordered_set requested_param_names; auto check_duplicate_param = [&requested_param_names, &message](const std::string ¶m_name) { if (param_name.empty()) { message = "Filter config param name is empty"; return false; } if (!requested_param_names.insert(param_name).second) { message = "Duplicate filter config param '" + param_name + "'"; return false; } return true; }; if (normalized_filter_name == "NoiseRemovalFilter") { const bool supported = device_->isPropertySupported(OB_PROP_DEPTH_SOFT_FILTER_BOOL, OB_PERMISSION_READ_WRITE) || device_->isPropertySupported(OB_PROP_DEPTH_MAX_DIFF_INT, OB_PERMISSION_WRITE) || device_->isPropertySupported(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT, OB_PERMISSION_WRITE); if (!supported) { message = "Filter '" + normalized_filter_name + "' is not supported by this device"; return false; } bool has_min_diff = false; bool has_max_size = false; int min_diff = 0; int max_size = 0; for (const auto ¶m : params) { const auto param_name = getDepthFilterConfigParamName(normalized_filter_name, param.name); if (!check_duplicate_param(param_name)) { return false; } double parsed_value = 0.0; if (!parseFilterConfigDouble(param.value, parsed_value, message)) { return false; } if (std::floor(parsed_value) != parsed_value) { message = "Filter config '" + param_name + "' expects an integer value"; return false; } if (param_name == "min_diff") { if (!device_->isPropertySupported(OB_PROP_DEPTH_MAX_DIFF_INT, OB_PERMISSION_WRITE)) { message = "Filter config 'min_diff' is not supported by this device"; return false; } has_min_diff = true; min_diff = static_cast(parsed_value); } else if (param_name == "max_size") { if (!device_->isPropertySupported(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT, OB_PERMISSION_WRITE)) { message = "Filter config 'max_size' is not supported by this device"; return false; } has_max_size = true; max_size = static_cast(parsed_value); } else { message = "Unknown filter config '" + param.name + "' for " + normalized_filter_name; return false; } } if (device_->isPropertySupported(OB_PROP_DEPTH_SOFT_FILTER_BOOL, OB_PERMISSION_READ_WRITE)) { device_->setBoolProperty(OB_PROP_DEPTH_SOFT_FILTER_BOOL, enabled); } if (has_min_diff) { device_->setIntProperty(OB_PROP_DEPTH_MAX_DIFF_INT, min_diff); noise_removal_filter_min_diff_ = min_diff; } if (has_max_size) { device_->setIntProperty(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT, max_size); noise_removal_filter_max_size_ = max_size; } updateDepthFilterEnabledCache(normalized_filter_name, enabled); return true; } if (normalized_filter_name == "HardwareNoiseRemovalFilter") { const bool supported = device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL, OB_PERMISSION_READ_WRITE) || device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT, OB_PERMISSION_READ_WRITE); if (!supported) { message = "Filter '" + normalized_filter_name + "' is not supported by this device"; return false; } bool has_threshold = false; double threshold = 0.0; for (const auto ¶m : params) { const auto param_name = getDepthFilterConfigParamName(normalized_filter_name, param.name); if (!check_duplicate_param(param_name)) { return false; } if (param_name != "threshold") { message = "Unknown filter config '" + param.name + "' for " + normalized_filter_name; return false; } if (!device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT, OB_PERMISSION_READ_WRITE)) { message = "Filter config 'threshold' is not supported by this device"; return false; } if (!parseFilterConfigDouble(param.value, threshold, message)) { return false; } has_threshold = true; } if (device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL, OB_PERMISSION_READ_WRITE)) { device_->setBoolProperty(OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL, enabled); } if (has_threshold) { device_->setFloatProperty(OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT, static_cast(threshold)); hardware_noise_removal_filter_threshold_ = static_cast(threshold); } updateDepthFilterEnabledCache(normalized_filter_name, enabled); return true; } if (normalized_filter_name == "DispOutliersFilter") { const bool supported = device_->isPropertySupported(OB_PROP_DEPTH_OUTLIERS_FILTER_BOOL, OB_PERMISSION_READ_WRITE) || device_->isPropertySupported(OB_PROP_DEPTH_OUTLIERS_FILTER_SEARCH_MODE_INT, OB_PERMISSION_READ_WRITE); if (!supported) { message = "Filter '" + normalized_filter_name + "' is not supported by this device"; return false; } bool has_search_mode = false; int search_mode = 0; for (const auto ¶m : params) { const auto param_name = getDepthFilterConfigParamName(normalized_filter_name, param.name); if (!check_duplicate_param(param_name)) { return false; } if (param_name != "search_mode") { message = "Unknown filter config '" + param.name + "' for " + normalized_filter_name; return false; } if (!device_->isPropertySupported(OB_PROP_DEPTH_OUTLIERS_FILTER_SEARCH_MODE_INT, OB_PERMISSION_READ_WRITE)) { message = "Filter config 'search_mode' is not supported by this device"; return false; } if (!parseDispOutliersSearchMode(param.value, search_mode, message)) { return false; } has_search_mode = true; } if (device_->isPropertySupported(OB_PROP_DEPTH_OUTLIERS_FILTER_BOOL, OB_PERMISSION_READ_WRITE)) { device_->setBoolProperty(OB_PROP_DEPTH_OUTLIERS_FILTER_BOOL, enabled); } if (has_search_mode) { device_->setIntProperty(OB_PROP_DEPTH_OUTLIERS_FILTER_SEARCH_MODE_INT, search_mode); disp_outliers_filter_search_mode_ = search_mode; } updateDepthFilterEnabledCache(normalized_filter_name, enabled); return true; } std::unique_lock depth_filter_lock(depth_filter_mutex_); auto is_same_filter = [&normalized_filter_name](const std::shared_ptr &filter) { if (!filter) { return false; } return normalizeDepthFilterName(filter->getName()) == normalized_filter_name || normalizeDepthFilterName(filter->type()) == normalized_filter_name; }; auto first_match_it = std::find_if(depth_filter_list_.begin(), depth_filter_list_.end(), [&is_same_filter](const auto &filter) { return is_same_filter(filter); }); if (first_match_it == depth_filter_list_.end() || !(*first_match_it)) { message = "Filter '" + normalized_filter_name + "' is not supported by this device"; return false; } const auto &existing_filter = *first_match_it; std::vector schema_vec; std::unordered_map schema_by_name; if (!params.empty()) { schema_vec = existing_filter->getConfigSchemaVec(); for (const auto &schema : schema_vec) { if (schema.name == nullptr || schema.name[0] == '\0') { continue; } schema_by_name.emplace(schema.name, schema); } } std::vector> parsed_params; parsed_params.reserve(params.size()); for (const auto ¶m : params) { const auto param_name = getDepthFilterConfigParamName(normalized_filter_name, param.name); if (!check_duplicate_param(param_name)) { return false; } const auto schema_it = schema_by_name.find(param_name); if (schema_it == schema_by_name.end()) { message = "Unknown filter config '" + param.name + "' for " + normalized_filter_name; return false; } double parsed_value = 0.0; if (!parseFilterConfigValue(schema_it->second, param.value, parsed_value, message)) { return false; } parsed_params.emplace_back(param_name, parsed_value); } existing_filter->enable(enabled); for (const auto &parsed_param : parsed_params) { existing_filter->setConfigValue(parsed_param.first, parsed_param.second); RCLCPP_INFO_STREAM(logger_, "Set " << normalized_filter_name << " config " << parsed_param.first << " to " << parsed_param.second); } updateDepthFilterEnabledCache(normalized_filter_name, enabled); return true; } bool OBCameraNode::applyEnhancedDepthFilterConfig( bool enabled, const std::vector &positional_params, const std::vector &named_params, std::string &message) { if (positional_params.size() > 1) { message = "EnhancedDepthFilter only supports one positional parameter"; return false; } if (!positional_params.empty() && !named_params.empty()) { message = "filter_param and filter_config cannot be used at the same time"; return false; } bool has_confidence_threshold = false; int confidence_threshold = enhanced_depth_confidence_threshold_; auto parse_confidence_threshold = [&message](double value, int &threshold) { if (std::floor(value) != value || value < 0.0 || value > 255.0) { message = "EnhancedDepthFilter confidence_threshold expects an integer value in range 0 - 255"; return false; } threshold = static_cast(value); return true; }; if (!positional_params.empty()) { if (!parse_confidence_threshold(positional_params[0], confidence_threshold)) { return false; } has_confidence_threshold = true; } for (const auto ¶m : named_params) { if (param.name != "confidence_threshold") { message = "Unknown filter config '" + param.name + "' for EnhancedDepthFilter"; return false; } double parsed_value = 0.0; if (!parseFilterConfigDouble(param.value, parsed_value, message)) { return false; } if (!parse_confidence_threshold(parsed_value, confidence_threshold)) { return false; } has_confidence_threshold = true; } std::string validate_message; if (enabled && !validateEnhancedDepthFilterConfig(validate_message)) { message = validate_message; return false; } const int previous_threshold = enhanced_depth_confidence_threshold_; if (has_confidence_threshold) { enhanced_depth_confidence_threshold_ = confidence_threshold; } if (enabled) { if (!ensureEnhancedDepthFilter(message)) { enhanced_depth_confidence_threshold_ = previous_threshold; return false; } } else if (has_confidence_threshold && enhanced_depth_filter_) { try { applyEnhancedDepthConfidenceThreshold(); } catch (const std::exception &e) { enhanced_depth_confidence_threshold_ = previous_threshold; message = e.what(); return false; } } { std::lock_guard lock(enhanced_depth_filter_mutex_); enable_enhanced_depth_.store(enabled); } filter_status_["EnhancedDepthFilter"] = enabled; publishDepthFiltersStatus(); return true; } void OBCameraNode::setFilterCallback(const std::shared_ptr &request, std::shared_ptr &response) { try { response->success = false; response->message.clear(); auto fail = [&response](const std::string &msg) { response->success = false; response->message = msg; }; const auto normalized_request_filter_name = normalizeDepthFilterName(request->filter_name); const bool is_noise_removal_filter = normalized_request_filter_name == "NoiseRemovalFilter"; const bool is_hardware_noise_removal_filter = normalized_request_filter_name == "HardwareNoiseRemovalFilter"; const bool is_disp_outliers_filter = normalized_request_filter_name == "DispOutliersFilter"; bool is_supported_by_property = false; if (is_noise_removal_filter) { is_supported_by_property = device_->isPropertySupported(OB_PROP_DEPTH_SOFT_FILTER_BOOL, OB_PERMISSION_READ_WRITE) || device_->isPropertySupported(OB_PROP_DEPTH_MAX_DIFF_INT, OB_PERMISSION_WRITE) || device_->isPropertySupported(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT, OB_PERMISSION_WRITE); } else if (is_hardware_noise_removal_filter) { is_supported_by_property = device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL, OB_PERMISSION_READ_WRITE) || device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT, OB_PERMISSION_READ_WRITE); } else if (is_disp_outliers_filter) { is_supported_by_property = device_->isPropertySupported(OB_PROP_DEPTH_OUTLIERS_FILTER_BOOL, OB_PERMISSION_READ_WRITE) || device_->isPropertySupported(OB_PROP_DEPTH_OUTLIERS_FILTER_SEARCH_MODE_INT, OB_PERMISSION_READ_WRITE); } RCLCPP_INFO_STREAM(logger_, "filter_name: " << request->filter_name << " filter_enable: " << (request->filter_enable ? "true" : "false")); const bool has_positional_params = !request->filter_param.empty(); const bool has_named_params = !request->filter_config.empty(); if (has_positional_params && has_named_params) { fail("filter_param and filter_config cannot be used at the same time"); return; } if (is_disp_outliers_filter && has_positional_params) { fail("DispOutliersFilter search_mode expects filter_config value FULL or OFFSET_80"); return; } if (normalized_request_filter_name == "EnhancedDepthFilter") { std::string message; if (!applyEnhancedDepthFilterConfig(request->filter_enable, request->filter_param, request->filter_config, message)) { fail(message); return; } response->success = true; return; } if (has_named_params || !has_positional_params) { std::string message; if (!applyNamedDepthFilterConfig(normalized_request_filter_name, request->filter_enable, request->filter_config, message)) { fail(message); return; } filter_status_[normalized_request_filter_name] = static_cast(request->filter_enable); publishDepthFiltersStatus(); response->success = true; return; } if (is_noise_removal_filter || is_hardware_noise_removal_filter || is_disp_outliers_filter) { if (!is_supported_by_property) { fail("Filter '" + normalized_request_filter_name + "' is not supported by this device"); return; } if (is_noise_removal_filter) { if (device_->isPropertySupported(OB_PROP_DEPTH_SOFT_FILTER_BOOL, OB_PERMISSION_READ_WRITE)) { device_->setBoolProperty(OB_PROP_DEPTH_SOFT_FILTER_BOOL, request->filter_enable); RCLCPP_INFO_STREAM(logger_, "enable_noise_removal_filter:" << request->filter_enable); } if (request->filter_param.size() > 1) { if (device_->isPropertySupported(OB_PROP_DEPTH_MAX_DIFF_INT, OB_PERMISSION_WRITE)) { device_->setIntProperty(OB_PROP_DEPTH_MAX_DIFF_INT, request->filter_param[0]); auto new_noise_removal_filter_min_diff = device_->getIntProperty(OB_PROP_DEPTH_MAX_DIFF_INT); RCLCPP_INFO_STREAM(logger_, "Set noise_removal_filter_min_diff: " << new_noise_removal_filter_min_diff); noise_removal_filter_min_diff_ = request->filter_param[0]; } if (device_->isPropertySupported(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT, OB_PERMISSION_WRITE)) { device_->setIntProperty(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT, request->filter_param[1]); auto new_noise_removal_filter_max_size = device_->getIntProperty(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT); RCLCPP_INFO_STREAM(logger_, "Set noise_removal_filter_max_size: " << new_noise_removal_filter_max_size); noise_removal_filter_max_size_ = request->filter_param[1]; } } enable_noise_removal_filter_ = request->filter_enable; } else if (is_hardware_noise_removal_filter) { if (device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL, OB_PERMISSION_READ_WRITE)) { device_->setBoolProperty(OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL, request->filter_enable); RCLCPP_INFO_STREAM(logger_, "Set hardware_noise_removal_filter:" << request->filter_enable); if (request->filter_param.size() > 0 && device_->isPropertySupported(OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT, OB_PERMISSION_READ_WRITE)) { if (request->filter_enable) { device_->setFloatProperty(OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT, request->filter_param[0]); RCLCPP_INFO_STREAM(logger_, "Set hardware_noise_removal_filter_threshold :" << request->filter_param[0]); hardware_noise_removal_filter_threshold_ = request->filter_param[0]; } } else { fail( "The filter switch setting is successful, but the filter parameter setting " "fails"); return; } } enable_hardware_noise_removal_filter_ = request->filter_enable; } } else { std::unique_lock depth_filter_lock(depth_filter_mutex_); auto is_same_filter = [&normalized_request_filter_name](const std::shared_ptr &filter) { if (!filter) { return false; } return normalizeDepthFilterName(filter->getName()) == normalized_request_filter_name || normalizeDepthFilterName(filter->type()) == normalized_request_filter_name; }; auto first_match_it = std::find_if(depth_filter_list_.begin(), depth_filter_list_.end(), [&is_same_filter](const auto &filter) { return is_same_filter(filter); }); if (first_match_it == depth_filter_list_.end()) { fail("Filter '" + normalized_request_filter_name + "' is not supported by this device"); return; } const auto &existing_filter = *first_match_it; if (!existing_filter) { fail("Filter '" + normalized_request_filter_name + "' is not supported by this device"); return; } if (normalized_request_filter_name == "DecimationFilter") { auto decimation_filter = existing_filter->as(); decimation_filter->enable(request->filter_enable); if (request->filter_param.size() > 0) { auto range = decimation_filter->getScaleRange(); auto decimation_filter_scale = request->filter_param[0]; if (decimation_filter_scale <= range.max && decimation_filter_scale >= range.min) { RCLCPP_INFO_STREAM(logger_, "Set decimation filter scale value to " << decimation_filter_scale); decimation_filter->setScaleValue(decimation_filter_scale); } if (decimation_filter_scale != -1 && (decimation_filter_scale < range.min || decimation_filter_scale > range.max)) { RCLCPP_ERROR_STREAM(logger_, "Decimation filter scale value is out of range " << range.min << " - " << range.max); fail("Decimation filter scale value is out of range"); return; } if (decimation_filter_scale <= range.max && decimation_filter_scale >= range.min) { decimation_filter_scale_ = decimation_filter_scale; } } else { fail("The filter switch setting is successful, but the filter parameter setting fails"); return; } enable_decimation_filter_ = request->filter_enable; } else if (normalized_request_filter_name == "HDRMerge") { auto hdr_merge_filter = existing_filter->as(); hdr_merge_filter->enable(request->filter_enable); if (request->filter_param.size() > 3) { auto config = OBHdrConfig(); config.enable = true; config.exposure_1 = request->filter_param[0]; config.gain_1 = request->filter_param[1]; config.exposure_2 = request->filter_param[2]; config.gain_2 = request->filter_param[3]; device_->setStructuredData(OB_STRUCT_DEPTH_HDR_CONFIG, reinterpret_cast(&config), sizeof(config)); RCLCPP_INFO_STREAM(logger_, "Set HDR merge filter params: " << "\nexposure_1: " << request->filter_param[0] << "\ngain_1: " << request->filter_param[1] << "\nexposure_2: " << request->filter_param[2] << "\ngain_2: " << request->filter_param[3]); hdr_merge_exposure_1_ = request->filter_param[0]; hdr_merge_gain_1_ = request->filter_param[1]; hdr_merge_exposure_2_ = request->filter_param[2]; hdr_merge_gain_2_ = request->filter_param[3]; } else { fail("The filter switch setting is successful, but the filter parameter setting fails"); return; } enable_hdr_merge_ = request->filter_enable; } else if (normalized_request_filter_name == "SequenceIdFilter") { auto sequenced_filter = existing_filter->as(); sequenced_filter->enable(request->filter_enable); if (request->filter_param.size() > 0) { sequenced_filter->selectSequenceId(request->filter_param[0]); RCLCPP_INFO_STREAM(logger_, "Set sequenced filter selectSequenceId value to " << request->filter_param[0]); sequence_id_filter_id_ = request->filter_param[0]; } else { fail("The filter switch setting is successful, but the filter parameter setting fails"); return; } enable_sequence_id_filter_ = request->filter_enable; } else if (normalized_request_filter_name == "ThresholdFilter") { auto threshold_filter = existing_filter->as(); threshold_filter->enable(request->filter_enable); if (request->filter_param.size() > 1) { auto threshold_filter_min = request->filter_param[0]; auto threshold_filter_max = request->filter_param[1]; threshold_filter->setValueRange(threshold_filter_min, threshold_filter_max); RCLCPP_INFO_STREAM(logger_, "Set threshold filter value range to " << threshold_filter_min << " - " << threshold_filter_max); threshold_filter_min_ = threshold_filter_min; threshold_filter_max_ = threshold_filter_max; } else { fail("The filter switch setting is successful, but the filter parameter setting fails"); return; } enable_threshold_filter_ = request->filter_enable; } else if (normalized_request_filter_name == "SpatialAdvancedFilter") { auto spatial_filter = existing_filter->as(); spatial_filter->enable(request->filter_enable); if (request->filter_param.size() > 3) { OBSpatialAdvancedFilterParams params{}; params.alpha = request->filter_param[0]; params.disp_diff = request->filter_param[1]; params.magnitude = request->filter_param[2]; params.radius = request->filter_param[3]; spatial_filter->setFilterParams(params); RCLCPP_INFO_STREAM(logger_, "Set SpatialFilter params: " << "\nalpha:" << params.alpha << "\ndisp_diff:" << params.disp_diff << "\nmagnitude:" << static_cast(params.magnitude) << "\nradius:" << params.radius); spatial_filter_alpha_ = params.alpha; spatial_filter_diff_threshold_ = params.disp_diff; spatial_filter_magnitude_ = params.magnitude; spatial_filter_radius_ = params.radius; } else { fail("The filter switch setting is successful, but the filter parameter setting fails"); return; } enable_spatial_filter_ = request->filter_enable; } else if (normalized_request_filter_name == "TemporalFilter") { auto temporal_filter = existing_filter->as(); temporal_filter->enable(request->filter_enable); if (request->filter_param.size() > 1) { temporal_filter->setDiffScale(request->filter_param[0]); temporal_filter->setWeight(request->filter_param[1]); RCLCPP_INFO_STREAM( logger_, "Set TemporalFilter params: " << "\ndiff_scale:" << request->filter_param[0] << "\nweight:" << request->filter_param[1]); temporal_filter_diff_threshold_ = request->filter_param[0]; temporal_filter_weight_ = request->filter_param[1]; } else { fail("The filter switch setting is successful, but the filter parameter setting fails"); return; } enable_temporal_filter_ = request->filter_enable; } else if (normalized_request_filter_name == "SpatialFastFilter") { auto spatial_fast_filter = existing_filter->as(); spatial_fast_filter->enable(request->filter_enable); if (request->filter_param.size() > 0) { OBSpatialFastFilterParams params{}; params.radius = request->filter_param[0]; spatial_fast_filter->setFilterParams(params); RCLCPP_INFO_STREAM(logger_, "Set SpatialFastFilter radius to " << static_cast(params.radius)); spatial_fast_filter_radius_ = params.radius; } else { fail("The filter switch setting is successful, but the filter parameter setting fails"); return; } enable_spatial_fast_filter_ = request->filter_enable; } else if (normalized_request_filter_name == "SpatialModerateFilter") { auto spatial_moderate_filter = existing_filter->as(); spatial_moderate_filter->enable(request->filter_enable); if (request->filter_param.size() > 2) { OBSpatialModerateFilterParams params{}; params.disp_diff = request->filter_param[0]; params.magnitude = request->filter_param[1]; params.radius = request->filter_param[2]; spatial_moderate_filter->setFilterParams(params); RCLCPP_INFO_STREAM(logger_, "Set SpatialModerateFilter params: " << "\ndisp_diff:" << params.disp_diff << "\nmagnitude:" << static_cast(params.magnitude) << "\nradius:" << static_cast(params.radius)); spatial_moderate_filter_diff_threshold_ = params.disp_diff; spatial_moderate_filter_magnitude_ = params.magnitude; spatial_moderate_filter_radius_ = params.radius; } else { fail("The filter switch setting is successful, but the filter parameter setting fails"); return; } enable_spatial_moderate_filter_ = request->filter_enable; } else if (normalized_request_filter_name == "FalsePositiveFilter") { auto false_positive_filter = existing_filter->as(); false_positive_filter->enable(request->filter_enable); enable_false_positive_filter_ = request->filter_enable; } else if (normalized_request_filter_name == "MgcNoiseRemovalFilter") { auto mgc_filter = existing_filter->as(); mgc_filter->enable(request->filter_enable); enable_mgc_noise_removal_filter_ = request->filter_enable; } else if (normalized_request_filter_name == "LutNoiseRemovalFilter") { auto lut_filter = existing_filter->as(); lut_filter->enable(request->filter_enable); enable_lut_noise_removal_filter_ = request->filter_enable; } else if (normalized_request_filter_name == "EdgeNoiseRemovalFilter") { existing_filter->enable(request->filter_enable); enable_edge_noise_removal_filter_ = request->filter_enable; } else { fail(normalized_request_filter_name + " cannot be set"); return; } } filter_status_[normalized_request_filter_name] = static_cast(request->filter_enable); publishDepthFiltersStatus(); response->success = true; } catch (const ob::Error &e) { response->success = false; response->message = "Failed to set filter: " + orbbec_camera::formatObErrorWithStatus(e); RCLCPP_ERROR_STREAM(logger_, "Failed to set filter: " << orbbec_camera::formatObErrorWithStatus(e)); } catch (const std::exception &e) { response->success = false; response->message = std::string("Failed to set filter: ") + e.what(); RCLCPP_ERROR_STREAM(logger_, "Failed to set filter: " << e.what()); } catch (...) { response->success = false; response->message = "unknown error"; RCLCPP_ERROR_STREAM(logger_, "unknown error"); } } bool OBCameraNode::isWriteCustomerDataSuccess() const { return write_customer_data_success_.load(); } } // namespace orbbec_camera