diff --git a/orbbec_camera/CMakeLists.txt b/orbbec_camera/CMakeLists.txt
index 3d64408e..79c53699 100644
--- a/orbbec_camera/CMakeLists.txt
+++ b/orbbec_camera/CMakeLists.txt
@@ -269,6 +269,22 @@ orbbec_target_dependencies(${PROJECT_NAME}
rclcpp_components_register_node(
${PROJECT_NAME} PLUGIN "orbbec_camera::OBCameraNodeDriver" EXECUTABLE orbbec_camera_node
)
+
+add_library(gige_action_command_component SHARED src/gige_action_command_node.cpp)
+target_include_directories(gige_action_command_component PUBLIC ${COMMON_INCLUDE_DIRS})
+target_link_directories(gige_action_command_component PRIVATE ${ORBBEC_LIBS_DIR})
+target_link_libraries(gige_action_command_component
+ ${orbbec_camera_msgs_TARGETS}
+ OrbbecSDK
+ rclcpp::rclcpp
+)
+orbbec_target_dependencies(gige_action_command_component rclcpp_components)
+rclcpp_components_register_node(
+ gige_action_command_component
+ PLUGIN "orbbec_camera::GigEActionCommandNode"
+ EXECUTABLE gige_action_command_node
+)
+
# Add nodes using the macro
add_orbbec_executable(list_devices_node tools/list_devices_node.cpp)
add_orbbec_executable(list_depth_work_mode_node tools/list_depth_work_mode.cpp)
@@ -360,7 +376,8 @@ rclcpp_components_register_node(
)
# Install rules
-install(TARGETS ${PROJECT_NAME} frame_latency start_benchmark multi_save_rgbir ARCHIVE DESTINATION lib
+install(TARGETS ${PROJECT_NAME} gige_action_command_component frame_latency start_benchmark
+ multi_save_rgbir ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib RUNTIME DESTINATION bin
)
@@ -391,7 +408,12 @@ install(TARGETS list_devices_node list_depth_work_mode_node list_camera_profile_
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
+ find_package(ament_cmake_gtest REQUIRED)
ament_lint_auto_find_test_dependencies()
+
+ ament_add_gtest(camera_info_distortion_test test/camera_info_distortion_test.cpp)
+ target_link_directories(camera_info_distortion_test PRIVATE ${ORBBEC_LIBS_DIR})
+ target_link_libraries(camera_info_distortion_test ${PROJECT_NAME})
endif()
ament_export_include_directories(include)
diff --git a/orbbec_camera/SDK/arm64/include/libobsensor/h/Advanced.h b/orbbec_camera/SDK/arm64/include/libobsensor/h/Advanced.h
index abff49dc..800b77df 100644
--- a/orbbec_camera/SDK/arm64/include/libobsensor/h/Advanced.h
+++ b/orbbec_camera/SDK/arm64/include/libobsensor/h/Advanced.h
@@ -101,6 +101,16 @@ OB_EXPORT void ob_delete_depth_work_mode_list(ob_depth_work_mode_list *work_mode
*/
OB_EXPORT const char *ob_device_get_current_preset_name(const ob_device *device, ob_error **error);
+/**
+ * @brief Get the version of the current depth work mode.
+ *
+ * @param[in] device The device object.
+ * @param[out] error Pointer to an error object that will be set if an error occurs.
+ *
+ * @return const char* The version string, e.g. "1.2.3". Empty string when the device does not
+ * support versioned modes.
+ */
+OB_EXPORT const char *ob_device_get_current_preset_depth_work_mode_version(const ob_device *device, ob_error **error);
/**
* @brief Get the available preset list.
* @attention After loading the preset, the settings in the preset will set to the device immediately. Therefore, it is recommended to re-read the device
@@ -113,6 +123,17 @@ OB_EXPORT const char *ob_device_get_current_preset_name(const ob_device *device,
*/
OB_EXPORT void ob_device_load_preset(ob_device *device, const char *preset_name, ob_error **error);
+/**
+ * @brief Load the preset by preset name and the target depth work mode version.
+ * When multiple presets share the same name, the version selects the exact one.
+ *
+ * @param[in] device The device object.
+ * @param[in] preset_name The preset name. The name should be one of the preset names returned by @ref ob_device_get_available_preset_list.
+ * @param[in] version The target depth work mode version string, e.g. "1.2.3". Must not be empty.
+ * @param[out] error Pointer to an error object that will be set if an error occurs.
+ */
+OB_EXPORT void ob_device_load_preset_by_depth_work_mode_version(ob_device *device, const char *preset_name, const char *version, ob_error **error);
+
/**
* @brief Load preset from json string.
* @brief After loading the custom preset, the settings in the custom preset will set to the device immediately.
@@ -217,6 +238,18 @@ OB_EXPORT const char *ob_device_preset_list_get_name(const ob_device_preset_list
*/
OB_EXPORT bool ob_device_preset_list_has_preset(const ob_device_preset_list *preset_list, const char *preset_name, ob_error **error);
+/**
+ * @brief Get the target depth work mode version of the preset at the specified index.
+ *
+ * @param[in] preset_list Data structure containing a list of presets
+ * @param[in] index Index of the target preset
+ * @param[out] error Pointer to an error object that will be set if an error occurs.
+ *
+ * @return const char* The version string of the depth work mode, e.g. "1.2.3". Empty string when the
+ * device does not support versioned modes.
+ */
+OB_EXPORT const char *ob_device_preset_list_get_depth_work_mode_version(const ob_device_preset_list *preset_list, uint32_t index, ob_error **error);
+
/**
* @brief Check if the device supports the frame interleave feature.
*
diff --git a/orbbec_camera/SDK/arm64/include/libobsensor/h/Context.h b/orbbec_camera/SDK/arm64/include/libobsensor/h/Context.h
index 3c84c5be..ab9ee954 100644
--- a/orbbec_camera/SDK/arm64/include/libobsensor/h/Context.h
+++ b/orbbec_camera/SDK/arm64/include/libobsensor/h/Context.h
@@ -80,6 +80,20 @@ OB_EXPORT void ob_enable_net_device_enumeration(ob_context *context, bool enable
*/
OB_EXPORT bool ob_force_ip_config(const char *macAddress, ob_net_ip_config config, ob_error **error);
+/**
+ * @brief Send a GigE Vision Action Command via GVCP.
+ *
+ * @param[in] deviceKey Device key to match.
+ * @param[in] groupKey Group key to match against the camera's Action blocks.
+ * @param[in] groupMask Group mask, bitwise-ANDed with each Action block's mask.
+ * @param[in] destIp Destination IPv4 address. NULL or "255.255.255.255" uses broadcast.
+ * @param[in] scheduledTime PTP absolute timestamp. 0 = immediate trigger, non-zero = scheduled.
+ * @param[out] error Pointer to an error object that will be populated if an error occurs.
+ *
+ * @return bool true on success. Fire-and-forget; does not wait for camera ACK.
+ */
+OB_EXPORT bool ob_send_action_command(uint32_t deviceKey, uint32_t groupKey, uint32_t groupMask, const char *destIp, uint64_t scheduledTime, ob_error **error);
+
/**
* @brief Set the GVCP port scheme used for network device discovery and control.
*
diff --git a/orbbec_camera/SDK/arm64/include/libobsensor/h/ObTypes.h b/orbbec_camera/SDK/arm64/include/libobsensor/h/ObTypes.h
index 1a234540..6590efd2 100644
--- a/orbbec_camera/SDK/arm64/include/libobsensor/h/ObTypes.h
+++ b/orbbec_camera/SDK/arm64/include/libobsensor/h/ObTypes.h
@@ -1578,6 +1578,12 @@ typedef enum {
* @brief The device captures data in software synchronization mode, starting acquisition based on the system time.
*/
OB_MULTI_DEVICE_SYNC_MODE_SOFTWARE_SYNCED = 1 << 8,
+
+ /**
+ * @brief Group actions trigger mode.
+ * @note The device waits for a GVCP ACTION_CMD matching the configured action parameters.
+ */
+ OB_MULTI_DEVICE_SYNC_MODE_GROUP_ACTIONS = 1 << 9,
} ob_multi_device_sync_mode,
OBMultiDeviceSyncMode;
diff --git a/orbbec_camera/SDK/arm64/include/libobsensor/h/Property.h b/orbbec_camera/SDK/arm64/include/libobsensor/h/Property.h
index 486a54f0..02c6a848 100644
--- a/orbbec_camera/SDK/arm64/include/libobsensor/h/Property.h
+++ b/orbbec_camera/SDK/arm64/include/libobsensor/h/Property.h
@@ -674,6 +674,7 @@ typedef enum {
/**
* @brief Enable FPS boost in trigger mode
+ * Not effective for devices connected via USB 2.x or lower
*/
OB_PROP_FPS_BOOST_BOOL = 275,
@@ -695,6 +696,44 @@ typedef enum {
*/
OB_PROP_COLOR_AE_AWB_STATUS_INT = 287,
+ /**
+ * @brief Number of Action Signal blocks supported by the device
+ */
+ OB_PROP_ACTION_SIGNAL_COUNT_INT = 296,
+
+ /**
+ * @brief Action Device Key, shared across all Action Commands
+ */
+ OB_PROP_ACTION_DEVICE_KEY_INT = 297,
+
+ /**
+ * @brief Maximum number of scheduled Action Commands that can be queued by the device
+ */
+ OB_PROP_ACTION_SCHEDULED_COMMAND_QUEUE_SIZE_INT = 298,
+
+ /**
+ * @brief Action Signal selector, 0..N-1; subsequent selector-scoped reads/writes target the selected block.
+ * @note The selector is stateful. The selector, Group Key, and Group Mask
+ * must be written serially as one caller-controlled sequence. Do not
+ * interleave this sequence with Action Command property operations
+ * from another thread on the same device.
+ */
+ OB_PROP_ACTION_SELECTOR_INT = 299,
+
+ /**
+ * @brief Action Group Key for the currently selected block.
+ * @note Must be written serially with OB_PROP_ACTION_SELECTOR_INT and
+ * OB_PROP_ACTION_GROUP_MASK_INT on the same device.
+ */
+ OB_PROP_ACTION_GROUP_KEY_INT = 300,
+
+ /**
+ * @brief Action Group Mask for the currently selected block.
+ * @note Must be written serially with OB_PROP_ACTION_SELECTOR_INT and
+ * OB_PROP_ACTION_GROUP_KEY_INT on the same device.
+ */
+ OB_PROP_ACTION_GROUP_MASK_INT = 301,
+
/**
* @brief Baseline calibration parameters
*/
@@ -978,6 +1017,11 @@ typedef enum {
*/
OB_PROP_DEPTH_AUTO_EXPOSURE_PRIORITY_INT = 2052,
+ /**
+ * @brief Color camera WB control
+ */
+ OB_PROP_COLOR_WB_CTRL_INT = 2053,
+
/**
* @brief Software disparity to depth
*/
diff --git a/orbbec_camera/SDK/arm64/include/libobsensor/hpp/Context.hpp b/orbbec_camera/SDK/arm64/include/libobsensor/hpp/Context.hpp
index 7201ed05..d01a52f0 100644
--- a/orbbec_camera/SDK/arm64/include/libobsensor/hpp/Context.hpp
+++ b/orbbec_camera/SDK/arm64/include/libobsensor/hpp/Context.hpp
@@ -157,6 +157,24 @@ public:
return res;
}
+ /**
+ * @brief Send a GigE Vision Action Command via GVCP.
+ *
+ * @param deviceKey Device key to match.
+ * @param groupKey Group key to match.
+ * @param groupMask Group mask, bitwise-ANDed with Action block masks.
+ * @param destIp Destination IPv4 address. Defaults to "255.255.255.255" for broadcast.
+ * @param scheduledTime PTP timestamp. 0 = immediate, non-zero = scheduled.
+ *
+ * @return bool true on success.
+ */
+ bool sendActionCommand(uint32_t deviceKey, uint32_t groupKey, uint32_t groupMask, const char *destIp = "255.255.255.255", uint64_t scheduledTime = 0) {
+ ob_error *error = nullptr;
+ bool ok = ob_send_action_command(deviceKey, groupKey, groupMask, destIp, scheduledTime, &error);
+ Error::handle(&error);
+ return ok;
+ }
+
/**
* @brief Set the GVCP port scheme used for network device discovery and control.
*
diff --git a/orbbec_camera/SDK/arm64/include/libobsensor/hpp/Device.hpp b/orbbec_camera/SDK/arm64/include/libobsensor/hpp/Device.hpp
index 37a420c2..b483a9b8 100644
--- a/orbbec_camera/SDK/arm64/include/libobsensor/hpp/Device.hpp
+++ b/orbbec_camera/SDK/arm64/include/libobsensor/hpp/Device.hpp
@@ -880,6 +880,31 @@ public:
Error::handle(&error);
}
+ /**
+ * @brief load the preset by preset name and the target depth work mode version.
+ * The version is forwarded to the depth work mode switch when loading the preset.
+ * @param[in] presetName The preset name to set. The name should be one of the preset names returned by @ref getAvailablePresetList.
+ * @param[in] version The target depth work mode version string, e.g. "1.2.3". Must not be empty.
+ */
+ void loadPreset(const char *presetName, const char *version) const {
+ ob_error *error = nullptr;
+ ob_device_load_preset_by_depth_work_mode_version(impl_, presetName, version, &error);
+ Error::handle(&error);
+ }
+
+ /**
+ * @brief Get the version of the current depth work mode.
+ *
+ * @return const char* The version string, e.g. "1.2.3". Empty string when the device
+ * does not support versioned modes.
+ */
+ const char *getCurrentPresetDepthWorkModeVersion() const {
+ ob_error *error = nullptr;
+ const char *version = ob_device_get_current_preset_depth_work_mode_version(impl_, &error);
+ Error::handle(&error);
+ return version;
+ }
+
/**
* @brief Get available preset list
* @brief The available preset list usually defined by the device manufacturer and restores on the device.
@@ -1836,6 +1861,21 @@ public:
return name;
}
+ /**
+ * @brief Get the depth work mode version of the preset at the specified index
+ *
+ * @param[in] index the index of the device preset
+ *
+ * @return const char* the version string of the depth work mode, e.g. "1.2.3". Empty string
+ * when the device does not support versioned modes.
+ */
+ const char *getDepthWorkModeVersion(uint32_t index) {
+ ob_error *error = nullptr;
+ const char *version = ob_device_preset_list_get_depth_work_mode_version(impl_, index, &error);
+ Error::handle(&error);
+ return version;
+ }
+
/**
* @brief check if the preset list contains the special name preset.
* @param[in] name The name of the preset
diff --git a/orbbec_camera/SDK/arm64/include/libobsensor/hpp/Filter.hpp b/orbbec_camera/SDK/arm64/include/libobsensor/hpp/Filter.hpp
index 591f2f99..0a2194d9 100644
--- a/orbbec_camera/SDK/arm64/include/libobsensor/hpp/Filter.hpp
+++ b/orbbec_camera/SDK/arm64/include/libobsensor/hpp/Filter.hpp
@@ -177,8 +177,12 @@ public:
* @return std::shared_ptr< Frame > The processed frame.
*/
virtual std::shared_ptr process(std::shared_ptr frame) const {
+ const ob_frame *frameImpl = nullptr;
+ if(frame) {
+ frameImpl = frame->getImpl();
+ }
ob_error *error = nullptr;
- auto result = ob_filter_process(impl_, frame->getImpl(), &error);
+ auto result = ob_filter_process(impl_, frameImpl, &error);
Error::handle(&error);
if(!result) {
return nullptr;
@@ -192,8 +196,12 @@ public:
* @param[in] frame The pending frame. The processing result is returned by the callback function.
*/
virtual void pushFrame(std::shared_ptr frame) const {
+ const ob_frame *frameImpl = nullptr;
+ if(frame) {
+ frameImpl = frame->getImpl();
+ }
ob_error *error = nullptr;
- ob_filter_push_frame(impl_, frame->getImpl(), &error);
+ ob_filter_push_frame(impl_, frameImpl, &error);
Error::handle(&error);
}
diff --git a/orbbec_camera/SDK/arm64/lib/OrbbecSDKConfig-release.cmake b/orbbec_camera/SDK/arm64/lib/OrbbecSDKConfig-release.cmake
index 231fafb1..c3bd1654 100644
--- a/orbbec_camera/SDK/arm64/lib/OrbbecSDKConfig-release.cmake
+++ b/orbbec_camera/SDK/arm64/lib/OrbbecSDKConfig-release.cmake
@@ -8,12 +8,12 @@ set(CMAKE_IMPORT_FILE_VERSION 1)
# Import target "ob::OrbbecSDK" for configuration "Release"
set_property(TARGET ob::OrbbecSDK APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_target_properties(ob::OrbbecSDK PROPERTIES
- IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libOrbbecSDK.so.2.10.1"
+ IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libOrbbecSDK.so.2.10.2"
IMPORTED_SONAME_RELEASE "libOrbbecSDK.so.2"
)
list(APPEND _cmake_import_check_targets ob::OrbbecSDK )
-list(APPEND _cmake_import_check_files_for_ob::OrbbecSDK "${_IMPORT_PREFIX}/lib/libOrbbecSDK.so.2.10.1" )
+list(APPEND _cmake_import_check_files_for_ob::OrbbecSDK "${_IMPORT_PREFIX}/lib/libOrbbecSDK.so.2.10.2" )
# Commands beyond this point should not need to know the version.
set(CMAKE_IMPORT_FILE_VERSION)
diff --git a/orbbec_camera/SDK/arm64/lib/OrbbecSDKVersion.cmake b/orbbec_camera/SDK/arm64/lib/OrbbecSDKVersion.cmake
index 7851ea03..515957a9 100644
--- a/orbbec_camera/SDK/arm64/lib/OrbbecSDKVersion.cmake
+++ b/orbbec_camera/SDK/arm64/lib/OrbbecSDKVersion.cmake
@@ -9,19 +9,19 @@
# The variable CVF_VERSION must be set before calling configure_file().
-set(PACKAGE_VERSION "2.10.1")
+set(PACKAGE_VERSION "2.10.2")
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
- if("2.10.1" MATCHES "^([0-9]+)\\.")
+ if("2.10.2" MATCHES "^([0-9]+)\\.")
set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}")
if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0)
string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}")
endif()
else()
- set(CVF_VERSION_MAJOR "2.10.1")
+ set(CVF_VERSION_MAJOR "2.10.2")
endif()
if(PACKAGE_FIND_VERSION_RANGE)
diff --git a/orbbec_camera/SDK/arm64/lib/libOrbbecSDK.so.2 b/orbbec_camera/SDK/arm64/lib/libOrbbecSDK.so.2
index 027ba8ff..6687316d 120000
--- a/orbbec_camera/SDK/arm64/lib/libOrbbecSDK.so.2
+++ b/orbbec_camera/SDK/arm64/lib/libOrbbecSDK.so.2
@@ -1 +1 @@
-libOrbbecSDK.so.2.10.1
\ No newline at end of file
+libOrbbecSDK.so.2.10.2
\ No newline at end of file
diff --git a/orbbec_camera/SDK/arm64/lib/libOrbbecSDK.so.2.10.1 b/orbbec_camera/SDK/arm64/lib/libOrbbecSDK.so.2.10.2
similarity index 67%
rename from orbbec_camera/SDK/arm64/lib/libOrbbecSDK.so.2.10.1
rename to orbbec_camera/SDK/arm64/lib/libOrbbecSDK.so.2.10.2
index c54fc1cd..1eb61970 100644
Binary files a/orbbec_camera/SDK/arm64/lib/libOrbbecSDK.so.2.10.1 and b/orbbec_camera/SDK/arm64/lib/libOrbbecSDK.so.2.10.2 differ
diff --git a/orbbec_camera/SDK/x64/include/libobsensor/h/Advanced.h b/orbbec_camera/SDK/x64/include/libobsensor/h/Advanced.h
index abff49dc..800b77df 100644
--- a/orbbec_camera/SDK/x64/include/libobsensor/h/Advanced.h
+++ b/orbbec_camera/SDK/x64/include/libobsensor/h/Advanced.h
@@ -101,6 +101,16 @@ OB_EXPORT void ob_delete_depth_work_mode_list(ob_depth_work_mode_list *work_mode
*/
OB_EXPORT const char *ob_device_get_current_preset_name(const ob_device *device, ob_error **error);
+/**
+ * @brief Get the version of the current depth work mode.
+ *
+ * @param[in] device The device object.
+ * @param[out] error Pointer to an error object that will be set if an error occurs.
+ *
+ * @return const char* The version string, e.g. "1.2.3". Empty string when the device does not
+ * support versioned modes.
+ */
+OB_EXPORT const char *ob_device_get_current_preset_depth_work_mode_version(const ob_device *device, ob_error **error);
/**
* @brief Get the available preset list.
* @attention After loading the preset, the settings in the preset will set to the device immediately. Therefore, it is recommended to re-read the device
@@ -113,6 +123,17 @@ OB_EXPORT const char *ob_device_get_current_preset_name(const ob_device *device,
*/
OB_EXPORT void ob_device_load_preset(ob_device *device, const char *preset_name, ob_error **error);
+/**
+ * @brief Load the preset by preset name and the target depth work mode version.
+ * When multiple presets share the same name, the version selects the exact one.
+ *
+ * @param[in] device The device object.
+ * @param[in] preset_name The preset name. The name should be one of the preset names returned by @ref ob_device_get_available_preset_list.
+ * @param[in] version The target depth work mode version string, e.g. "1.2.3". Must not be empty.
+ * @param[out] error Pointer to an error object that will be set if an error occurs.
+ */
+OB_EXPORT void ob_device_load_preset_by_depth_work_mode_version(ob_device *device, const char *preset_name, const char *version, ob_error **error);
+
/**
* @brief Load preset from json string.
* @brief After loading the custom preset, the settings in the custom preset will set to the device immediately.
@@ -217,6 +238,18 @@ OB_EXPORT const char *ob_device_preset_list_get_name(const ob_device_preset_list
*/
OB_EXPORT bool ob_device_preset_list_has_preset(const ob_device_preset_list *preset_list, const char *preset_name, ob_error **error);
+/**
+ * @brief Get the target depth work mode version of the preset at the specified index.
+ *
+ * @param[in] preset_list Data structure containing a list of presets
+ * @param[in] index Index of the target preset
+ * @param[out] error Pointer to an error object that will be set if an error occurs.
+ *
+ * @return const char* The version string of the depth work mode, e.g. "1.2.3". Empty string when the
+ * device does not support versioned modes.
+ */
+OB_EXPORT const char *ob_device_preset_list_get_depth_work_mode_version(const ob_device_preset_list *preset_list, uint32_t index, ob_error **error);
+
/**
* @brief Check if the device supports the frame interleave feature.
*
diff --git a/orbbec_camera/SDK/x64/include/libobsensor/h/Context.h b/orbbec_camera/SDK/x64/include/libobsensor/h/Context.h
index 3c84c5be..ab9ee954 100644
--- a/orbbec_camera/SDK/x64/include/libobsensor/h/Context.h
+++ b/orbbec_camera/SDK/x64/include/libobsensor/h/Context.h
@@ -80,6 +80,20 @@ OB_EXPORT void ob_enable_net_device_enumeration(ob_context *context, bool enable
*/
OB_EXPORT bool ob_force_ip_config(const char *macAddress, ob_net_ip_config config, ob_error **error);
+/**
+ * @brief Send a GigE Vision Action Command via GVCP.
+ *
+ * @param[in] deviceKey Device key to match.
+ * @param[in] groupKey Group key to match against the camera's Action blocks.
+ * @param[in] groupMask Group mask, bitwise-ANDed with each Action block's mask.
+ * @param[in] destIp Destination IPv4 address. NULL or "255.255.255.255" uses broadcast.
+ * @param[in] scheduledTime PTP absolute timestamp. 0 = immediate trigger, non-zero = scheduled.
+ * @param[out] error Pointer to an error object that will be populated if an error occurs.
+ *
+ * @return bool true on success. Fire-and-forget; does not wait for camera ACK.
+ */
+OB_EXPORT bool ob_send_action_command(uint32_t deviceKey, uint32_t groupKey, uint32_t groupMask, const char *destIp, uint64_t scheduledTime, ob_error **error);
+
/**
* @brief Set the GVCP port scheme used for network device discovery and control.
*
diff --git a/orbbec_camera/SDK/x64/include/libobsensor/h/ObTypes.h b/orbbec_camera/SDK/x64/include/libobsensor/h/ObTypes.h
index 1a234540..6590efd2 100644
--- a/orbbec_camera/SDK/x64/include/libobsensor/h/ObTypes.h
+++ b/orbbec_camera/SDK/x64/include/libobsensor/h/ObTypes.h
@@ -1578,6 +1578,12 @@ typedef enum {
* @brief The device captures data in software synchronization mode, starting acquisition based on the system time.
*/
OB_MULTI_DEVICE_SYNC_MODE_SOFTWARE_SYNCED = 1 << 8,
+
+ /**
+ * @brief Group actions trigger mode.
+ * @note The device waits for a GVCP ACTION_CMD matching the configured action parameters.
+ */
+ OB_MULTI_DEVICE_SYNC_MODE_GROUP_ACTIONS = 1 << 9,
} ob_multi_device_sync_mode,
OBMultiDeviceSyncMode;
diff --git a/orbbec_camera/SDK/x64/include/libobsensor/h/Property.h b/orbbec_camera/SDK/x64/include/libobsensor/h/Property.h
index 486a54f0..02c6a848 100644
--- a/orbbec_camera/SDK/x64/include/libobsensor/h/Property.h
+++ b/orbbec_camera/SDK/x64/include/libobsensor/h/Property.h
@@ -674,6 +674,7 @@ typedef enum {
/**
* @brief Enable FPS boost in trigger mode
+ * Not effective for devices connected via USB 2.x or lower
*/
OB_PROP_FPS_BOOST_BOOL = 275,
@@ -695,6 +696,44 @@ typedef enum {
*/
OB_PROP_COLOR_AE_AWB_STATUS_INT = 287,
+ /**
+ * @brief Number of Action Signal blocks supported by the device
+ */
+ OB_PROP_ACTION_SIGNAL_COUNT_INT = 296,
+
+ /**
+ * @brief Action Device Key, shared across all Action Commands
+ */
+ OB_PROP_ACTION_DEVICE_KEY_INT = 297,
+
+ /**
+ * @brief Maximum number of scheduled Action Commands that can be queued by the device
+ */
+ OB_PROP_ACTION_SCHEDULED_COMMAND_QUEUE_SIZE_INT = 298,
+
+ /**
+ * @brief Action Signal selector, 0..N-1; subsequent selector-scoped reads/writes target the selected block.
+ * @note The selector is stateful. The selector, Group Key, and Group Mask
+ * must be written serially as one caller-controlled sequence. Do not
+ * interleave this sequence with Action Command property operations
+ * from another thread on the same device.
+ */
+ OB_PROP_ACTION_SELECTOR_INT = 299,
+
+ /**
+ * @brief Action Group Key for the currently selected block.
+ * @note Must be written serially with OB_PROP_ACTION_SELECTOR_INT and
+ * OB_PROP_ACTION_GROUP_MASK_INT on the same device.
+ */
+ OB_PROP_ACTION_GROUP_KEY_INT = 300,
+
+ /**
+ * @brief Action Group Mask for the currently selected block.
+ * @note Must be written serially with OB_PROP_ACTION_SELECTOR_INT and
+ * OB_PROP_ACTION_GROUP_KEY_INT on the same device.
+ */
+ OB_PROP_ACTION_GROUP_MASK_INT = 301,
+
/**
* @brief Baseline calibration parameters
*/
@@ -978,6 +1017,11 @@ typedef enum {
*/
OB_PROP_DEPTH_AUTO_EXPOSURE_PRIORITY_INT = 2052,
+ /**
+ * @brief Color camera WB control
+ */
+ OB_PROP_COLOR_WB_CTRL_INT = 2053,
+
/**
* @brief Software disparity to depth
*/
diff --git a/orbbec_camera/SDK/x64/include/libobsensor/hpp/Context.hpp b/orbbec_camera/SDK/x64/include/libobsensor/hpp/Context.hpp
index 7201ed05..d01a52f0 100644
--- a/orbbec_camera/SDK/x64/include/libobsensor/hpp/Context.hpp
+++ b/orbbec_camera/SDK/x64/include/libobsensor/hpp/Context.hpp
@@ -157,6 +157,24 @@ public:
return res;
}
+ /**
+ * @brief Send a GigE Vision Action Command via GVCP.
+ *
+ * @param deviceKey Device key to match.
+ * @param groupKey Group key to match.
+ * @param groupMask Group mask, bitwise-ANDed with Action block masks.
+ * @param destIp Destination IPv4 address. Defaults to "255.255.255.255" for broadcast.
+ * @param scheduledTime PTP timestamp. 0 = immediate, non-zero = scheduled.
+ *
+ * @return bool true on success.
+ */
+ bool sendActionCommand(uint32_t deviceKey, uint32_t groupKey, uint32_t groupMask, const char *destIp = "255.255.255.255", uint64_t scheduledTime = 0) {
+ ob_error *error = nullptr;
+ bool ok = ob_send_action_command(deviceKey, groupKey, groupMask, destIp, scheduledTime, &error);
+ Error::handle(&error);
+ return ok;
+ }
+
/**
* @brief Set the GVCP port scheme used for network device discovery and control.
*
diff --git a/orbbec_camera/SDK/x64/include/libobsensor/hpp/Device.hpp b/orbbec_camera/SDK/x64/include/libobsensor/hpp/Device.hpp
index 37a420c2..b483a9b8 100644
--- a/orbbec_camera/SDK/x64/include/libobsensor/hpp/Device.hpp
+++ b/orbbec_camera/SDK/x64/include/libobsensor/hpp/Device.hpp
@@ -880,6 +880,31 @@ public:
Error::handle(&error);
}
+ /**
+ * @brief load the preset by preset name and the target depth work mode version.
+ * The version is forwarded to the depth work mode switch when loading the preset.
+ * @param[in] presetName The preset name to set. The name should be one of the preset names returned by @ref getAvailablePresetList.
+ * @param[in] version The target depth work mode version string, e.g. "1.2.3". Must not be empty.
+ */
+ void loadPreset(const char *presetName, const char *version) const {
+ ob_error *error = nullptr;
+ ob_device_load_preset_by_depth_work_mode_version(impl_, presetName, version, &error);
+ Error::handle(&error);
+ }
+
+ /**
+ * @brief Get the version of the current depth work mode.
+ *
+ * @return const char* The version string, e.g. "1.2.3". Empty string when the device
+ * does not support versioned modes.
+ */
+ const char *getCurrentPresetDepthWorkModeVersion() const {
+ ob_error *error = nullptr;
+ const char *version = ob_device_get_current_preset_depth_work_mode_version(impl_, &error);
+ Error::handle(&error);
+ return version;
+ }
+
/**
* @brief Get available preset list
* @brief The available preset list usually defined by the device manufacturer and restores on the device.
@@ -1836,6 +1861,21 @@ public:
return name;
}
+ /**
+ * @brief Get the depth work mode version of the preset at the specified index
+ *
+ * @param[in] index the index of the device preset
+ *
+ * @return const char* the version string of the depth work mode, e.g. "1.2.3". Empty string
+ * when the device does not support versioned modes.
+ */
+ const char *getDepthWorkModeVersion(uint32_t index) {
+ ob_error *error = nullptr;
+ const char *version = ob_device_preset_list_get_depth_work_mode_version(impl_, index, &error);
+ Error::handle(&error);
+ return version;
+ }
+
/**
* @brief check if the preset list contains the special name preset.
* @param[in] name The name of the preset
diff --git a/orbbec_camera/SDK/x64/include/libobsensor/hpp/Filter.hpp b/orbbec_camera/SDK/x64/include/libobsensor/hpp/Filter.hpp
index 591f2f99..0a2194d9 100644
--- a/orbbec_camera/SDK/x64/include/libobsensor/hpp/Filter.hpp
+++ b/orbbec_camera/SDK/x64/include/libobsensor/hpp/Filter.hpp
@@ -177,8 +177,12 @@ public:
* @return std::shared_ptr< Frame > The processed frame.
*/
virtual std::shared_ptr process(std::shared_ptr frame) const {
+ const ob_frame *frameImpl = nullptr;
+ if(frame) {
+ frameImpl = frame->getImpl();
+ }
ob_error *error = nullptr;
- auto result = ob_filter_process(impl_, frame->getImpl(), &error);
+ auto result = ob_filter_process(impl_, frameImpl, &error);
Error::handle(&error);
if(!result) {
return nullptr;
@@ -192,8 +196,12 @@ public:
* @param[in] frame The pending frame. The processing result is returned by the callback function.
*/
virtual void pushFrame(std::shared_ptr frame) const {
+ const ob_frame *frameImpl = nullptr;
+ if(frame) {
+ frameImpl = frame->getImpl();
+ }
ob_error *error = nullptr;
- ob_filter_push_frame(impl_, frame->getImpl(), &error);
+ ob_filter_push_frame(impl_, frameImpl, &error);
Error::handle(&error);
}
diff --git a/orbbec_camera/SDK/x64/lib/OrbbecSDKConfig-release.cmake b/orbbec_camera/SDK/x64/lib/OrbbecSDKConfig-release.cmake
index 231fafb1..c3bd1654 100644
--- a/orbbec_camera/SDK/x64/lib/OrbbecSDKConfig-release.cmake
+++ b/orbbec_camera/SDK/x64/lib/OrbbecSDKConfig-release.cmake
@@ -8,12 +8,12 @@ set(CMAKE_IMPORT_FILE_VERSION 1)
# Import target "ob::OrbbecSDK" for configuration "Release"
set_property(TARGET ob::OrbbecSDK APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_target_properties(ob::OrbbecSDK PROPERTIES
- IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libOrbbecSDK.so.2.10.1"
+ IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libOrbbecSDK.so.2.10.2"
IMPORTED_SONAME_RELEASE "libOrbbecSDK.so.2"
)
list(APPEND _cmake_import_check_targets ob::OrbbecSDK )
-list(APPEND _cmake_import_check_files_for_ob::OrbbecSDK "${_IMPORT_PREFIX}/lib/libOrbbecSDK.so.2.10.1" )
+list(APPEND _cmake_import_check_files_for_ob::OrbbecSDK "${_IMPORT_PREFIX}/lib/libOrbbecSDK.so.2.10.2" )
# Commands beyond this point should not need to know the version.
set(CMAKE_IMPORT_FILE_VERSION)
diff --git a/orbbec_camera/SDK/x64/lib/OrbbecSDKVersion.cmake b/orbbec_camera/SDK/x64/lib/OrbbecSDKVersion.cmake
index 7851ea03..515957a9 100644
--- a/orbbec_camera/SDK/x64/lib/OrbbecSDKVersion.cmake
+++ b/orbbec_camera/SDK/x64/lib/OrbbecSDKVersion.cmake
@@ -9,19 +9,19 @@
# The variable CVF_VERSION must be set before calling configure_file().
-set(PACKAGE_VERSION "2.10.1")
+set(PACKAGE_VERSION "2.10.2")
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
- if("2.10.1" MATCHES "^([0-9]+)\\.")
+ if("2.10.2" MATCHES "^([0-9]+)\\.")
set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}")
if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0)
string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}")
endif()
else()
- set(CVF_VERSION_MAJOR "2.10.1")
+ set(CVF_VERSION_MAJOR "2.10.2")
endif()
if(PACKAGE_FIND_VERSION_RANGE)
diff --git a/orbbec_camera/SDK/x64/lib/libOrbbecSDK.so.2 b/orbbec_camera/SDK/x64/lib/libOrbbecSDK.so.2
index 027ba8ff..6687316d 120000
--- a/orbbec_camera/SDK/x64/lib/libOrbbecSDK.so.2
+++ b/orbbec_camera/SDK/x64/lib/libOrbbecSDK.so.2
@@ -1 +1 @@
-libOrbbecSDK.so.2.10.1
\ No newline at end of file
+libOrbbecSDK.so.2.10.2
\ No newline at end of file
diff --git a/orbbec_camera/SDK/x64/lib/libOrbbecSDK.so.2.10.1 b/orbbec_camera/SDK/x64/lib/libOrbbecSDK.so.2.10.2
similarity index 68%
rename from orbbec_camera/SDK/x64/lib/libOrbbecSDK.so.2.10.1
rename to orbbec_camera/SDK/x64/lib/libOrbbecSDK.so.2.10.2
index 4b9023d2..ba8936d3 100644
Binary files a/orbbec_camera/SDK/x64/lib/libOrbbecSDK.so.2.10.1 and b/orbbec_camera/SDK/x64/lib/libOrbbecSDK.so.2.10.2 differ
diff --git a/orbbec_camera/config/camera_params.yaml b/orbbec_camera/config/camera_params.yaml
index d2cedfa3..60a68cd2 100644
--- a/orbbec_camera/config/camera_params.yaml
+++ b/orbbec_camera/config/camera_params.yaml
@@ -6,6 +6,7 @@ enable_point_cloud: false
enable_colored_point_cloud: false
enable_ldp: false
device_preset: "Default"
+device_preset_version: ""
enable_laser: true
# enable_laser: true
diff --git a/orbbec_camera/config/camera_secondary_params.yaml b/orbbec_camera/config/camera_secondary_params.yaml
index 2e865f80..44d62d65 100644
--- a/orbbec_camera/config/camera_secondary_params.yaml
+++ b/orbbec_camera/config/camera_secondary_params.yaml
@@ -6,6 +6,7 @@ enable_point_cloud: false
enable_colored_point_cloud: false
enable_ldp: false
device_preset: "Default"
+device_preset_version: ""
enable_laser: true
# enable_laser: true
diff --git a/orbbec_camera/examples/README.MD b/orbbec_camera/examples/README.MD
index f598b6f7..92ea0115 100644
--- a/orbbec_camera/examples/README.MD
+++ b/orbbec_camera/examples/README.MD
@@ -12,6 +12,7 @@ For command-line maintenance and diagnostic utilities, see the official
| Source | Purpose | Experience Level | Official Guide |
| :---: | --- | :---: | --- |
| [Net camera](./net_camera) | Launch supported Orbbec network cameras. | ⭐️ | [Network camera guide](https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/5_advanced_guide/configuration/net_camera.html) |
+| [GigE Action Command](./gige_action_command) | Configure and trigger a Gemini 335Le camera group over GVCP. | ⭐️⭐️ | [README](./gige_action_command/README.md) |
| [GMSL camera](./gmsl_camera) | Launch single, multiple, or synchronized GMSL cameras. | ⭐️ | [GMSL camera guide](https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/5_advanced_guide/multi_camera/gmsl_camera.html) |
| [AE/AWB lock test](./ae_awb_lock) | Verify AE/AWB convergence, frame-metadata capture, and manual lock-in through ROS 2. | ⭐️⭐️ | [README](./ae_awb_lock/README.md) |
| [Benchmark](./benchmark) | Benchmark the performance of different camera configurations. | ⭐️⭐️ | [Performance benchmark tools](https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/6_benchmark/benchmark_tools.html) |
diff --git a/orbbec_camera/examples/benchmark/gemini_330_series_benchmark.launch.py b/orbbec_camera/examples/benchmark/gemini_330_series_benchmark.launch.py
index 906b31cd..863dd123 100644
--- a/orbbec_camera/examples/benchmark/gemini_330_series_benchmark.launch.py
+++ b/orbbec_camera/examples/benchmark/gemini_330_series_benchmark.launch.py
@@ -43,7 +43,7 @@ def load_parameters(context, args):
if config_file_path:
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
- skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
+ skip_convert = {'config_file_path', 'usb_port', 'serial_number', 'device_preset_version'}
return {
key: (value if key in skip_convert else convert_value(value))
for key, value in default_params.items()
@@ -223,6 +223,7 @@ def generate_launch_description():
DeclareLaunchArgument('enable_laser', default_value='true'),
DeclareLaunchArgument('depth_precision', default_value=''),
DeclareLaunchArgument('device_preset', default_value='Default'),
+ DeclareLaunchArgument('device_preset_version', default_value=''),
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
DeclareLaunchArgument('enable_sync_host_time', default_value='false'),
diff --git a/orbbec_camera/examples/gige_action_command/README.md b/orbbec_camera/examples/gige_action_command/README.md
new file mode 100644
index 00000000..a2663fef
--- /dev/null
+++ b/orbbec_camera/examples/gige_action_command/README.md
@@ -0,0 +1,68 @@
+# GigE Action Command
+
+This example starts two Gemini 335Le cameras in Group Actions synchronization mode and one
+host-side Action Command sender. The sender is intentionally created once at the top level because
+a GVCP Action Command can trigger multiple cameras.
+
+## Requirements
+
+- Gemini 335Le firmware 1.8.24 or later
+- Orbbec SDK 2.10.2 or later
+- Both cameras and the host on the same network
+
+Before running the example, change the two `net_device_ip` values in
+`multi_gige_action_command.launch.py` to match the cameras.
+
+## Start the cameras and sender
+
+```bash
+ros2 launch orbbec_camera multi_gige_action_command.launch.py
+```
+
+The launch file creates these device-scoped configuration services and one network-scoped sender:
+
+```text
+/camera_01/get_action_config
+/camera_01/set_action_config
+/camera_02/get_action_config
+/camera_02/set_action_config
+/gige_action_command_node/send_action_command
+```
+
+## Configure the cameras
+
+Configure Action Signal block 0 on both cameras with matching keys and masks:
+
+```bash
+ros2 service call /camera_01/set_action_config \
+ orbbec_camera_msgs/srv/SetActionConfig \
+ "{device_key: 1, selector: 0, group_key: 1, group_mask: 1}"
+
+ros2 service call /camera_02/set_action_config \
+ orbbec_camera_msgs/srv/SetActionConfig \
+ "{device_key: 1, selector: 0, group_key: 1, group_mask: 1}"
+```
+
+Read the configuration back when needed:
+
+```bash
+ros2 service call /camera_01/get_action_config \
+ orbbec_camera_msgs/srv/GetActionConfig \
+ "{selector: 0}"
+```
+
+## Trigger the group
+
+Send an immediate broadcast command. Every camera whose device key, group key, and group mask
+match the request will be triggered:
+
+```bash
+ros2 service call /gige_action_command_node/send_action_command \
+ orbbec_camera_msgs/srv/SendActionCommand \
+ "{device_key: 1, group_key: 1, group_mask: 1, destination_ip: '255.255.255.255', scheduled_time: 0}"
+```
+
+`success: true` means the host dispatched the GVCP command; the protocol does not return a device
+acknowledgment. A nonzero `scheduled_time` uses a GVCP/PTP timestamp, with seconds in the upper
+32 bits and nanoseconds in the lower 32 bits. Scheduled triggering requires the cameras and sender
+host to use synchronized time.
diff --git a/orbbec_camera/examples/gige_action_command/multi_gige_action_command.launch.py b/orbbec_camera/examples/gige_action_command/multi_gige_action_command.launch.py
new file mode 100644
index 00000000..3efeb0bf
--- /dev/null
+++ b/orbbec_camera/examples/gige_action_command/multi_gige_action_command.launch.py
@@ -0,0 +1,53 @@
+import os
+
+from ament_index_python.packages import get_package_share_directory
+from launch import LaunchDescription
+from launch.actions import GroupAction, IncludeLaunchDescription, TimerAction
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch_ros.actions import Node
+
+
+def generate_launch_description():
+ package_dir = get_package_share_directory("orbbec_camera")
+ camera_launch = os.path.join(
+ package_dir, "launch", "gemini_330_series.launch.py"
+ )
+
+ camera_01 = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(camera_launch),
+ launch_arguments={
+ "camera_name": "camera_01",
+ "enumerate_net_device": "false",
+ "net_device_ip": "192.168.1.10",
+ "net_device_port": "8090",
+ "sync_mode": "group_actions",
+ "log_file_name": "camera_01.log",
+ }.items(),
+ )
+
+ camera_02 = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(camera_launch),
+ launch_arguments={
+ "camera_name": "camera_02",
+ "enumerate_net_device": "false",
+ "net_device_ip": "192.168.1.11",
+ "net_device_port": "8090",
+ "sync_mode": "group_actions",
+ "log_file_name": "camera_02.log",
+ }.items(),
+ )
+
+ action_command_node = Node(
+ package="orbbec_camera",
+ executable="gige_action_command_node",
+ name="gige_action_command_node",
+ output="screen",
+ )
+
+ return LaunchDescription(
+ [
+ action_command_node,
+ TimerAction(period=0.0, actions=[GroupAction([camera_01])]),
+ TimerAction(period=2.0, actions=[GroupAction([camera_02])]),
+ ]
+ )
diff --git a/orbbec_camera/examples/gmsl_camera/gemini_330_gmsl.launch.py b/orbbec_camera/examples/gmsl_camera/gemini_330_gmsl.launch.py
index 906b31cd..863dd123 100644
--- a/orbbec_camera/examples/gmsl_camera/gemini_330_gmsl.launch.py
+++ b/orbbec_camera/examples/gmsl_camera/gemini_330_gmsl.launch.py
@@ -43,7 +43,7 @@ def load_parameters(context, args):
if config_file_path:
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
- skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
+ skip_convert = {'config_file_path', 'usb_port', 'serial_number', 'device_preset_version'}
return {
key: (value if key in skip_convert else convert_value(value))
for key, value in default_params.items()
@@ -223,6 +223,7 @@ def generate_launch_description():
DeclareLaunchArgument('enable_laser', default_value='true'),
DeclareLaunchArgument('depth_precision', default_value=''),
DeclareLaunchArgument('device_preset', default_value='Default'),
+ DeclareLaunchArgument('device_preset_version', default_value=''),
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
DeclareLaunchArgument('enable_sync_host_time', default_value='false'),
diff --git a/orbbec_camera/examples/multi_camera_synced_verification_tool/gemini_330_series_synced_verify.launch.py b/orbbec_camera/examples/multi_camera_synced_verification_tool/gemini_330_series_synced_verify.launch.py
index 906b31cd..863dd123 100644
--- a/orbbec_camera/examples/multi_camera_synced_verification_tool/gemini_330_series_synced_verify.launch.py
+++ b/orbbec_camera/examples/multi_camera_synced_verification_tool/gemini_330_series_synced_verify.launch.py
@@ -43,7 +43,7 @@ def load_parameters(context, args):
if config_file_path:
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
- skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
+ skip_convert = {'config_file_path', 'usb_port', 'serial_number', 'device_preset_version'}
return {
key: (value if key in skip_convert else convert_value(value))
for key, value in default_params.items()
@@ -223,6 +223,7 @@ def generate_launch_description():
DeclareLaunchArgument('enable_laser', default_value='true'),
DeclareLaunchArgument('depth_precision', default_value=''),
DeclareLaunchArgument('device_preset', default_value='Default'),
+ DeclareLaunchArgument('device_preset_version', default_value=''),
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
DeclareLaunchArgument('enable_sync_host_time', default_value='false'),
diff --git a/orbbec_camera/include/orbbec_camera/constants.h b/orbbec_camera/include/orbbec_camera/constants.h
index 0872e8ff..02a3e45c 100644
--- a/orbbec_camera/include/orbbec_camera/constants.h
+++ b/orbbec_camera/include/orbbec_camera/constants.h
@@ -24,7 +24,7 @@
#define OB_ROS_MAJOR_VERSION 2
#define OB_ROS_MINOR_VERSION 10
-#define OB_ROS_PATCH_VERSION 1
+#define OB_ROS_PATCH_VERSION 2
#ifndef STRINGIFY
#define STRINGIFY(arg) #arg
diff --git a/orbbec_camera/include/orbbec_camera/gige_action_command_node.h b/orbbec_camera/include/orbbec_camera/gige_action_command_node.h
new file mode 100644
index 00000000..e7768790
--- /dev/null
+++ b/orbbec_camera/include/orbbec_camera/gige_action_command_node.h
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2026 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.
+ *******************************************************************************/
+
+#pragma once
+
+#include
+
+#include
+
+#include "libobsensor/ObSensor.hpp"
+#include "orbbec_camera_msgs/srv/send_action_command.hpp"
+
+namespace orbbec_camera {
+
+class GigEActionCommandNode : public rclcpp::Node {
+ public:
+ explicit GigEActionCommandNode(const rclcpp::NodeOptions& node_options = rclcpp::NodeOptions());
+
+ private:
+ void sendActionCommandCallback(
+ const std::shared_ptr request,
+ std::shared_ptr response);
+
+ std::unique_ptr context_;
+ rclcpp::Service::SharedPtr
+ send_action_command_service_;
+};
+
+} // namespace orbbec_camera
diff --git a/orbbec_camera/include/orbbec_camera/ob_camera_node.h b/orbbec_camera/include/orbbec_camera/ob_camera_node.h
index 30427d15..08ed6ab7 100644
--- a/orbbec_camera/include/orbbec_camera/ob_camera_node.h
+++ b/orbbec_camera/include/orbbec_camera/ob_camera_node.h
@@ -53,6 +53,7 @@
#include "orbbec_camera_msgs/msg/depth_filter_state.hpp"
#include "orbbec_camera_msgs/msg/depth_filters_status.hpp"
#include "orbbec_camera_msgs/srv/get_device_config.hpp"
+#include "orbbec_camera_msgs/srv/get_action_config.hpp"
#include "orbbec_camera_msgs/srv/get_device_info.hpp"
#include "orbbec_camera_msgs/srv/get_awb_gain.hpp"
#include "orbbec_camera_msgs/msg/extrinsics.hpp"
@@ -65,6 +66,7 @@
#include "orbbec_camera_msgs/srv/get_bool.hpp"
#include "orbbec_camera_msgs/srv/set_string.hpp"
#include "orbbec_camera_msgs/srv/set_filter.hpp"
+#include "orbbec_camera_msgs/srv/set_action_config.hpp"
#include "orbbec_camera_msgs/srv/set_arrays.hpp"
#include "orbbec_camera_msgs/srv/set_stream_profile.hpp"
#include "orbbec_camera_msgs/srv/get_user_calib_params.hpp"
@@ -135,6 +137,7 @@
namespace orbbec_camera {
using GetDeviceConfig = orbbec_camera_msgs::srv::GetDeviceConfig;
+using GetActionConfig = orbbec_camera_msgs::srv::GetActionConfig;
using GetDeviceInfo = orbbec_camera_msgs::srv::GetDeviceInfo;
using Extrinsics = orbbec_camera_msgs::msg::Extrinsics;
using SetInt32 = orbbec_camera_msgs::srv::SetInt32;
@@ -146,6 +149,7 @@ using SetString = orbbec_camera_msgs::srv::SetString;
using SetBool = std_srvs::srv::SetBool;
using GetBool = orbbec_camera_msgs::srv::GetBool;
using SetFilter = orbbec_camera_msgs::srv::SetFilter;
+using SetActionConfig = orbbec_camera_msgs::srv::SetActionConfig;
using SetArrays = orbbec_camera_msgs::srv::SetArrays;
using SetStreamProfile = orbbec_camera_msgs::srv::SetStreamProfile;
using SetUserCalibParams = orbbec_camera_msgs::srv::SetUserCalibParams;
@@ -433,6 +437,12 @@ class OBCameraNode {
void setWhiteBalanceCallback(const std::shared_ptr& request,
std::shared_ptr& response);
+ void getColorWbCtrlCallback(const std::shared_ptr& request,
+ std::shared_ptr& response);
+
+ void setColorWbCtrlCallback(const std::shared_ptr& request,
+ std::shared_ptr& response);
+
void getAutoWhiteBalanceCallback(const std::shared_ptr& request,
std::shared_ptr& response);
@@ -481,6 +491,12 @@ class OBCameraNode {
void getDeviceConfigCallback(const std::shared_ptr& request,
std::shared_ptr& response);
+ void getActionConfigCallback(const std::shared_ptr& request,
+ std::shared_ptr& response);
+
+ void setActionConfigCallback(const std::shared_ptr& request,
+ std::shared_ptr& response);
+
void getSDKVersion(const std::shared_ptr& request,
std::shared_ptr& response);
@@ -762,6 +778,8 @@ class OBCameraNode {
std::map::SharedPtr> set_rotation_srv_;
rclcpp::Service::SharedPtr get_white_balance_srv_;
rclcpp::Service::SharedPtr set_white_balance_srv_;
+ rclcpp::Service::SharedPtr get_color_wb_ctrl_srv_;
+ rclcpp::Service::SharedPtr set_color_wb_ctrl_srv_;
rclcpp::Service::SharedPtr get_auto_white_balance_srv_;
rclcpp::Service::SharedPtr set_auto_white_balance_srv_;
rclcpp::Service::SharedPtr get_ae_awb_status_srv_;
@@ -778,6 +796,8 @@ class OBCameraNode {
std::map::SharedPtr> set_ae_roi_srv_;
rclcpp::Service::SharedPtr get_device_srv_;
rclcpp::Service::SharedPtr get_device_config_srv_;
+ rclcpp::Service::SharedPtr get_action_config_srv_;
+ rclcpp::Service::SharedPtr set_action_config_srv_;
rclcpp::Service::SharedPtr set_laser_enable_srv_;
rclcpp::Service::SharedPtr set_ldp_enable_srv_;
rclcpp::Service::SharedPtr get_ldp_status_srv_;
@@ -1002,6 +1022,7 @@ class OBCameraNode {
int left_ir_decimation_factor_ = 1;
int right_ir_decimation_factor_ = 1;
std::string device_preset_;
+ std::string device_preset_version_;
// filter switch
bool enable_decimation_filter_ = false;
bool enable_hdr_merge_ = false;
diff --git a/orbbec_camera/include/orbbec_camera/utils.h b/orbbec_camera/include/orbbec_camera/utils.h
index b74ef056..a65484a9 100644
--- a/orbbec_camera/include/orbbec_camera/utils.h
+++ b/orbbec_camera/include/orbbec_camera/utils.h
@@ -78,18 +78,18 @@ inline std::string formatObErrorWithStatus(const ob::Error& e) {
#define TRY_TO_SET_PROPERTY(func, property, value) \
try { \
- device_->func(property, value); \
+ device_->func((property), (value)); \
} catch (const ob::Error& e) { \
- RCLCPP_ERROR_STREAM(logger_, "Failed to set " << property << " to " << value << " in " \
+ RCLCPP_ERROR_STREAM(logger_, "Failed to set " << (property) << " to " << (value) << " in " \
<< __FUNCTION__ << " at line " << __LINE__ \
<< ": " \
<< orbbec_camera::formatObErrorWithStatus(e)); \
} catch (const std::exception& e) { \
- RCLCPP_ERROR_STREAM(logger_, "Failed to set " << property << " to " << value << " in " \
+ RCLCPP_ERROR_STREAM(logger_, "Failed to set " << (property) << " to " << (value) << " in " \
<< __FUNCTION__ << " at line " << __LINE__ \
<< ": " << e.what()); \
} catch (...) { \
- RCLCPP_ERROR_STREAM(logger_, "Failed to set " << property << " to " << value << " in " \
+ RCLCPP_ERROR_STREAM(logger_, "Failed to set " << (property) << " to " << (value) << " in " \
<< __FUNCTION__ << " at line " << __LINE__); \
}
diff --git a/orbbec_camera/launch/gemini_330_series.launch.py b/orbbec_camera/launch/gemini_330_series.launch.py
index 2bff99ba..5b80cd0e 100644
--- a/orbbec_camera/launch/gemini_330_series.launch.py
+++ b/orbbec_camera/launch/gemini_330_series.launch.py
@@ -49,7 +49,7 @@ def load_parameters(context, args):
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
skip_convert = {'config_file_path', 'usb_port', 'serial_number', 'bag_record_filename', 'bag_filename',
- 'enhanced_depth_model_path', 'depth_colorizer_mode'}
+ 'enhanced_depth_model_path', 'depth_colorizer_mode', 'device_preset_version'}
result = {}
for key, value in default_params.items():
@@ -287,6 +287,7 @@ def generate_launch_description():
DeclareLaunchArgument('enable_laser', default_value='true'),
DeclareLaunchArgument('depth_precision', default_value=''),
DeclareLaunchArgument('device_preset', default_value='Default'),
+ DeclareLaunchArgument('device_preset_version', default_value=''),
DeclareLaunchArgument('color_preset', default_value='Default'),# color preset name reported by the device
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
diff --git a/orbbec_camera/launch/gemini_330_series_low_cpu.launch.py b/orbbec_camera/launch/gemini_330_series_low_cpu.launch.py
index f6480f17..4624d300 100644
--- a/orbbec_camera/launch/gemini_330_series_low_cpu.launch.py
+++ b/orbbec_camera/launch/gemini_330_series_low_cpu.launch.py
@@ -49,7 +49,7 @@ def load_parameters(context, args):
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
skip_convert = {'config_file_path', 'usb_port', 'serial_number', 'bag_record_filename', 'bag_filename',
- 'enhanced_depth_model_path', 'depth_colorizer_mode'}
+ 'enhanced_depth_model_path', 'depth_colorizer_mode', 'device_preset_version'}
result = {}
for key, value in default_params.items():
@@ -282,6 +282,7 @@ def generate_launch_description():
DeclareLaunchArgument('enable_laser', default_value='true'),
DeclareLaunchArgument('depth_precision', default_value=''),
DeclareLaunchArgument('device_preset', default_value='Default'),
+ DeclareLaunchArgument('device_preset_version', default_value=''),
DeclareLaunchArgument('color_preset', default_value='Default'),# color preset name reported by the device
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
diff --git a/orbbec_camera/launch/gemini_intra_process_demo_launch.py b/orbbec_camera/launch/gemini_intra_process_demo_launch.py
index 4f742871..d280e1f5 100644
--- a/orbbec_camera/launch/gemini_intra_process_demo_launch.py
+++ b/orbbec_camera/launch/gemini_intra_process_demo_launch.py
@@ -43,7 +43,7 @@ def load_parameters(context, args):
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
skip_convert = {'config_file_path', 'usb_port', 'serial_number', 'bag_record_filename', 'bag_filename',
- 'depth_colorizer_mode'}
+ 'depth_colorizer_mode', 'device_preset_version'}
return {
key: (value if key in skip_convert else convert_value(value))
for key, value in default_params.items()
@@ -171,6 +171,7 @@ def generate_launch_description():
DeclareLaunchArgument('enable_laser', default_value='true'),
DeclareLaunchArgument('depth_precision', default_value=''),
DeclareLaunchArgument('device_preset', default_value='Default'),
+ DeclareLaunchArgument('device_preset_version', default_value=''),
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
DeclareLaunchArgument('enable_sync_host_time', default_value='true'),
diff --git a/orbbec_camera/package.xml b/orbbec_camera/package.xml
index 90cce657..b784e323 100644
--- a/orbbec_camera/package.xml
+++ b/orbbec_camera/package.xml
@@ -2,7 +2,7 @@
orbbec_camera
- 2.10.1
+ 2.10.2
Orbbec Camera package
yalian
Apache-2.0
@@ -20,7 +20,7 @@
rclcpp_components
cv_bridge
camera_info_manager
- orbbec_camera_msgs
+ orbbec_camera_msgs
builtin_interfaces
rclcpp
rclcpp_action
@@ -55,6 +55,8 @@
python3-yaml
rclpy
+ ament_cmake_gtest
+
ament_cmake
diff --git a/orbbec_camera/scripts/update_bundled_sdk.sh b/orbbec_camera/scripts/update_bundled_sdk.sh
new file mode 100755
index 00000000..67a1586e
--- /dev/null
+++ b/orbbec_camera/scripts/update_bundled_sdk.sh
@@ -0,0 +1,322 @@
+#!/usr/bin/env bash
+
+set -Eeuo pipefail
+
+readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
+readonly PACKAGE_DIR="$(cd -- "${SCRIPT_DIR}/.." && pwd -P)"
+readonly SDK_ROOT="${PACKAGE_DIR}/SDK"
+readonly ROS_CONFIG_FILE="${PACKAGE_DIR}/config/OrbbecSDKConfig_v2.0.xml"
+
+dry_run=false
+work_dir=""
+arm64_backup_dir=""
+x64_backup_dir=""
+config_backup_file=""
+
+usage() {
+ cat <
+
+Replace the bundled Orbbec SDK for both supported ROS package architectures.
+The other SDK directory is inferred by swapping the input path suffix between
+"_arm64" and "_x86_64".
+
+Only the files used or distributed by orbbec_camera are copied:
+ - include/libobsensor/
+ - lib/libOrbbecSDK.so*
+ - lib/OrbbecSDKConfig-release.cmake
+ - lib/OrbbecSDKConfig.cmake
+ - lib/OrbbecSDKVersion.cmake
+ - lib/extensions/
+ - lib/OrbbecSDKConfig.xml -> config/OrbbecSDKConfig_v2.0.xml
+
+SDK/licenses/ is left unchanged.
+
+Example:
+ $(basename "$0") \\
+ /home/user/Downloads/SDK/OrbbecSDK_v2.10.2_linux_arm64
+EOF
+}
+
+die() {
+ echo "Error: $*" >&2
+ exit 1
+}
+
+cleanup_work_dir() {
+ if [[ -n "${work_dir}" && "${work_dir}" == "${PACKAGE_DIR}"/.sdk-update.* ]]; then
+ rm -rf -- "${work_dir}"
+ fi
+}
+
+backup_exists() {
+ [[ -n "${arm64_backup_dir}" && -d "${arm64_backup_dir}" ]] ||
+ [[ -n "${x64_backup_dir}" && -d "${x64_backup_dir}" ]] ||
+ [[ -n "${config_backup_file}" && -e "${config_backup_file}" ]]
+}
+
+restore_backup() {
+ backup_exists || return 0
+
+ echo "Update failed; restoring the original SDK libraries and runtime configuration..." >&2
+ if [[ -n "${config_backup_file}" && -e "${config_backup_file}" ]]; then
+ if [[ -e "${ROS_CONFIG_FILE}" ]]; then
+ mv -- "${ROS_CONFIG_FILE}" "${work_dir}/OrbbecSDKConfig.failed.xml" || return 1
+ fi
+ mv -- "${config_backup_file}" "${ROS_CONFIG_FILE}" || return 1
+ fi
+
+ if [[ -n "${x64_backup_dir}" && -d "${x64_backup_dir}" ]]; then
+ if [[ -e "${SDK_ROOT}/x64" ]]; then
+ mv -- "${SDK_ROOT}/x64" "${work_dir}/x64.failed" || return 1
+ fi
+ mv -- "${x64_backup_dir}" "${SDK_ROOT}/x64" || return 1
+ fi
+
+ if [[ -n "${arm64_backup_dir}" && -d "${arm64_backup_dir}" ]]; then
+ if [[ -e "${SDK_ROOT}/arm64" ]]; then
+ mv -- "${SDK_ROOT}/arm64" "${work_dir}/arm64.failed" || return 1
+ fi
+ mv -- "${arm64_backup_dir}" "${SDK_ROOT}/arm64" || return 1
+ fi
+}
+
+on_exit() {
+ local status=$?
+ trap - EXIT
+
+ if ((status != 0)) && backup_exists; then
+ if ! restore_backup; then
+ echo "Error: automatic rollback failed; recovery files remain in ${work_dir}" >&2
+ exit "${status}"
+ fi
+ fi
+
+ if ! backup_exists; then
+ cleanup_work_dir
+ fi
+ exit "${status}"
+}
+
+trap on_exit EXIT
+trap 'exit 130' INT TERM HUP
+
+if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
+ usage
+ exit 0
+fi
+
+if [[ "${1:-}" == "--dry-run" ]]; then
+ dry_run=true
+ shift
+fi
+
+[[ $# -eq 1 ]] || {
+ usage >&2
+ exit 2
+}
+
+for command_name in basename cat cmp cp dirname file find mkdir mktemp mv readlink rm wc; do
+ command -v "${command_name}" >/dev/null 2>&1 || die "required command not found: ${command_name}"
+done
+
+[[ -d "${SDK_ROOT}" && ! -L "${SDK_ROOT}" ]] ||
+ die "bundled SDK must be a real directory: ${SDK_ROOT}"
+[[ -d "${SDK_ROOT}/arm64" && ! -L "${SDK_ROOT}/arm64" ]] ||
+ die "bundled ARM64 SDK must be a real directory: ${SDK_ROOT}/arm64"
+[[ -d "${SDK_ROOT}/x64" && ! -L "${SDK_ROOT}/x64" ]] ||
+ die "bundled x86-64 SDK must be a real directory: ${SDK_ROOT}/x64"
+[[ -d "${SDK_ROOT}/licenses" && ! -L "${SDK_ROOT}/licenses" ]] ||
+ die "bundled SDK licenses must be a real directory: ${SDK_ROOT}/licenses"
+[[ -f "${ROS_CONFIG_FILE}" && ! -L "${ROS_CONFIG_FILE}" ]] ||
+ die "ROS SDK configuration must be a real file: ${ROS_CONFIG_FILE}"
+
+[[ -d "$1" ]] || die "SDK directory not found: $1"
+input_source="$(readlink -f -- "$1")"
+
+case "${input_source}" in
+ *_arm64)
+ arm64_source="${input_source}"
+ x64_source="${input_source%_arm64}_x86_64"
+ ;;
+ *_x86_64)
+ x64_source="${input_source}"
+ arm64_source="${input_source%_x86_64}_arm64"
+ ;;
+ *)
+ die "SDK directory name must end with _arm64 or _x86_64: ${input_source}"
+ ;;
+esac
+
+[[ -d "${arm64_source}" ]] || die "paired ARM64 SDK directory not found: ${arm64_source}"
+[[ -d "${x64_source}" ]] || die "paired x86-64 SDK directory not found: ${x64_source}"
+case "${arm64_source}" in
+ "${SDK_ROOT}" | "${SDK_ROOT}"/*) die "the ARM64 source cannot be inside ${SDK_ROOT}" ;;
+esac
+case "${x64_source}" in
+ "${SDK_ROOT}" | "${SDK_ROOT}"/*) die "the x86-64 source cannot be inside ${SDK_ROOT}" ;;
+esac
+
+core_library_path() {
+ local source_dir=$1
+ local core_link="${source_dir}/lib/libOrbbecSDK.so"
+
+ [[ -e "${core_link}" ]] || die "missing core SDK library: ${core_link}"
+ readlink -f -- "${core_link}"
+}
+
+sdk_version() {
+ local core_library
+ core_library="$(core_library_path "$1")"
+ local filename=${core_library##*/}
+
+ [[ "${filename}" == libOrbbecSDK.so.* ]] ||
+ die "cannot determine SDK version from core library: ${core_library}"
+ echo "${filename#libOrbbecSDK.so.}"
+}
+
+verify_architecture() {
+ local source_dir=$1
+ local expected_arch=$2
+ local core_library
+ local description
+ core_library="$(core_library_path "${source_dir}")"
+ description="$(file -Lb -- "${core_library}")"
+
+ case "${expected_arch}" in
+ arm64)
+ [[ "${description}" == *"ARM aarch64"* || "${description}" == *"AArch64"* ]] ||
+ die "${source_dir} is not an ARM64 SDK (${description})"
+ ;;
+ x64)
+ [[ "${description}" == *"x86-64"* ]] ||
+ die "${source_dir} is not an x86-64 SDK (${description})"
+ ;;
+ *)
+ die "internal error: unsupported architecture ${expected_arch}"
+ ;;
+ esac
+}
+
+verify_symlinks_within() {
+ local root_dir
+ local link_path
+ local resolved_path
+ root_dir="$(readlink -f -- "$1")"
+
+ while IFS= read -r -d '' link_path; do
+ if ! resolved_path="$(readlink -f -- "${link_path}")"; then
+ die "broken symbolic link: ${link_path}"
+ fi
+ case "${resolved_path}" in
+ "${root_dir}"/*) ;;
+ *) die "symbolic link points outside ${root_dir}: ${link_path} -> ${resolved_path}" ;;
+ esac
+ done < <(find "${root_dir}" -type l -print0)
+}
+
+verify_source_layout() {
+ local source_dir=$1
+ local expected_arch=$2
+ local required_path
+
+ for required_path in \
+ include/libobsensor/ObSensor.h \
+ include/libobsensor/ObSensor.hpp \
+ lib/OrbbecSDKConfig-release.cmake \
+ lib/OrbbecSDKConfig.cmake \
+ lib/OrbbecSDKVersion.cmake \
+ lib/OrbbecSDKConfig.xml; do
+ [[ -f "${source_dir}/${required_path}" ]] ||
+ die "missing required ${expected_arch} SDK file: ${source_dir}/${required_path}"
+ done
+
+ [[ -d "${source_dir}/lib/extensions" ]] ||
+ die "missing ${expected_arch} SDK extensions directory: ${source_dir}/lib/extensions"
+
+ verify_architecture "${source_dir}" "${expected_arch}"
+ verify_symlinks_within "${source_dir}/lib"
+}
+
+copy_architecture() {
+ local source_dir=$1
+ local target_arch=$2
+ local destination_dir=$3
+ local cmake_file
+ local copied_core_library=false
+
+ mkdir -p -- "${destination_dir}/include" "${destination_dir}/lib"
+ cp -a -- "${source_dir}/include/libobsensor" "${destination_dir}/include/"
+
+ while IFS= read -r -d '' library; do
+ cp -a -- "${library}" "${destination_dir}/lib/"
+ copied_core_library=true
+ done < <(find "${source_dir}/lib" -maxdepth 1 \( -type f -o -type l \) \
+ -name 'libOrbbecSDK.so*' -print0)
+ [[ "${copied_core_library}" == true ]] ||
+ die "no core SDK libraries were copied for ${target_arch}"
+
+ for cmake_file in \
+ OrbbecSDKConfig-release.cmake \
+ OrbbecSDKConfig.cmake \
+ OrbbecSDKVersion.cmake; do
+ cp -a -- "${source_dir}/lib/${cmake_file}" "${destination_dir}/lib/"
+ done
+
+ cp -a -- "${source_dir}/lib/extensions" "${destination_dir}/lib/"
+}
+
+verify_source_layout "${arm64_source}" arm64
+verify_source_layout "${x64_source}" x64
+cmp -s -- "${arm64_source}/lib/OrbbecSDKConfig.xml" \
+ "${x64_source}/lib/OrbbecSDKConfig.xml" ||
+ die "ARM64 and x86-64 OrbbecSDKConfig.xml files do not match"
+
+arm64_version="$(sdk_version "${arm64_source}")"
+x64_version="$(sdk_version "${x64_source}")"
+[[ "${arm64_version}" == "${x64_version}" ]] ||
+ die "SDK versions do not match: ARM64=${arm64_version}, x86-64=${x64_version}"
+
+work_dir="$(mktemp -d -- "${PACKAGE_DIR}/.sdk-update.XXXXXX")"
+staged_sdk="${work_dir}/SDK.new"
+staged_config="${work_dir}/OrbbecSDKConfig_v2.0.xml.new"
+mkdir -p -- "${staged_sdk}"
+
+copy_architecture "${arm64_source}" arm64 "${staged_sdk}/arm64"
+copy_architecture "${x64_source}" x64 "${staged_sdk}/x64"
+
+cp -a -- "${x64_source}/lib/OrbbecSDKConfig.xml" "${staged_config}"
+
+verify_architecture "${staged_sdk}/arm64" arm64
+verify_architecture "${staged_sdk}/x64" x64
+verify_symlinks_within "${staged_sdk}"
+
+sdk_file_count="$(find "${staged_sdk}" \( -type f -o -type l \) | wc -l)"
+file_count=$((sdk_file_count + 1))
+echo "Validated Orbbec SDK ${arm64_version} for ARM64 and x86-64."
+echo "Staged ${file_count} ROS package files, including ${ROS_CONFIG_FILE}."
+
+if [[ "${dry_run}" == true ]]; then
+ echo "Dry run complete; SDK libraries, licenses, and runtime configuration were not changed."
+ exit 0
+fi
+
+arm64_backup_dir="${work_dir}/arm64.old"
+x64_backup_dir="${work_dir}/x64.old"
+config_backup_file="${work_dir}/OrbbecSDKConfig_v2.0.xml.old"
+mv -- "${SDK_ROOT}/arm64" "${arm64_backup_dir}"
+mv -- "${SDK_ROOT}/x64" "${x64_backup_dir}"
+mv -- "${ROS_CONFIG_FILE}" "${config_backup_file}"
+mv -- "${staged_sdk}/arm64" "${SDK_ROOT}/arm64"
+mv -- "${staged_sdk}/x64" "${SDK_ROOT}/x64"
+mv -- "${staged_config}" "${ROS_CONFIG_FILE}"
+
+# The verified SDK and runtime configuration are now active. Mark the
+# transaction as committed; the exit handler removes the temporary backups.
+arm64_backup_dir=""
+x64_backup_dir=""
+config_backup_file=""
+
+echo "Updated bundled ARM64, x86-64, and runtime configuration to Orbbec SDK ${arm64_version}."
+echo "Left ${SDK_ROOT}/licenses unchanged."
diff --git a/orbbec_camera/src/gige_action_command_node.cpp b/orbbec_camera/src/gige_action_command_node.cpp
new file mode 100644
index 00000000..f1e201b5
--- /dev/null
+++ b/orbbec_camera/src/gige_action_command_node.cpp
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2026 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/gige_action_command_node.h"
+
+#include
+#include
+#include
+#include
+
+#include "rclcpp_components/register_node_macro.hpp"
+
+namespace orbbec_camera {
+namespace {
+
+std::string formatObError(const ob::Error& error) {
+ std::ostringstream stream;
+ stream << (error.getMessage() ? error.getMessage() : "Unknown OB error")
+ << " status:" << static_cast(error.getStatus());
+ return stream.str();
+}
+
+} // namespace
+
+GigEActionCommandNode::GigEActionCommandNode(const rclcpp::NodeOptions& node_options)
+ : Node("gige_action_command_node", node_options), context_(std::make_unique()) {
+ context_->enableNetDeviceEnumeration(true);
+ send_action_command_service_ = create_service(
+ "~/send_action_command", std::bind(&GigEActionCommandNode::sendActionCommandCallback, this,
+ std::placeholders::_1, std::placeholders::_2));
+ RCLCPP_INFO(get_logger(), "GigE Action Command service is ready");
+}
+
+void GigEActionCommandNode::sendActionCommandCallback(
+ const std::shared_ptr request,
+ std::shared_ptr response) {
+ if (!request) {
+ response->success = false;
+ response->message = "Invalid request";
+ return;
+ }
+
+ const std::string destination_ip =
+ request->destination_ip.empty() ? "255.255.255.255" : request->destination_ip;
+ try {
+ response->success =
+ context_->sendActionCommand(request->device_key, request->group_key, request->group_mask,
+ destination_ip.c_str(), request->scheduled_time);
+ response->message =
+ response->success ? "Action Command dispatched" : "SDK failed to send Action Command";
+ } catch (const ob::Error& error) {
+ response->success = false;
+ response->message = formatObError(error);
+ } catch (const std::exception& error) {
+ response->success = false;
+ response->message = error.what();
+ } catch (...) {
+ response->success = false;
+ response->message = "Unknown error";
+ }
+}
+
+} // namespace orbbec_camera
+
+RCLCPP_COMPONENTS_REGISTER_NODE(orbbec_camera::GigEActionCommandNode)
diff --git a/orbbec_camera/src/ob_camera_node.cpp b/orbbec_camera/src/ob_camera_node.cpp
index 9d26eb53..2b4e9405 100644
--- a/orbbec_camera/src/ob_camera_node.cpp
+++ b/orbbec_camera/src/ob_camera_node.cpp
@@ -922,8 +922,11 @@ void OBCameraNode::clean() noexcept {
}
void OBCameraNode::setupDevices() {
- if (!depth_work_mode_.empty() &&
- device_->isPropertySupported(OB_STRUCT_CURRENT_DEPTH_ALG_MODE, OB_PERMISSION_READ_WRITE)) {
+ if (is_playback_device_ && (!depth_work_mode_.empty() || !device_preset_.empty())) {
+ RCLCPP_INFO_STREAM(logger_, "Skip device preset selection during bag playback");
+ } else 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);
@@ -935,10 +938,48 @@ void OBCameraNode::setupDevices() {
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));
+ std::string version;
+ try {
+ const char *version_value = preset_list->getDepthWorkModeVersion(i);
+ version = version_value == nullptr ? "" : version_value;
+ } catch (...) {
+ // Older firmware can enumerate presets but may not report per-preset versions.
+ }
+ RCLCPP_DEBUG_STREAM(logger_, "Preset " << i << ": " << preset_list->getName(i)
+ << ", depth work mode version: "
+ << (version.empty() ? "not available" : version));
+ }
+
+ if (device_preset_version_.empty()) {
+ device_->loadPreset(device_preset_.c_str());
+ } else {
+ device_->loadPreset(device_preset_.c_str(), device_preset_version_.c_str());
+ }
+
+ std::string current_preset = device_preset_;
+ std::string current_version;
+ try {
+ const char *preset_name = device_->getCurrentPresetName();
+ current_preset = preset_name == nullptr ? device_preset_ : preset_name;
+ } catch (...) {
+ // Loading succeeded; failure to read back the name should not mark the load as failed.
+ }
+ try {
+ const char *version = device_->getCurrentPresetDepthWorkModeVersion();
+ current_version = version == nullptr ? "" : version;
+ } catch (...) {
+ // Older firmware does not report the current preset's depth work mode version.
+ }
+ RCLCPP_INFO_STREAM(logger_,
+ "Loaded device preset: "
+ << current_preset << ", depth work mode version: "
+ << (current_version.empty() ? "not available" : current_version));
+ if (!device_preset_version_.empty() && !current_version.empty() &&
+ current_version != device_preset_version_) {
+ RCLCPP_WARN_STREAM(logger_, "Requested device preset depth work mode version "
+ << device_preset_version_ << ", but device reports "
+ << current_version);
}
- 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));
@@ -947,6 +988,8 @@ void OBCameraNode::setupDevices() {
} catch (...) {
RCLCPP_ERROR_STREAM(logger_, "Failed to load device preset");
}
+ } else if (!device_preset_version_.empty()) {
+ RCLCPP_WARN_STREAM(logger_, "Ignore device_preset_version because device_preset is empty");
}
if (!preset_resolution_config_.empty()) {
@@ -1036,8 +1079,9 @@ void OBCameraNode::setupDevices() {
} 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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_, "Current sync IO voltage level: "
+ << device_->getIntProperty(OB_PROP_USB_SYNC_VOLTAGE_LEVEL_INT)));
}
}
if (monitor_poll_interval_sec_ != -1) {
@@ -1054,16 +1098,16 @@ void OBCameraNode::setupDevices() {
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"));
+ TRY_EXECUTE_BLOCK(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"));
+ TRY_EXECUTE_BLOCK(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_);
@@ -1072,14 +1116,14 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(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));
+ TRY_EXECUTE_BLOCK(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)) {
@@ -1089,8 +1133,10 @@ void OBCameraNode::setupDevices() {
"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);
+ TRY_EXECUTE_BLOCK({
+ 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") {
@@ -1102,22 +1148,42 @@ void OBCameraNode::setupDevices() {
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_EXECUTE_BLOCK({
+ bool expected_hardware_enabled = false;
+ bool expected_software_enabled = false;
+ bool mode_supported = true;
+ if (disparity_to_depth_mode_ == "HW") {
+ expected_hardware_enabled = true;
+ device_->setBoolProperty(OB_PROP_SDK_DISPARITY_TO_DEPTH_BOOL, false);
+ device_->setBoolProperty(OB_PROP_DISPARITY_TO_DEPTH_BOOL, true);
+ } else if (disparity_to_depth_mode_ == "SW") {
+ expected_software_enabled = true;
+ device_->setBoolProperty(OB_PROP_DISPARITY_TO_DEPTH_BOOL, false);
+ device_->setBoolProperty(OB_PROP_SDK_DISPARITY_TO_DEPTH_BOOL, true);
+ } else if (disparity_to_depth_mode_ == "disable") {
+ device_->setBoolProperty(OB_PROP_DISPARITY_TO_DEPTH_BOOL, false);
+ device_->setBoolProperty(OB_PROP_SDK_DISPARITY_TO_DEPTH_BOOL, false);
+ } else {
+ RCLCPP_WARN_STREAM(logger_, "Unknown disparity to depth mode '"
+ << disparity_to_depth_mode_
+ << "', keeping default settings");
+ mode_supported = false;
+ }
+
+ if (mode_supported) {
+ const bool hardware_enabled = device_->getBoolProperty(OB_PROP_DISPARITY_TO_DEPTH_BOOL);
+ const bool software_enabled = device_->getBoolProperty(OB_PROP_SDK_DISPARITY_TO_DEPTH_BOOL);
+ if (hardware_enabled != expected_hardware_enabled ||
+ software_enabled != expected_software_enabled) {
+ RCLCPP_ERROR_STREAM(logger_, "Failed to apply disparity to depth mode "
+ << disparity_to_depth_mode_
+ << ": device reports hardware=" << hardware_enabled
+ << ", software=" << software_enabled);
+ } else {
+ RCLCPP_INFO_STREAM(logger_, "Disparity to depth mode: " << disparity_to_depth_mode_);
+ }
+ }
+ });
}
try {
if (should_apply_launch_config("enable_ldp") &&
@@ -1153,69 +1219,75 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(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"));
+ TRY_EXECUTE_BLOCK(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(
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_,
- "Current laser control: " << (device_->getIntProperty(OB_PROP_LASER_BOOL) ? "ON" : "OFF"));
+ "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);
+ TRY_EXECUTE_BLOCK({
+ 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_;
+ device_->setMultiDeviceSyncConfig(sync_config);
+ sync_config = device_->getMultiDeviceSyncConfig();
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());
- }
- });
- }
+ "Current sync mode: " << magic_enum::enum_name(sync_config.syncMode));
+ if (sync_config.syncMode == 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(
+ TRY_TO_SET_PROPERTY(setBoolProperty, OB_DEVICE_PTP_CLOCK_SYNC_ENABLE_BOOL, enable_ptp_config_);
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_, "Current PTP Config: "
<< (device_->getBoolProperty(OB_DEVICE_PTP_CLOCK_SYNC_ENABLE_BOOL) ? "ON"
- : "OFF"));
+ : "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));
+ TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_DEPTH_PRECISION_LEVEL_INT, depth_precision_);
+ TRY_EXECUTE_BLOCK({
+ 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) &&
@@ -1230,10 +1302,10 @@ void OBCameraNode::setupDevices() {
} else {
TRY_TO_SET_PROPERTY(setFloatProperty, OB_PROP_DEPTH_UNIT_FLEXIBLE_ADJUSTMENT_FLOAT,
depth_unit_flexible_adjustment);
- RCLCPP_INFO_STREAM(
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_, "Current depth unit: "
<< device_->getFloatProperty(OB_PROP_DEPTH_UNIT_FLEXIBLE_ADJUSTMENT_FLOAT)
- << "mm");
+ << "mm"));
}
}
@@ -1258,9 +1330,9 @@ void OBCameraNode::setupDevices() {
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(
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_, "Current " << stream_name_[stream_index] << " mirror: "
- << (device_->getBoolProperty(mirrorPropertyID) ? "ON" : "OFF"));
+ << (device_->getBoolProperty(mirrorPropertyID) ? "ON" : "OFF")));
}
OBPropertyID flipPropertyID = OB_PROP_DEPTH_FLIP_BOOL;
if (stream_index == COLOR) {
@@ -1281,9 +1353,9 @@ void OBCameraNode::setupDevices() {
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"));
+ TRY_EXECUTE_BLOCK(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) {
@@ -1304,8 +1376,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_, "Current " << stream_name_[stream_index]
+ << " rotation: " << device_->getIntProperty(rotationPropertyID)));
}
}
}
@@ -1314,10 +1387,10 @@ void OBCameraNode::setupDevices() {
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(
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_,
"Current color auto white balance: "
- << (device_->getBoolProperty(OB_PROP_COLOR_AUTO_WHITE_BALANCE_BOOL) ? "ON" : "OFF"));
+ << (device_->getBoolProperty(OB_PROP_COLOR_AUTO_WHITE_BALANCE_BOOL) ? "ON" : "OFF")));
}
if (should_apply_launch_config("color_preset") && !color_preset_.empty()) {
try {
@@ -1370,8 +1443,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_,
+ "Current color exposure: " << device_->getIntProperty(OB_PROP_COLOR_EXPOSURE_INT)));
}
}
if (color_gain_ != -1 &&
@@ -1382,8 +1456,8 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_, "Current color gain: " << device_->getIntProperty(OB_PROP_COLOR_GAIN_INT)));
}
}
if (color_mjpeg_quality_ != -1) {
@@ -1397,8 +1471,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_,
+ "Current color MJPEG quality: " << device_->getIntProperty(OB_PROP_MJPEG_QUALITY_INT)));
}
}
}
@@ -1407,19 +1482,19 @@ void OBCameraNode::setupDevices() {
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(
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_,
"Current color auto exposure priority: "
- << (device_->getIntProperty(OB_PROP_COLOR_AUTO_EXPOSURE_PRIORITY_INT) ? "ON" : "OFF"));
+ << (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(
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_,
"Current color auto exposure: "
- << (device_->getBoolProperty(OB_PROP_COLOR_AUTO_EXPOSURE_BOOL) ? "ON" : "OFF"));
+ << (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)) {
@@ -1430,8 +1505,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_, "Current color white balance: "
+ << device_->getIntProperty(OB_PROP_COLOR_WHITE_BALANCE_INT)));
}
}
@@ -1445,8 +1521,9 @@ void OBCameraNode::setupDevices() {
} 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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_, "Current color AE max exposure: "
+ << device_->getIntProperty(OB_PROP_COLOR_AE_MAX_EXPOSURE_INT)));
}
}
if (color_ae_max_gain_ != -1 &&
@@ -1458,8 +1535,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_,
+ "Current color AE max gain: " << device_->getIntProperty(OB_PROP_COLOR_AE_MAX_GAIN_INT)));
}
}
if (color_brightness_ != -1 &&
@@ -1470,8 +1548,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_,
+ "Current color brightness: " << device_->getIntProperty(OB_PROP_COLOR_BRIGHTNESS_INT)));
}
}
if (color_roi_brightness_ != -1 &&
@@ -1483,8 +1562,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_, "Current color roi brightness: "
+ << device_->getIntProperty(OB_PROP_COLOR_ROI_BRIGHTNESS_INT)));
}
}
if (color_sharpness_ != -1 &&
@@ -1495,8 +1575,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_,
+ "Current color sharpness: " << device_->getIntProperty(OB_PROP_COLOR_SHARPNESS_INT)));
}
}
if (color_gamma_ != -1 &&
@@ -1507,8 +1588,8 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_, "Current color gamma: " << device_->getIntProperty(OB_PROP_COLOR_GAMMA_INT)));
}
}
if (color_saturation_ != -1 &&
@@ -1519,8 +1600,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_,
+ "Current color saturation: " << device_->getIntProperty(OB_PROP_COLOR_SATURATION_INT)));
}
}
if (color_contrast_ != -1 &&
@@ -1531,8 +1613,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_,
+ "Current color contrast: " << device_->getIntProperty(OB_PROP_COLOR_CONTRAST_INT)));
}
}
if (color_hue_ != -1 &&
@@ -1543,29 +1626,32 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(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));
+ TRY_EXECUTE_BLOCK(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));
+ TRY_EXECUTE_BLOCK(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"));
+ TRY_EXECUTE_BLOCK(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)) {
@@ -1578,10 +1664,13 @@ void OBCameraNode::setupDevices() {
} 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));
+ TRY_EXECUTE_BLOCK({
+ 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)) {
@@ -1591,8 +1680,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_,
+ "Current depth exposure: " << device_->getIntProperty(OB_PROP_DEPTH_EXPOSURE_INT)));
}
}
if (depth_gain_ != -1 &&
@@ -1603,8 +1693,8 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_, "Current depth gain: " << device_->getIntProperty(OB_PROP_DEPTH_GAIN_INT)));
}
}
if (should_apply_launch_config("enable_depth_auto_exposure_priority") &&
@@ -1613,17 +1703,17 @@ void OBCameraNode::setupDevices() {
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(
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_,
"Current depth auto exposure priority: "
- << (device_->getIntProperty(OB_PROP_DEPTH_AUTO_EXPOSURE_PRIORITY_INT) ? "ON" : "OFF"));
+ << (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(
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_, "Current IR auto exposure: "
- << (device_->getBoolProperty(OB_PROP_IR_AUTO_EXPOSURE_BOOL) ? "ON" : "OFF"));
+ << (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)) {
@@ -1633,8 +1723,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_,
+ "Current depth brightness: " << device_->getIntProperty(OB_PROP_IR_BRIGHTNESS_INT)));
}
}
// ir ae max
@@ -1647,8 +1738,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_, "Current IR AE max exposure: "
+ << device_->getIntProperty(OB_PROP_IR_AE_MAX_EXPOSURE_INT)));
}
}
// ir brightness
@@ -1660,8 +1752,9 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_,
+ "Current IR brightness: " << device_->getIntProperty(OB_PROP_IR_BRIGHTNESS_INT)));
}
}
if (ir_exposure_ != -1 &&
@@ -1672,8 +1765,8 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(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)) {
@@ -1683,16 +1776,16 @@ void OBCameraNode::setupDevices() {
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));
+ TRY_EXECUTE_BLOCK(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(
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_, "Current IR long exposure: "
- << (device_->getBoolProperty(OB_PROP_IR_LONG_EXPOSURE_BOOL) ? "ON" : "OFF"));
+ << (device_->getBoolProperty(OB_PROP_IR_LONG_EXPOSURE_BOOL) ? "ON" : "OFF")));
}
if (should_apply_launch_config("enable_noise_removal_filter") && enable_noise_removal_filter_ &&
@@ -1710,11 +1803,13 @@ void OBCameraNode::setupDevices() {
"the value",
range.min, range.max);
} else {
- device_->setIntProperty(OB_PROP_DEPTH_MAX_DIFF_INT, noise_removal_filter_min_diff_);
+ TRY_TO_SET_PROPERTY(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));
+ TRY_EXECUTE_BLOCK(
+ 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_ &&
@@ -1732,34 +1827,38 @@ void OBCameraNode::setupDevices() {
"the value",
range.min, range.max);
} else {
- device_->setIntProperty(OB_PROP_DEPTH_MAX_SPECKLE_SIZE_INT, noise_removal_filter_max_size_);
+ TRY_TO_SET_PROPERTY(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));
+ TRY_EXECUTE_BLOCK(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_);
+ TRY_TO_SET_PROPERTY(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_);
+ TRY_TO_SET_PROPERTY(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(
+ TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_DEPTH_OUTLIERS_FILTER_SEARCH_MODE_INT,
+ disp_outliers_filter_search_mode_);
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_, "Current DispOutliersFilter search mode: "
- << device_->getIntProperty(OB_PROP_DEPTH_OUTLIERS_FILTER_SEARCH_MODE_INT));
+ << 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)) {
@@ -1772,30 +1871,33 @@ void OBCameraNode::setupDevices() {
} 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));
+ TRY_EXECUTE_BLOCK({
+ 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(
+ TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL,
+ enable_hardware_noise_removal_filter_);
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_,
"Set hardware noise removal filter to "
<< (device_->getBoolProperty(OB_PROP_HW_NOISE_REMOVE_FILTER_ENABLE_BOOL) ? "true"
- : "false"));
+ : "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));
+ TRY_TO_SET_PROPERTY(setFloatProperty, OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT,
+ hardware_noise_removal_filter_threshold_);
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_,
+ "Current hardware noise removal filter threshold: "
+ << device_->getFloatProperty(OB_PROP_HW_NOISE_REMOVE_FILTER_THRESHOLD_FLOAT)));
}
}
}
@@ -1808,30 +1910,33 @@ void OBCameraNode::setupDevices() {
} 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));
+ TRY_EXECUTE_BLOCK({
+ 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(
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_,
"Current accel data correction: "
- << (device_->getBoolProperty(OB_PROP_SDK_ACCEL_FRAME_TRANSFORMED_BOOL) ? "ON" : "OFF"));
+ << (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(
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_,
"Current gyro data correction: "
- << (device_->getBoolProperty(OB_PROP_SDK_GYRO_FRAME_TRANSFORMED_BOOL) ? "ON" : "OFF"));
+ << (device_->getBoolProperty(OB_PROP_SDK_GYRO_FRAME_TRANSFORMED_BOOL) ? "ON" : "OFF")));
}
- if (isGemini335PID(pid_) && !intra_camera_sync_reference_.empty() &&
+ if (!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);
@@ -1842,19 +1947,22 @@ void OBCameraNode::setupDevices() {
} 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));
+ TRY_EXECUTE_BLOCK({
+ 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(
+ TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_DEVICE_AE_STRATEGY_INT,
+ (ae_strategy_ == "motion" ? 1 : 0));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_, "Current Sports Mode: "
- << (device_->getIntProperty(OB_PROP_DEVICE_AE_STRATEGY_INT) == 0 ? "ON"
- : "OFF"));
+ << (device_->getIntProperty(OB_PROP_DEVICE_AE_STRATEGY_INT) == 1 ? "ON"
+ : "OFF")));
}
if (should_apply_launch_config("ae_reference_stream") &&
@@ -1862,10 +1970,13 @@ void OBCameraNode::setupDevices() {
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"));
+ TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_DEVICE_AE_REFERENCE_INT, ae_reference);
+ TRY_EXECUTE_BLOCK({
+ 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"));
+ });
}
}
}
@@ -4154,11 +4265,11 @@ void OBCameraNode::startStreams() {
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(
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_,
"Enable enable_interleave_depth_frame to "
<< (device_->getBoolProperty(OB_PROP_FRAME_INTERLEAVE_ENABLE_BOOL) ? "true"
- : "false"));
+ : "false")));
}
}
// set interleave larse PATTERN_SYNC_DELAY
@@ -4168,9 +4279,9 @@ void OBCameraNode::startStreams() {
(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));
+ TRY_EXECUTE_BLOCK(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);
}
@@ -4408,7 +4519,10 @@ int OBCameraNode::closeSocSyncPwmTrigger() {
}
void OBCameraNode::startGmslTrigger() {
- if (gmsl_trigger_fps_ > 0 && enable_gmsl_trigger_) {
+ if (!enable_gmsl_trigger_) {
+ return;
+ }
+ if (gmsl_trigger_fps_ > 0) {
RCLCPP_WARN_STREAM(logger_, "Start HardwareTrigger by soc-trigger-source. gmsl_trigger_fps_: "
<< gmsl_trigger_fps_);
openSocSyncPwmTrigger(gmsl_trigger_fps_);
@@ -4691,6 +4805,7 @@ void OBCameraNode::getParameters() {
} else {
setAndGetNodeParameter(device_preset_, "device_preset", "");
}
+ setAndGetNodeParameter(device_preset_version_, "device_preset_version", "");
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);
@@ -7371,7 +7486,7 @@ void OBCameraNode::onNewIMUFrameCallback(const std::shared_ptr &frame
setDefaultIMUMessage(imu_msg);
imu_msg.header.frame_id = optical_frame_id_[stream_index];
- auto timestamp = fromUsToROSTime(frame->getTimeStampUs());
+ auto timestamp = fromUsToROSTime(getFrameTimestampUs(frame));
imu_msg.header.stamp = timestamp;
auto imu_info = createIMUInfo(stream_index);
diff --git a/orbbec_camera/src/ob_lidar_node.cpp b/orbbec_camera/src/ob_lidar_node.cpp
index b21bc13e..51e9d03d 100644
--- a/orbbec_camera/src/ob_lidar_node.cpp
+++ b/orbbec_camera/src/ob_lidar_node.cpp
@@ -205,8 +205,10 @@ void OBLidarNode::setupDevices() {
}
}
if (device_->isPropertySupported(OB_PROP_HEARTBEAT_BOOL, OB_PERMISSION_READ_WRITE)) {
- RCLCPP_INFO_STREAM(logger_, "Current heartbeat: " << (enable_heartbeat_ ? "ON" : "OFF"));
TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_HEARTBEAT_BOOL, enable_heartbeat_);
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_, "Current heartbeat: "
+ << (device_->getBoolProperty(OB_PROP_HEARTBEAT_BOOL) ? "ON" : "OFF")));
}
if (!echo_mode_.empty() &&
device_->isPropertySupported(OB_PROP_LIDAR_SPECIFIC_MODE_INT, OB_PERMISSION_READ_WRITE)) {
@@ -215,10 +217,10 @@ void OBLidarNode::setupDevices() {
} else if (echo_mode_ == "First Echo") {
TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_LIDAR_SPECIFIC_MODE_INT, 1);
}
- RCLCPP_INFO_STREAM(
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
logger_, "Current echo mode: " << (device_->getIntProperty(OB_PROP_LIDAR_SPECIFIC_MODE_INT)
? "First Echo"
- : "Last Echo"));
+ : "Last Echo")));
}
if (repetitive_scan_mode_ != -1 &&
device_->isPropertySupported(OB_PROP_LIDAR_REPETITIVE_SCAN_MODE_INT,
@@ -231,8 +233,9 @@ void OBLidarNode::setupDevices() {
} else {
TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_LIDAR_REPETITIVE_SCAN_MODE_INT,
repetitive_scan_mode_);
- RCLCPP_INFO_STREAM(logger_, "Current repetitive scan mode: " << device_->getIntProperty(
- OB_PROP_LIDAR_REPETITIVE_SCAN_MODE_INT));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_, "Current repetitive scan mode: "
+ << device_->getIntProperty(OB_PROP_LIDAR_REPETITIVE_SCAN_MODE_INT)));
}
}
if (filter_level_ != -1 &&
@@ -242,10 +245,12 @@ void OBLidarNode::setupDevices() {
RCLCPP_ERROR(logger_, "filter level value is out of range[%d,%d], please check the value",
range.min, range.max);
} else {
- TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_LIDAR_TAIL_FILTER_LEVEL_INT, filter_level_);
- TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_LIDAR_APPLY_CONFIGS_INT, 1);
- RCLCPP_INFO_STREAM(logger_, "Current filter level: " << device_->getIntProperty(
- OB_PROP_LIDAR_TAIL_FILTER_LEVEL_INT));
+ TRY_EXECUTE_BLOCK({
+ device_->setIntProperty(OB_PROP_LIDAR_TAIL_FILTER_LEVEL_INT, filter_level_);
+ device_->setIntProperty(OB_PROP_LIDAR_APPLY_CONFIGS_INT, 1);
+ RCLCPP_INFO_STREAM(logger_, "Current filter level: " << device_->getIntProperty(
+ OB_PROP_LIDAR_TAIL_FILTER_LEVEL_INT));
+ });
}
}
@@ -257,8 +262,9 @@ void OBLidarNode::setupDevices() {
range.min, range.max);
} else {
TRY_TO_SET_PROPERTY(setFloatProperty, OB_PROP_LIDAR_MEMS_FOV_SIZE_FLOAT, vertical_fov_);
- RCLCPP_INFO_STREAM(logger_, "Current vertical fov: " << device_->getFloatProperty(
- OB_PROP_LIDAR_MEMS_FOV_SIZE_FLOAT));
+ TRY_EXECUTE_BLOCK(RCLCPP_INFO_STREAM(
+ logger_, "Current vertical fov: "
+ << device_->getFloatProperty(OB_PROP_LIDAR_MEMS_FOV_SIZE_FLOAT)));
}
}
}
diff --git a/orbbec_camera/src/ros_service.cpp b/orbbec_camera/src/ros_service.cpp
index 4fd14f55..95ee5d86 100644
--- a/orbbec_camera/src/ros_service.cpp
+++ b/orbbec_camera/src/ros_service.cpp
@@ -112,6 +112,8 @@ std::string OBSyncModeToString(const OBMultiDeviceSyncMode& mode) {
return "SOFTWARE_TRIGGERING";
case OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_HARDWARE_TRIGGERING:
return "HARDWARE_TRIGGERING";
+ case OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_GROUP_ACTIONS:
+ return "GROUP_ACTIONS";
default:
return "FREE_RUN";
}
@@ -293,6 +295,20 @@ void OBCameraNode::setupCameraCtrlServices() {
std::shared_ptr response) {
setWhiteBalanceCallback(request, response);
});
+ if (isPropertyReadable(device_, OB_PROP_COLOR_WB_CTRL_INT)) {
+ get_color_wb_ctrl_srv_ = node_->create_service(
+ "get_color_wb_ctrl", [this](const std::shared_ptr request,
+ std::shared_ptr response) {
+ getColorWbCtrlCallback(request, response);
+ });
+ }
+ if (isPropertyWritable(device_, OB_PROP_COLOR_WB_CTRL_INT)) {
+ set_color_wb_ctrl_srv_ = node_->create_service(
+ "set_color_wb_ctrl", [this](const std::shared_ptr request,
+ std::shared_ptr response) {
+ setColorWbCtrlCallback(request, response);
+ });
+ }
get_auto_white_balance_srv_ = node_->create_service(
"get_auto_white_balance", [this](const std::shared_ptr request,
std::shared_ptr response) {
@@ -334,6 +350,28 @@ void OBCameraNode::setupCameraCtrlServices() {
std::shared_ptr response) {
getDeviceConfigCallback(request, response);
});
+ if (isPropertyReadable(device_, OB_PROP_ACTION_SIGNAL_COUNT_INT) &&
+ isPropertyReadable(device_, OB_PROP_ACTION_DEVICE_KEY_INT) &&
+ isPropertyWritable(device_, OB_PROP_ACTION_SELECTOR_INT) &&
+ isPropertyReadable(device_, OB_PROP_ACTION_GROUP_KEY_INT) &&
+ isPropertyReadable(device_, OB_PROP_ACTION_GROUP_MASK_INT)) {
+ get_action_config_srv_ = node_->create_service(
+ "get_action_config", [this](const std::shared_ptr request,
+ std::shared_ptr response) {
+ getActionConfigCallback(request, response);
+ });
+ }
+ if (isPropertyReadable(device_, OB_PROP_ACTION_SIGNAL_COUNT_INT) &&
+ isPropertyWritable(device_, OB_PROP_ACTION_DEVICE_KEY_INT) &&
+ isPropertyWritable(device_, OB_PROP_ACTION_SELECTOR_INT) &&
+ isPropertyWritable(device_, OB_PROP_ACTION_GROUP_KEY_INT) &&
+ isPropertyWritable(device_, OB_PROP_ACTION_GROUP_MASK_INT)) {
+ set_action_config_srv_ = node_->create_service(
+ "set_action_config", [this](const std::shared_ptr request,
+ std::shared_ptr response) {
+ setActionConfigCallback(request, response);
+ });
+ }
get_sdk_version_srv_ = node_->create_service(
"get_sdk_version",
[this](const std::shared_ptr request,
@@ -1252,6 +1290,67 @@ void OBCameraNode::setWhiteBalanceCallback(const std::shared_ptr& request,
+ std::shared_ptr& response) {
+ (void)request;
+ std::lock_guard lock(device_lock_);
+ try {
+ response->data = device_->getIntProperty(OB_PROP_COLOR_WB_CTRL_INT);
+ response->success = true;
+ response->message = "OK";
+ } catch (const ob::Error& e) {
+ response->success = false;
+ response->message = orbbec_camera::formatObErrorWithStatus(e);
+ } catch (const std::exception& e) {
+ response->success = false;
+ response->message = e.what();
+ } catch (...) {
+ response->success = false;
+ response->message = "unknown error";
+ }
+}
+
+void OBCameraNode::setColorWbCtrlCallback(const std::shared_ptr& request,
+ std::shared_ptr& response) {
+ if (!request) {
+ response->success = false;
+ response->message = "Invalid request";
+ return;
+ }
+
+ std::lock_guard lock(device_lock_);
+ try {
+ const auto range = device_->getIntPropertyRange(OB_PROP_COLOR_WB_CTRL_INT);
+ if (request->data < range.min || request->data > range.max) {
+ response->success = false;
+ response->message = "value out of range [" + std::to_string(range.min) + ", " +
+ std::to_string(range.max) + "]";
+ return;
+ }
+
+ device_->setIntProperty(OB_PROP_COLOR_WB_CTRL_INT, request->data);
+ response->success = true;
+ response->message = "OK";
+ if (isPropertyReadable(device_, OB_PROP_COLOR_WB_CTRL_INT)) {
+ const auto current_value = device_->getIntProperty(OB_PROP_COLOR_WB_CTRL_INT);
+ response->success = current_value == request->data;
+ if (!response->success) {
+ response->message = "device reported " + std::to_string(current_value) + " after setting " +
+ std::to_string(request->data);
+ }
+ }
+ } catch (const ob::Error& e) {
+ response->success = false;
+ response->message = orbbec_camera::formatObErrorWithStatus(e);
+ } catch (const std::exception& e) {
+ response->success = false;
+ response->message = e.what();
+ } catch (...) {
+ response->success = false;
+ response->message = "unknown error";
+ }
+}
+
void OBCameraNode::getAutoWhiteBalanceCallback(const std::shared_ptr& request,
std::shared_ptr& response) {
(void)request;
@@ -1598,6 +1697,86 @@ void OBCameraNode::getDeviceInfoCallback(const std::shared_ptr& request,
+ std::shared_ptr& response) {
+ if (!request) {
+ response->success = false;
+ response->message = "Invalid request";
+ return;
+ }
+
+ std::lock_guard lock(device_lock_);
+ try {
+ const int action_signal_count = device_->getIntProperty(OB_PROP_ACTION_SIGNAL_COUNT_INT);
+ if (action_signal_count <= 0 ||
+ request->selector >= static_cast(action_signal_count)) {
+ response->success = false;
+ response->message = "selector must be less than action signal count " +
+ std::to_string(std::max(action_signal_count, 0));
+ return;
+ }
+
+ device_->setIntProperty(OB_PROP_ACTION_SELECTOR_INT, static_cast(request->selector));
+ response->action_signal_count = static_cast(action_signal_count);
+ response->device_key =
+ static_cast(device_->getIntProperty(OB_PROP_ACTION_DEVICE_KEY_INT));
+ response->group_key =
+ static_cast(device_->getIntProperty(OB_PROP_ACTION_GROUP_KEY_INT));
+ response->group_mask =
+ static_cast(device_->getIntProperty(OB_PROP_ACTION_GROUP_MASK_INT));
+ response->success = true;
+ response->message = "OK";
+ } catch (const ob::Error& e) {
+ response->success = false;
+ response->message = orbbec_camera::formatObErrorWithStatus(e);
+ } catch (const std::exception& e) {
+ response->success = false;
+ response->message = e.what();
+ } catch (...) {
+ response->success = false;
+ response->message = "unknown error";
+ }
+}
+
+void OBCameraNode::setActionConfigCallback(const std::shared_ptr& request,
+ std::shared_ptr& response) {
+ if (!request) {
+ response->success = false;
+ response->message = "Invalid request";
+ return;
+ }
+
+ std::lock_guard lock(device_lock_);
+ try {
+ const int action_signal_count = device_->getIntProperty(OB_PROP_ACTION_SIGNAL_COUNT_INT);
+ if (action_signal_count <= 0 ||
+ request->selector >= static_cast(action_signal_count)) {
+ response->success = false;
+ response->message = "selector must be less than action signal count " +
+ std::to_string(std::max(action_signal_count, 0));
+ return;
+ }
+
+ device_->setIntProperty(OB_PROP_ACTION_DEVICE_KEY_INT,
+ static_cast(request->device_key));
+ device_->setIntProperty(OB_PROP_ACTION_SELECTOR_INT, static_cast(request->selector));
+ device_->setIntProperty(OB_PROP_ACTION_GROUP_KEY_INT, static_cast(request->group_key));
+ device_->setIntProperty(OB_PROP_ACTION_GROUP_MASK_INT,
+ static_cast(request->group_mask));
+ response->success = true;
+ response->message = "OK";
+ } catch (const ob::Error& e) {
+ response->success = false;
+ response->message = orbbec_camera::formatObErrorWithStatus(e);
+ } catch (const std::exception& e) {
+ response->success = false;
+ response->message = e.what();
+ } catch (...) {
+ response->success = false;
+ response->message = "unknown error";
+ }
+}
+
void OBCameraNode::getDeviceConfigCallback(const std::shared_ptr& request,
std::shared_ptr& response) {
(void)request;
@@ -1745,6 +1924,21 @@ void OBCameraNode::getDeviceConfigCallback(const std::shared_ptrgetCurrentPresetDepthWorkModeVersion();
+ if (version != nullptr) {
+ response->preset_depth_work_mode_version = version;
+ }
+ } catch (const ob::Error& e) {
+ RCLCPP_DEBUG_STREAM(logger_, "Failed to get current preset depth work mode version: "
+ << orbbec_camera::formatObErrorWithStatus(e));
+ } catch (const std::exception& e) {
+ RCLCPP_DEBUG_STREAM(logger_,
+ "Failed to get current preset depth work mode version: " << e.what());
+ } catch (...) {
+ RCLCPP_DEBUG_STREAM(logger_, "Failed to get current preset depth work mode version");
+ }
+
try {
if (device_->isColorPresetSupported()) {
const char* color_preset_name = device_->getCurrentColorPresetName();
diff --git a/orbbec_camera/src/utils.cpp b/orbbec_camera/src/utils.cpp
index 3cb41255..c9fe56c4 100644
--- a/orbbec_camera/src/utils.cpp
+++ b/orbbec_camera/src/utils.cpp
@@ -95,15 +95,14 @@ sensor_msgs::msg::CameraInfo convertToCameraInfo(OBCameraIntrinsic intrinsic,
info.distortion_model = getDistortionModels(distortion);
info.width = intrinsic.width;
info.height = intrinsic.height;
- info.d.resize(8, 0.0);
- info.d[0] = distortion.k1;
- info.d[1] = distortion.k2;
- info.d[2] = distortion.p1;
- info.d[3] = distortion.p2;
- info.d[4] = distortion.k3;
- info.d[5] = distortion.k4;
- info.d[6] = distortion.k5;
- info.d[7] = distortion.k6;
+ if (info.distortion_model == sensor_msgs::distortion_models::RATIONAL_POLYNOMIAL) {
+ info.d = {distortion.k1, distortion.k2, distortion.p1, distortion.p2,
+ distortion.k3, distortion.k4, distortion.k5, distortion.k6};
+ } else if (info.distortion_model == sensor_msgs::distortion_models::EQUIDISTANT) {
+ info.d = {distortion.k1, distortion.k2, distortion.k3, distortion.k4};
+ } else {
+ info.d = {distortion.k1, distortion.k2, distortion.p1, distortion.p2, distortion.k3};
+ }
bool all_zero = std::all_of(info.d.begin(), info.d.end(), [](double val) { return val == 0.0; });
info.roi.do_rectify = all_zero;
@@ -679,6 +678,8 @@ OBMultiDeviceSyncMode OBSyncModeFromString(const std::string &mode) {
return OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_SOFTWARE_TRIGGERING;
} else if (mode == "HARDWARE_TRIGGERING") {
return OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_HARDWARE_TRIGGERING;
+ } else if (mode == "GROUP_ACTIONS") {
+ return OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_GROUP_ACTIONS;
} else {
return OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_FREE_RUN;
}
@@ -1174,7 +1175,7 @@ std::string getDistortionModels(OBCameraDistortion distortion) {
case OB_DISTORTION_BROWN_CONRADY:
return sensor_msgs::distortion_models::PLUMB_BOB;
case OB_DISTORTION_BROWN_CONRADY_K6:
- return sensor_msgs::distortion_models::PLUMB_BOB;
+ return sensor_msgs::distortion_models::RATIONAL_POLYNOMIAL;
case OB_DISTORTION_KANNALA_BRANDT4:
return sensor_msgs::distortion_models::EQUIDISTANT;
default:
diff --git a/orbbec_camera/test/camera_info_distortion_test.cpp b/orbbec_camera/test/camera_info_distortion_test.cpp
new file mode 100644
index 00000000..5c303284
--- /dev/null
+++ b/orbbec_camera/test/camera_info_distortion_test.cpp
@@ -0,0 +1,73 @@
+#include
+
+#include
+
+#include "orbbec_camera/utils.h"
+
+namespace orbbec_camera {
+namespace {
+
+OBCameraIntrinsic makeIntrinsic() {
+ OBCameraIntrinsic intrinsic{};
+ intrinsic.width = 1280;
+ intrinsic.height = 800;
+ intrinsic.fx = 600.0F;
+ intrinsic.fy = 601.0F;
+ intrinsic.cx = 640.0F;
+ intrinsic.cy = 400.0F;
+ return intrinsic;
+}
+
+OBCameraDistortion makeDistortion(OBCameraDistortionModel model) {
+ OBCameraDistortion distortion{};
+ distortion.k1 = 0.1F;
+ distortion.k2 = 0.2F;
+ distortion.k3 = 0.3F;
+ distortion.k4 = 0.4F;
+ distortion.k5 = 0.5F;
+ distortion.k6 = 0.6F;
+ distortion.p1 = 0.01F;
+ distortion.p2 = 0.02F;
+ distortion.model = model;
+ return distortion;
+}
+
+TEST(CameraInfoDistortionTest, ConvertsBrownConradyToPlumbBob) {
+ const auto intrinsic = makeIntrinsic();
+ const auto distortion = makeDistortion(OB_DISTORTION_BROWN_CONRADY);
+
+ const auto info = convertToCameraInfo(intrinsic, distortion, intrinsic.width);
+
+ EXPECT_EQ(info.distortion_model, sensor_msgs::distortion_models::PLUMB_BOB);
+ EXPECT_EQ(info.d, std::vector({distortion.k1, distortion.k2, distortion.p1, distortion.p2,
+ distortion.k3}));
+}
+
+TEST(CameraInfoDistortionTest, KeepsK6ModelWhenHigherOrderCoefficientsAreZero) {
+ const auto intrinsic = makeIntrinsic();
+ auto distortion = makeDistortion(OB_DISTORTION_BROWN_CONRADY_K6);
+ distortion.k4 = 0.0F;
+ distortion.k5 = 0.0F;
+ distortion.k6 = 0.0F;
+
+ const auto info = convertToCameraInfo(intrinsic, distortion, intrinsic.width);
+
+ EXPECT_EQ(info.distortion_model, sensor_msgs::distortion_models::RATIONAL_POLYNOMIAL);
+ EXPECT_EQ(info.d,
+ std::vector({distortion.k1, distortion.k2, distortion.p1, distortion.p2,
+ distortion.k3, distortion.k4, distortion.k5, distortion.k6}));
+}
+
+TEST(CameraInfoDistortionTest, ConvertsKannalaBrandtToEquidistant) {
+ const auto intrinsic = makeIntrinsic();
+ const auto distortion = makeDistortion(OB_DISTORTION_KANNALA_BRANDT4);
+
+ const auto info = convertToCameraInfo(intrinsic, distortion, intrinsic.width);
+
+ EXPECT_EQ(info.distortion_model, sensor_msgs::distortion_models::EQUIDISTANT);
+ EXPECT_EQ(info.d,
+ std::vector({distortion.k1, distortion.k2, distortion.k3, distortion.k4}));
+}
+
+} // namespace
+} // namespace orbbec_camera
diff --git a/orbbec_camera/tools/firmware_update_tool.cpp b/orbbec_camera/tools/firmware_update_tool.cpp
index 3fdb97b5..e4cfd1c5 100644
--- a/orbbec_camera/tools/firmware_update_tool.cpp
+++ b/orbbec_camera/tools/firmware_update_tool.cpp
@@ -70,10 +70,19 @@ struct FirmwareUpdateResult {
bool success = false;
bool need_reupdate = false;
bool retryable = true;
+ bool reboot_command_sent = false;
OBFwUpdateState final_state = STAT_START;
+ std::chrono::steady_clock::time_point reboot_started_at;
std::string error_message;
};
+struct DeviceIdentity {
+ std::string serial_number;
+ std::string uid;
+ std::string ip_address;
+ bool is_network = false;
+};
+
std::string trim(std::string value) {
const auto not_space = [](unsigned char ch) { return !std::isspace(ch); };
value.erase(value.begin(), std::find_if(value.begin(), value.end(), not_space));
@@ -405,6 +414,29 @@ bool isBootDeviceName(const std::string &name) {
return lower == "boot" || lower.find("boot") != std::string::npos;
}
+std::string safeString(const char *value) { return value == nullptr ? "" : value; }
+
+DeviceIdentity getDeviceIdentity(const std::shared_ptr &device_info) {
+ DeviceIdentity identity;
+ identity.serial_number = safeString(device_info->getSerialNumber());
+ identity.uid = safeString(device_info->getUid());
+ identity.ip_address = safeString(device_info->getIpAddress());
+ identity.is_network = safeString(device_info->getConnectionType()) == "Ethernet";
+ return identity;
+}
+
+void logRebootReconnectElapsed(const rclcpp::Logger &logger, const FirmwareUpdateResult &result,
+ const char *stage) {
+ if (!result.reboot_command_sent) {
+ return;
+ }
+ const auto elapsed_ms = std::chrono::duration_cast(
+ std::chrono::steady_clock::now() - result.reboot_started_at)
+ .count();
+ RCLCPP_INFO(logger, "[%s] Device detected after reboot in %lld ms", stage,
+ static_cast(elapsed_ms));
+}
+
void printUpdateProgress(const std::string &task, OBFwUpdateState state, const char *message,
uint8_t percent) {
std::cout << "[" << task << "] " << static_cast(percent) << "% | "
@@ -423,7 +455,15 @@ void logCurrentPresetList(const rclcpp::Logger &logger, const std::shared_ptrgetCount();
RCLCPP_INFO(logger, "[%s] Current preset count: %u", stage, count);
for (uint32_t i = 0; i < count; ++i) {
- RCLCPP_INFO(logger, "[%s] Preset[%u]: %s", stage, i, preset_list->getName(i));
+ const char *version = nullptr;
+ try {
+ version = preset_list->getDepthWorkModeVersion(i);
+ } catch (...) {
+ // Older firmware can enumerate presets without exposing version information.
+ }
+ RCLCPP_INFO(logger, "[%s] Preset[%u]: %s, depth work mode version: %s", stage, i,
+ preset_list->getName(i),
+ version == nullptr || version[0] == '\0' ? "not available" : version);
}
} catch (const ob::Error &e) {
RCLCPP_WARN(logger, "[%s] Failed to query preset list: %s", stage,
@@ -499,33 +539,94 @@ std::shared_ptr connectDevice(const rclcpp::Logger &logger,
return device;
}
+bool matchesDeviceIdentity(const std::shared_ptr &list, uint32_t index,
+ const DeviceIdentity &identity) {
+ if (!identity.serial_number.empty()) {
+ try {
+ if (safeString(list->getSerialNumber(index)) == identity.serial_number) {
+ return true;
+ }
+ } catch (const ob::Error &) {
+ }
+ }
+
+ if (!identity.uid.empty()) {
+ try {
+ if (safeString(list->getUid(index)) == identity.uid) {
+ return true;
+ }
+ } catch (const ob::Error &) {
+ }
+ }
+
+ if (identity.is_network && identity.serial_number.empty() && identity.uid.empty() &&
+ !identity.ip_address.empty()) {
+ try {
+ return safeString(list->getIpAddress(index)) == identity.ip_address;
+ } catch (const ob::Error &) {
+ }
+ }
+ return false;
+}
+
+std::shared_ptr findEnumeratedDevice(const std::shared_ptr &list,
+ const DeviceIdentity &identity) {
+ for (uint32_t i = 0; i < list->getCount(); ++i) {
+ try {
+ if (matchesDeviceIdentity(list, i, identity)) {
+ return list->getDevice(i, OB_DEVICE_DEFAULT_ACCESS);
+ }
+ } catch (const ob::Error &) {
+ continue;
+ }
+ }
+ return nullptr;
+}
+
std::shared_ptr waitForReconnect(const rclcpp::Logger &logger,
const std::shared_ptr &ctx,
- const CliArgs &args, bool require_non_boot = false) {
+ const CliArgs &args, const DeviceIdentity &identity,
+ bool require_non_boot = false,
+ bool require_reboot_transition = false) {
const auto deadline =
std::chrono::steady_clock::now() + std::chrono::seconds(args.reconnect_timeout_sec);
+ bool reboot_transition_observed = !require_reboot_transition;
while (std::chrono::steady_clock::now() < deadline) {
try {
- auto device = connectDevice(logger, ctx, args);
+ auto list = ctx->queryDeviceList();
+ auto device = findEnumeratedDevice(list, identity);
if (device) {
auto device_info = device->getDeviceInfo();
const std::string name = device_info->getName();
const std::string serial = device_info->getSerialNumber();
const bool is_boot = isBootDeviceName(name);
- RCLCPP_INFO(logger, "Device reconnected: %s (%s)", name.c_str(), serial.c_str());
+ if (!reboot_transition_observed) {
+ if (is_boot) {
+ reboot_transition_observed = true;
+ RCLCPP_INFO(logger, "Device entered boot stage: %s (%s)", name.c_str(), serial.c_str());
+ } else {
+ std::this_thread::sleep_for(std::chrono::milliseconds(args.reconnect_poll_ms));
+ continue;
+ }
+ }
if (require_non_boot && is_boot) {
RCLCPP_INFO(logger, "Device is still in boot stage, waiting for normal mode...");
std::this_thread::sleep_for(std::chrono::milliseconds(args.reconnect_poll_ms));
continue;
}
+ RCLCPP_INFO(logger, "Device reconnected: %s (%s)", name.c_str(), serial.c_str());
return device;
}
+ reboot_transition_observed = true;
} catch (const ob::Error &e) {
+ reboot_transition_observed = true;
RCLCPP_WARN(logger, "Reconnect attempt failed (SDK): %s",
orbbec_camera::formatObErrorWithStatus(e).c_str());
} catch (const std::exception &e) {
+ reboot_transition_observed = true;
RCLCPP_WARN(logger, "Reconnect attempt failed: %s", e.what());
} catch (...) {
+ reboot_transition_observed = true;
RCLCPP_WARN(logger, "Reconnect attempt failed: unknown error");
}
std::this_thread::sleep_for(std::chrono::milliseconds(args.reconnect_poll_ms));
@@ -536,7 +637,8 @@ std::shared_ptr waitForReconnect(const rclcpp::Logger &logger,
std::shared_ptr waitForReconnectUntil(
const rclcpp::Logger &logger, const std::shared_ptr &ctx, const CliArgs &args,
- bool require_non_boot, const std::chrono::steady_clock::time_point &deadline) {
+ const DeviceIdentity &identity, bool require_non_boot,
+ const std::chrono::steady_clock::time_point &deadline, bool require_reboot_transition = false) {
const auto now = std::chrono::steady_clock::now();
if (now >= deadline) {
throw std::runtime_error("Timeout waiting for device reconnection");
@@ -548,7 +650,8 @@ std::shared_ptr waitForReconnectUntil(
bounded_args.reconnect_timeout_sec = std::max(1, static_cast((remaining_ms + 999) / 1000));
bounded_args.reconnect_poll_ms =
std::max(100, std::min(bounded_args.reconnect_poll_ms, static_cast(remaining_ms)));
- return waitForReconnect(logger, ctx, bounded_args, require_non_boot);
+ return waitForReconnect(logger, ctx, bounded_args, identity, require_non_boot,
+ require_reboot_transition);
}
bool updatePresetFirmware(const rclcpp::Logger &logger, const std::shared_ptr &device,
@@ -684,7 +787,9 @@ FirmwareUpdateResult updateFirmware(const rclcpp::Logger &logger,
waitForFirmwareLogDrain(logger);
}
RCLCPP_INFO(logger, "Rebooting device after firmware update...");
+ result.reboot_started_at = std::chrono::steady_clock::now();
device->reboot();
+ result.reboot_command_sent = true;
RCLCPP_INFO(logger, "Device reboot command sent.");
return result;
}
@@ -746,8 +851,14 @@ int main(int argc, char **argv) {
try {
auto device = connectDevice(logger, ctx, run_args);
auto device_info = device->getDeviceInfo();
+ const auto device_identity = getDeviceIdentity(device_info);
RCLCPP_INFO(logger, "Selected device: %s, SN: %s, UID: %s", device_info->getName(),
device_info->getSerialNumber(), device_info->getUid());
+ if (device_identity.is_network) {
+ ctx->enableNetDeviceEnumeration(true);
+ RCLCPP_INFO(logger,
+ "Network device detected; reconnect will be verified by device enumeration.");
+ }
const bool enable_firmware_log = isSdkLogEnabled(run_args.sdk_log_level);
bool firmware_log_enabled = enable_firmware_log && enableFirmwareLog(logger, device);
@@ -769,6 +880,7 @@ int main(int argc, char **argv) {
? "First firmware update failed"
: first_update.error_message);
}
+ FirmwareUpdateResult final_update = first_update;
if (first_update.need_reupdate) {
RCLCPP_INFO(
@@ -776,7 +888,10 @@ int main(int argc, char **argv) {
"Firmware requires reboot and second update. Waiting for device reconnect...");
const auto second_deadline = std::chrono::steady_clock::now() +
std::chrono::seconds(run_args.reconnect_timeout_sec);
- device = waitForReconnectUntil(logger, ctx, run_args, true, second_deadline);
+ device.reset();
+ device = waitForReconnectUntil(logger, ctx, run_args, device_identity, true,
+ second_deadline, true);
+ logRebootReconnectElapsed(logger, first_update, "first update reconnect");
firmware_log_enabled = enable_firmware_log && enableFirmwareLog(logger, device);
bool second_ok = false;
while (std::chrono::steady_clock::now() < second_deadline) {
@@ -795,14 +910,20 @@ int main(int argc, char **argv) {
RCLCPP_WARN(logger, "Second firmware update attempt failed: %s, retrying...",
second_update.error_message.c_str());
}
- device = waitForReconnectUntil(logger, ctx, run_args, true, second_deadline);
+ device.reset();
+ device = waitForReconnectUntil(logger, ctx, run_args, device_identity, true,
+ second_deadline);
firmware_log_enabled = enable_firmware_log && enableFirmwareLog(logger, device);
continue;
}
+ final_update = second_update;
if (second_update.need_reupdate) {
RCLCPP_WARN(logger,
"Second attempt still requires reupdate, waiting and retrying...");
- device = waitForReconnectUntil(logger, ctx, run_args, true, second_deadline);
+ device.reset();
+ device = waitForReconnectUntil(logger, ctx, run_args, device_identity, true,
+ second_deadline, true);
+ logRebootReconnectElapsed(logger, second_update, "reupdate reconnect");
firmware_log_enabled = enable_firmware_log && enableFirmwareLog(logger, device);
continue;
}
@@ -811,7 +932,9 @@ int main(int argc, char **argv) {
} catch (const ob::Error &e) {
RCLCPP_WARN(logger, "Second update transient error: %s, retrying...",
orbbec_camera::formatObErrorWithStatus(e).c_str());
- device = waitForReconnectUntil(logger, ctx, run_args, true, second_deadline);
+ device.reset();
+ device = waitForReconnectUntil(logger, ctx, run_args, device_identity, true,
+ second_deadline);
firmware_log_enabled = enable_firmware_log && enableFirmwareLog(logger, device);
}
}
@@ -819,6 +942,15 @@ int main(int argc, char **argv) {
throw std::runtime_error("Second firmware update failed after retries");
}
}
+
+ RCLCPP_INFO(logger, "Waiting for device to reconnect after final reboot...");
+ device.reset();
+ device = waitForReconnect(logger, ctx, run_args, device_identity, true, true);
+ logRebootReconnectElapsed(logger, final_update, "final reconnect");
+ device_info = device->getDeviceInfo();
+ RCLCPP_INFO(logger, "Device is online after firmware update: %s, SN: %s, firmware: %s",
+ device_info->getName(), device_info->getSerialNumber(),
+ device_info->getFirmwareVersion());
}
success_count++;
diff --git a/orbbec_camera/tools/list_camera_profile.cpp b/orbbec_camera/tools/list_camera_profile.cpp
index 78c03a6f..3e31a706 100644
--- a/orbbec_camera/tools/list_camera_profile.cpp
+++ b/orbbec_camera/tools/list_camera_profile.cpp
@@ -205,7 +205,16 @@ void printPreset(const std::shared_ptr& device) {
std::cout << "Preset list:" << std::endl;
for (uint32_t i = 0; i < preset_list->getCount(); i++) {
auto name = preset_list->getName(i);
- std::cout << "Preset list[" << i << "]: " << name << std::endl;
+ std::string version;
+ try {
+ const char* version_value = preset_list->getDepthWorkModeVersion(i);
+ version = version_value == nullptr ? "" : version_value;
+ } catch (...) {
+ // Older firmware can enumerate presets without exposing version information.
+ }
+ std::cout << "Preset list[" << i << "]: " << name
+ << ", depth work mode version: " << (version.empty() ? "not available" : version)
+ << std::endl;
}
}
diff --git a/orbbec_camera/tools/list_devices_node.cpp b/orbbec_camera/tools/list_devices_node.cpp
index 6cdfcb36..274a25a7 100644
--- a/orbbec_camera/tools/list_devices_node.cpp
+++ b/orbbec_camera/tools/list_devices_node.cpp
@@ -181,7 +181,15 @@ void printPresetInfo(const std::shared_ptr &device) {
for (uint32_t i = 0; i < preset_count; ++i) {
const char *preset_name = preset_list->getName(i);
if (preset_name != nullptr && preset_name[0] != '\0') {
- RCLCPP_INFO_STREAM(logger, " - " << preset_name);
+ std::string version;
+ try {
+ const char *version_value = preset_list->getDepthWorkModeVersion(i);
+ version = version_value == nullptr ? "" : version_value;
+ } catch (...) {
+ // Older firmware can enumerate presets without exposing version information.
+ }
+ RCLCPP_INFO_STREAM(logger, " - " << preset_name << " (depth work mode version: "
+ << (version.empty() ? "not available" : version) << ")");
}
}
diff --git a/orbbec_camera_msgs/CMakeLists.txt b/orbbec_camera_msgs/CMakeLists.txt
index 5212ea6d..c004d1d6 100644
--- a/orbbec_camera_msgs/CMakeLists.txt
+++ b/orbbec_camera_msgs/CMakeLists.txt
@@ -28,6 +28,7 @@ rosidl_generate_interfaces(
"msg/RGBD.msg"
"msg/StreamProfile.msg"
"srv/GetBool.srv"
+ "srv/GetActionConfig.srv"
"srv/GetDeviceConfig.srv"
"srv/GetDeviceInfo.srv"
"srv/GetCameraInfo.srv"
@@ -35,6 +36,7 @@ rosidl_generate_interfaces(
"srv/GetAwbGain.srv"
"srv/GetString.srv"
"srv/SetFilter.srv"
+ "srv/SetActionConfig.srv"
"srv/SetInt32.srv"
"srv/SetAwbGain.srv"
"srv/SetString.srv"
@@ -43,6 +45,7 @@ rosidl_generate_interfaces(
"srv/SetUserCalibParams.srv"
"srv/SetBagRecording.srv"
"srv/SetStreamProfile.srv"
+ "srv/SendActionCommand.srv"
"action/RunAeAwbLockTest.action"
DEPENDENCIES
action_msgs
diff --git a/orbbec_camera_msgs/package.xml b/orbbec_camera_msgs/package.xml
index dfcac903..9d462713 100644
--- a/orbbec_camera_msgs/package.xml
+++ b/orbbec_camera_msgs/package.xml
@@ -2,7 +2,7 @@
orbbec_camera_msgs
- 2.10.1
+ 2.10.2
A package containing orbbec camera messages definitions.
yalian
Apache-2.0
diff --git a/orbbec_camera_msgs/srv/GetActionConfig.srv b/orbbec_camera_msgs/srv/GetActionConfig.srv
new file mode 100644
index 00000000..d45d0116
--- /dev/null
+++ b/orbbec_camera_msgs/srv/GetActionConfig.srv
@@ -0,0 +1,8 @@
+uint32 selector
+---
+uint32 action_signal_count
+uint32 device_key
+uint32 group_key
+uint32 group_mask
+bool success
+string message
diff --git a/orbbec_camera_msgs/srv/GetDeviceConfig.srv b/orbbec_camera_msgs/srv/GetDeviceConfig.srv
index e7cdb8ab..42a8ba09 100644
--- a/orbbec_camera_msgs/srv/GetDeviceConfig.srv
+++ b/orbbec_camera_msgs/srv/GetDeviceConfig.srv
@@ -4,7 +4,10 @@ string schema_version
# Effective device configuration state that is not provided by existing device/version services.
string device_preset
+# Legacy preset package version reported by the device extension information.
string preset_version
+# Current preset's target depth work mode version. Empty when unsupported or unavailable.
+string preset_depth_work_mode_version
string color_preset
string depth_precision
string disparity_to_depth_mode
diff --git a/orbbec_camera_msgs/srv/SendActionCommand.srv b/orbbec_camera_msgs/srv/SendActionCommand.srv
new file mode 100644
index 00000000..c8a9628a
--- /dev/null
+++ b/orbbec_camera_msgs/srv/SendActionCommand.srv
@@ -0,0 +1,12 @@
+uint32 device_key
+uint32 group_key
+uint32 group_mask
+# IPv4 destination. Leave empty to use the SDK broadcast address (255.255.255.255).
+string destination_ip
+# GVCP/PTP timestamp: upper 32 bits are seconds and lower 32 bits are nanoseconds.
+# Zero sends the command immediately.
+uint64 scheduled_time
+---
+# Success means that the SDK dispatched the command; Action Command has no device acknowledgment.
+bool success
+string message
diff --git a/orbbec_camera_msgs/srv/SetActionConfig.srv b/orbbec_camera_msgs/srv/SetActionConfig.srv
new file mode 100644
index 00000000..b8af7a47
--- /dev/null
+++ b/orbbec_camera_msgs/srv/SetActionConfig.srv
@@ -0,0 +1,7 @@
+uint32 device_key
+uint32 selector
+uint32 group_key
+uint32 group_mask
+---
+bool success
+string message
diff --git a/orbbec_description/package.xml b/orbbec_description/package.xml
index b7e89822..79eb77f1 100644
--- a/orbbec_description/package.xml
+++ b/orbbec_description/package.xml
@@ -2,7 +2,7 @@
orbbec_description
- 2.10.1
+ 2.10.2
TODO: Package description
yalian
Apache-2.0