From 8d75f34eee039481aeab3966f2e15bae847dbbdd Mon Sep 17 00:00:00 2001 From: Walter Lucetti Date: Wed, 1 Dec 2021 17:54:08 +0100 Subject: [PATCH 1/9] Remove zed_interfaces files and add submodule --- .gitmodules | 3 +++ zed-ros2-interfaces | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 zed-ros2-interfaces diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..79208b15 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "zed-ros2-interfaces"] + path = zed-ros2-interfaces + url = git@github.com:stereolabs/zed-ros2-interfaces.git diff --git a/zed-ros2-interfaces b/zed-ros2-interfaces new file mode 160000 index 00000000..a8ba9907 --- /dev/null +++ b/zed-ros2-interfaces @@ -0,0 +1 @@ +Subproject commit a8ba99072f3e128173c10497f6db674e1b7959e1 From 3aa4ca36937625278d170d6ad7889cbc8e6eec38 Mon Sep 17 00:00:00 2001 From: Walter Lucetti Date: Wed, 1 Dec 2021 17:55:13 +0100 Subject: [PATCH 2/9] Remove zed_interfaces files and add the submodule --- zed-ros2-interfaces | 2 +- zed_interfaces/CMakeLists.txt | 50 ---------------------- zed_interfaces/msg/BoundingBox2Df.msg | 6 --- zed_interfaces/msg/BoundingBox2Di.msg | 6 --- zed_interfaces/msg/BoundingBox3D.msg | 8 ---- zed_interfaces/msg/Keypoint2Df.msg | 1 - zed_interfaces/msg/Keypoint2Di.msg | 1 - zed_interfaces/msg/Keypoint3D.msg | 1 - zed_interfaces/msg/Object.msg | 58 -------------------------- zed_interfaces/msg/ObjectsStamped.msg | 5 --- zed_interfaces/msg/Skeleton2D.msg | 21 ---------- zed_interfaces/msg/Skeleton3D.msg | 21 ---------- zed_interfaces/package.xml | 31 -------------- zed_interfaces/srv/SetPose.srv | 9 ---- zed_interfaces/srv/StartSvoRec.srv | 21 ---------- zed_wrapper/urdf/models/zed.stl | Bin 48684 -> 0 bytes zed_wrapper/urdf/models/zed2.stl | Bin 48684 -> 0 bytes zed_wrapper/urdf/models/zed2i.stl | Bin 718084 -> 0 bytes zed_wrapper/urdf/models/zedm.stl | Bin 75584 -> 0 bytes zed_wrapper/urdf/zed_macro.urdf.xacro | 4 +- 20 files changed, 3 insertions(+), 242 deletions(-) delete mode 100644 zed_interfaces/CMakeLists.txt delete mode 100644 zed_interfaces/msg/BoundingBox2Df.msg delete mode 100644 zed_interfaces/msg/BoundingBox2Di.msg delete mode 100644 zed_interfaces/msg/BoundingBox3D.msg delete mode 100644 zed_interfaces/msg/Keypoint2Df.msg delete mode 100644 zed_interfaces/msg/Keypoint2Di.msg delete mode 100644 zed_interfaces/msg/Keypoint3D.msg delete mode 100644 zed_interfaces/msg/Object.msg delete mode 100644 zed_interfaces/msg/ObjectsStamped.msg delete mode 100644 zed_interfaces/msg/Skeleton2D.msg delete mode 100644 zed_interfaces/msg/Skeleton3D.msg delete mode 100644 zed_interfaces/package.xml delete mode 100644 zed_interfaces/srv/SetPose.srv delete mode 100644 zed_interfaces/srv/StartSvoRec.srv delete mode 100644 zed_wrapper/urdf/models/zed.stl delete mode 100644 zed_wrapper/urdf/models/zed2.stl delete mode 100644 zed_wrapper/urdf/models/zed2i.stl delete mode 100644 zed_wrapper/urdf/models/zedm.stl diff --git a/zed-ros2-interfaces b/zed-ros2-interfaces index a8ba9907..f766201a 160000 --- a/zed-ros2-interfaces +++ b/zed-ros2-interfaces @@ -1 +1 @@ -Subproject commit a8ba99072f3e128173c10497f6db674e1b7959e1 +Subproject commit f766201a15968c8efa234ddad4ddd84f4273e5c3 diff --git a/zed_interfaces/CMakeLists.txt b/zed_interfaces/CMakeLists.txt deleted file mode 100644 index 4d2a15c5..00000000 --- a/zed_interfaces/CMakeLists.txt +++ /dev/null @@ -1,50 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(zed_interfaces) - -# Default to C++14 -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 14) -endif() -if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - add_compile_options(-Wall -Wextra -Wpedantic) -endif() - -find_package(ament_cmake REQUIRED) -find_package(builtin_interfaces REQUIRED) -find_package(rosidl_default_generators REQUIRED) -find_package(std_msgs REQUIRED) -find_package(geometry_msgs REQUIRED) - -############################################################################### -# Add all files in subdirectories of the project in -# a dummy_target so qtcreator have access to all files -FILE(GLOB_RECURSE extra_files ${CMAKE_SOURCE_DIR}/*) -add_custom_target(all_${PROJECT_NAME}_files SOURCES ${extra_files}) -############################################################################### - -set(MSG_FILES - "msg/Object.msg" - "msg/ObjectsStamped.msg" - "msg/Keypoint2Di.msg" - "msg/Keypoint2Df.msg" - "msg/Keypoint3D.msg" - "msg/BoundingBox2Di.msg" - "msg/BoundingBox2Df.msg" - "msg/BoundingBox3D.msg" - "msg/Skeleton2D.msg" - "msg/Skeleton3D.msg" -) - -set(SRV_FILES - "srv/SetPose.srv" - "srv/StartSvoRec.srv" -) - -rosidl_generate_interfaces(${PROJECT_NAME} - ${MSG_FILES} - ${SRV_FILES} - DEPENDENCIES builtin_interfaces std_msgs geometry_msgs -) - -ament_export_dependencies(rosidl_default_runtime) -ament_package() diff --git a/zed_interfaces/msg/BoundingBox2Df.msg b/zed_interfaces/msg/BoundingBox2Df.msg deleted file mode 100644 index cd11758e..00000000 --- a/zed_interfaces/msg/BoundingBox2Df.msg +++ /dev/null @@ -1,6 +0,0 @@ -# 0 ------- 1 -# | | -# | | -# | | -# 3 ------- 2 -zed_interfaces/Keypoint2Df[4] corners diff --git a/zed_interfaces/msg/BoundingBox2Di.msg b/zed_interfaces/msg/BoundingBox2Di.msg deleted file mode 100644 index bc5fd790..00000000 --- a/zed_interfaces/msg/BoundingBox2Di.msg +++ /dev/null @@ -1,6 +0,0 @@ -# 0 ------- 1 -# | | -# | | -# | | -# 3 ------- 2 -zed_interfaces/Keypoint2Di[4] corners diff --git a/zed_interfaces/msg/BoundingBox3D.msg b/zed_interfaces/msg/BoundingBox3D.msg deleted file mode 100644 index 89b3d647..00000000 --- a/zed_interfaces/msg/BoundingBox3D.msg +++ /dev/null @@ -1,8 +0,0 @@ -# 1 ------- 2 -# /. /| -# 0 ------- 3 | -# | . | | -# | 5.......| 6 -# |. |/ -# 4 ------- 7 -zed_interfaces/Keypoint3D[8] corners diff --git a/zed_interfaces/msg/Keypoint2Df.msg b/zed_interfaces/msg/Keypoint2Df.msg deleted file mode 100644 index dbadcaf2..00000000 --- a/zed_interfaces/msg/Keypoint2Df.msg +++ /dev/null @@ -1 +0,0 @@ -float32[2] kp diff --git a/zed_interfaces/msg/Keypoint2Di.msg b/zed_interfaces/msg/Keypoint2Di.msg deleted file mode 100644 index 16a8348d..00000000 --- a/zed_interfaces/msg/Keypoint2Di.msg +++ /dev/null @@ -1 +0,0 @@ -uint32[2] kp diff --git a/zed_interfaces/msg/Keypoint3D.msg b/zed_interfaces/msg/Keypoint3D.msg deleted file mode 100644 index aa21f09f..00000000 --- a/zed_interfaces/msg/Keypoint3D.msg +++ /dev/null @@ -1 +0,0 @@ -float32[3] kp diff --git a/zed_interfaces/msg/Object.msg b/zed_interfaces/msg/Object.msg deleted file mode 100644 index 75e50261..00000000 --- a/zed_interfaces/msg/Object.msg +++ /dev/null @@ -1,58 +0,0 @@ -# Object label -string label - -# Object label ID -int16 label_id - -# Object sub -string sublabel - -# Object confidence level (1-99) -float32 confidence - -# Object centroid position -float32[3] position - -# Position covariance -float32[6] position_covariance - -# Object velocity -float32[3] velocity - -# Tracking state -# 0 -> OFF (object not valid) -# 1 -> OK -# 2 -> SEARCHING (occlusion occurred, trajectory is estimated) -int8 tracking_state - -# Action state -# 0 -> IDLE -# 2 -> MOVING -int8 action_state - -# 2D Bounding box projected to Camera image -zed_interfaces/BoundingBox2Di bounding_box_2d - -# 3D Bounding box in world frame -zed_interfaces/BoundingBox3D bounding_box_3d - -# 3D dimensions (width, height, lenght) -float32[3] dimensions_3d - -# Is skeleton available? -bool skeleton_available - -# 2D Bounding box projected to Camera image of the person head -zed_interfaces/BoundingBox2Df head_bounding_box_2d - -# 3D Bounding box in world frame of the person head -zed_interfaces/BoundingBox3D head_bounding_box_3d - -# 3D position of the centroid of the person head -float32[3] head_position - -# 2D Person skeleton projected to Camera image -zed_interfaces/Skeleton2D skeleton_2d - -# 3D Person skeleton in world frame -zed_interfaces/Skeleton3D skeleton_3d diff --git a/zed_interfaces/msg/ObjectsStamped.msg b/zed_interfaces/msg/ObjectsStamped.msg deleted file mode 100644 index 6a74cf7b..00000000 --- a/zed_interfaces/msg/ObjectsStamped.msg +++ /dev/null @@ -1,5 +0,0 @@ -# Standard Header -std_msgs/Header header - -# Array of `object_stamped` topics -zed_interfaces/Object[] objects diff --git a/zed_interfaces/msg/Skeleton2D.msg b/zed_interfaces/msg/Skeleton2D.msg deleted file mode 100644 index 2e64d28e..00000000 --- a/zed_interfaces/msg/Skeleton2D.msg +++ /dev/null @@ -1,21 +0,0 @@ -# Skeleton joints indices -# 16-14 15-17 -# \ / -# 0 -# | -# 2------1------5 -# | | | | -# | | | | -# 3 | | 6 -# | | | | -# | | | | -# 4 8 11 7 -# | | -# | | -# | | -# 9 12 -# | | -# | | -# | | -# 10 13 -zed_interfaces/Keypoint2Df[18] keypoints diff --git a/zed_interfaces/msg/Skeleton3D.msg b/zed_interfaces/msg/Skeleton3D.msg deleted file mode 100644 index 136859f0..00000000 --- a/zed_interfaces/msg/Skeleton3D.msg +++ /dev/null @@ -1,21 +0,0 @@ -# Skeleton joints indices -# 16-14 15-17 -# \ / -# 0 -# | -# 2------1------5 -# | | | | -# | | | | -# 3 | | 6 -# | | | | -# | | | | -# 4 8 11 7 -# | | -# | | -# | | -# 9 12 -# | | -# | | -# | | -# 10 13 -zed_interfaces/Keypoint3D[18] keypoints diff --git a/zed_interfaces/package.xml b/zed_interfaces/package.xml deleted file mode 100644 index 45a1b92b..00000000 --- a/zed_interfaces/package.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - zed_interfaces - 0.2.0 - Contains message and service definitions used by the ZED ROS2 nodes. - STEREOLABS - - MIT - - https://www.stereolabs.com/ - https://github.com/stereolabs/zed-ros2-wrapper - https://github.com/stereolabs/zed-ros2-wrapper/issues - - ament_cmake - - builtin_interfaces - std_msgs - geometry_msgs - rosidl_default_generators - - std_msgs - geometry_msgs - rosidl_default_runtime - - rosidl_interface_packages - - - ament_cmake - - diff --git a/zed_interfaces/srv/SetPose.srv b/zed_interfaces/srv/SetPose.srv deleted file mode 100644 index e273cbfb..00000000 --- a/zed_interfaces/srv/SetPose.srv +++ /dev/null @@ -1,9 +0,0 @@ -# Restart positional tracking to a new pose in base_link frame - -# Translation XYZ [meters] -float32[3] pos -# Orientation RPY [radians] -float32[3] orient ---- -bool success # indicate successful run of service -string message # informational, e.g. for error messages diff --git a/zed_interfaces/srv/StartSvoRec.srv b/zed_interfaces/srv/StartSvoRec.srv deleted file mode 100644 index c20f0c8b..00000000 --- a/zed_interfaces/srv/StartSvoRec.srv +++ /dev/null @@ -1,21 +0,0 @@ -# Start SVO Recording - -# Bitrate - 0 or [1000,60000] -uint32 bitrate - -# Compression Mode - [0,2] -uint8 compression_mode - -# Target Framerate -# Allowed framerates are 0,15,30, 60 or 100. -# Any other values will be discarded and camera FPS will be taken. -uint32 target_framerate - -# Input Transcode -bool input_transcode - -# Filename -string svo_filename ---- -bool success # indicate successful run of service -string message # informational, e.g. for error messages diff --git a/zed_wrapper/urdf/models/zed.stl b/zed_wrapper/urdf/models/zed.stl deleted file mode 100644 index 913d2cd9657a77cda8be4f1be69c8728a6cce9a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48684 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 diff --git a/zed_wrapper/urdf/models/zed2.stl b/zed_wrapper/urdf/models/zed2.stl deleted file mode 100644 index 913d2cd9657a77cda8be4f1be69c8728a6cce9a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48684 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 diff --git a/zed_wrapper/urdf/models/zed2i.stl b/zed_wrapper/urdf/models/zed2i.stl deleted file mode 100644 index 76a08d0cbb7d54f694d6c40420a3c25eb15cf28b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 718084 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

}RDX)C1fxT->iTVMGEHJX6D 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 diff --git a/zed_wrapper/urdf/models/zedm.stl b/zed_wrapper/urdf/models/zedm.stl deleted file mode 100644 index 7048bc7798f754239dcaced2a75e06f9871d6598..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75584 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 diff --git a/zed_wrapper/urdf/zed_macro.urdf.xacro b/zed_wrapper/urdf/zed_macro.urdf.xacro index 6c55a6e8..81e4d188 100644 --- a/zed_wrapper/urdf/zed_macro.urdf.xacro +++ b/zed_wrapper/urdf/zed_macro.urdf.xacro @@ -75,13 +75,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + - + From 3cdde68d7755f2fa81c9b355b6b621b69f6d976d Mon Sep 17 00:00:00 2001 From: Walter Lucetti Date: Thu, 2 Dec 2021 12:34:10 +0100 Subject: [PATCH 3/9] Add new Object topic field --- last_changes.md | 5 + zed-ros2-interfaces | 2 +- .../zed_camera/src/zed_camera_component.cpp | 9804 +++++++++-------- zed_wrapper/config/zed2.yaml | 2 +- 4 files changed, 4962 insertions(+), 4851 deletions(-) diff --git a/last_changes.md b/last_changes.md index ede9d437..1143d92b 100644 --- a/last_changes.md +++ b/last_changes.md @@ -1,6 +1,11 @@ LATEST CHANGES ============== +2021-12-02 +---------- +- Moved the `zed_interfaces` package to the `zed-ros2-interfaces` repository to match the same configuration of the ROS1 wrapper +- The `zed-ros2-interfaces` repository has been added as a sub-module to this repository + 2021-12-01 ---------- - Add new _base_link frame on the base of the camera to easily handle camera positioning on robots. Thx @civerachb-cpr diff --git a/zed-ros2-interfaces b/zed-ros2-interfaces index f766201a..fc26b8ca 160000 --- a/zed-ros2-interfaces +++ b/zed-ros2-interfaces @@ -1 +1 @@ -Subproject commit f766201a15968c8efa234ddad4ddd84f4273e5c3 +Subproject commit fc26b8ca6deca9cba89f03a2be927a89b3f65e88 diff --git a/zed_components/src/zed_camera/src/zed_camera_component.cpp b/zed_components/src/zed_camera/src/zed_camera_component.cpp index 8630057e..fae29394 100644 --- a/zed_components/src/zed_camera/src/zed_camera_component.cpp +++ b/zed_components/src/zed_camera/src/zed_camera_component.cpp @@ -10,8 +10,8 @@ * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in + *all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -22,6 +22,8 @@ * SOFTWARE. ********************************************************************************/ +#include "zed_camera_component.hpp" + #include #include #include @@ -31,169 +33,154 @@ #include #include "sl_tools.h" -#include "zed_camera_component.hpp" using namespace std::chrono_literals; using namespace std::placeholders; #define TIMER_TIME_FACTOR 1.5 -namespace stereolabs -{ +namespace stereolabs { #ifndef DEG2RAD #define DEG2RAD 0.017453293 #define RAD2DEG 57.295777937 #endif ZedCamera::ZedCamera(const rclcpp::NodeOptions& options) - : Node("zed_node", options) - , mDiagUpdater(this) - , mVideoQos(1) - , mDepthQos(1) - , mSensQos(1) - , mPoseQos(1) - , mMappingQos(1) - , mObjDetQos(1) + : Node("zed_node", options) + , mDiagUpdater(this) + , mVideoQos(1) + , mDepthQos(1) + , mSensQos(1) + , mPoseQos(1) + , mMappingQos(1) + , mObjDetQos(1) { - RCLCPP_INFO(get_logger(), "********************************"); - RCLCPP_INFO(get_logger(), " ZED Camera Component "); - RCLCPP_INFO(get_logger(), "********************************"); - RCLCPP_INFO(get_logger(), " * namespace: %s", get_namespace()); - RCLCPP_INFO(get_logger(), " * node name: %s", get_name()); - RCLCPP_INFO(get_logger(), "********************************"); - - if (ZED_SDK_MAJOR_VERSION < 3 || (ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION < 3)) - { - RCLCPP_ERROR(get_logger(), "The ZED ROS2 wrapper is designed to work with ZED SDK v3.3 or newer."); - RCLCPP_INFO_STREAM(get_logger(), "* Detected SDK v" << ZED_SDK_MAJOR_VERSION << "." << ZED_SDK_MINOR_VERSION << "." - << ZED_SDK_PATCH_VERSION << "-" << ZED_SDK_BUILD_ID); - RCLCPP_INFO(get_logger(), "Node stopped"); - exit(EXIT_FAILURE); - } - - // ----> Parameters initialization - getParam("general.debug_mode", mDebugMode, mDebugMode); - if (mDebugMode) - { - rcutils_ret_t res = rcutils_logging_set_logger_level(get_logger().get_name(), RCUTILS_LOG_SEVERITY_DEBUG); - - if (res != RCUTILS_RET_OK) - { - RCLCPP_INFO(get_logger(), "Error setting DEBUG level fot logger"); - } - else - { - RCLCPP_INFO(get_logger(), "*** Debug Mode enabled ***"); - } - } - else - { - rcutils_ret_t res = rcutils_logging_set_logger_level(get_logger().get_name(), RCUTILS_LOG_SEVERITY_INFO); + RCLCPP_INFO(get_logger(), "********************************"); + RCLCPP_INFO(get_logger(), " ZED Camera Component "); + RCLCPP_INFO(get_logger(), "********************************"); + RCLCPP_INFO(get_logger(), " * namespace: %s", get_namespace()); + RCLCPP_INFO(get_logger(), " * node name: %s", get_name()); + RCLCPP_INFO(get_logger(), "********************************"); + + if (ZED_SDK_MAJOR_VERSION < 3 || (ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION < 3)) { + RCLCPP_ERROR( + get_logger(), + "The ZED ROS2 wrapper is designed to work with ZED SDK v3.3 or newer."); + RCLCPP_INFO_STREAM(get_logger(), + "* Detected SDK v" << ZED_SDK_MAJOR_VERSION << "." + << ZED_SDK_MINOR_VERSION << "." + << ZED_SDK_PATCH_VERSION << "-" + << ZED_SDK_BUILD_ID); + RCLCPP_INFO(get_logger(), "Node stopped"); + exit(EXIT_FAILURE); + } + + // ----> Parameters initialization + getParam("general.debug_mode", mDebugMode, mDebugMode); + if (mDebugMode) { + rcutils_ret_t res = rcutils_logging_set_logger_level( + get_logger().get_name(), RCUTILS_LOG_SEVERITY_DEBUG); + + if (res != RCUTILS_RET_OK) { + RCLCPP_INFO(get_logger(), "Error setting DEBUG level fot logger"); + } else { + RCLCPP_INFO(get_logger(), "*** Debug Mode enabled ***"); + } + } else { + rcutils_ret_t res = rcutils_logging_set_logger_level( + get_logger().get_name(), RCUTILS_LOG_SEVERITY_INFO); - if (res != RCUTILS_RET_OK) - { - RCLCPP_INFO(get_logger(), "Error setting INFO level for logger"); + if (res != RCUTILS_RET_OK) { + RCLCPP_INFO(get_logger(), "Error setting INFO level for logger"); + } } - } - RCLCPP_DEBUG(get_logger(), "[ROS2] Using RMW_IMPLEMENTATION = %s", rmw_get_implementation_identifier()); + RCLCPP_DEBUG(get_logger(), + "[ROS2] Using RMW_IMPLEMENTATION = %s", + rmw_get_implementation_identifier()); - initParameters(); - // <---- Parameters initialization + initParameters(); + // <---- Parameters initialization - // ----> Diagnostic - mDiagUpdater.add("ZED Diagnostic", this, &ZedCamera::callback_updateDiagnostic); - mDiagUpdater.setHardwareID("ZED camera"); - // <---- Diagnostic + // ----> Diagnostic + mDiagUpdater.add( + "ZED Diagnostic", this, &ZedCamera::callback_updateDiagnostic); + mDiagUpdater.setHardwareID("ZED camera"); + // <---- Diagnostic - // Init services - initServices(); + // Init services + initServices(); - // Start camera - if (!startCamera()) - { - exit(EXIT_FAILURE); - } + // Start camera + if (!startCamera()) { + exit(EXIT_FAILURE); + } } ZedCamera::~ZedCamera() { - RCLCPP_DEBUG(get_logger(), "Destroying node"); - - if (mObjDetRunning) - { - std::lock_guard lock(mObjDetMutex); - stopObjDetect(); - } - - if (mMappingRunning) - { - std::lock_guard lock(mMappingMutex); - stop3dMapping(); - } + RCLCPP_DEBUG(get_logger(), "Destroying node"); - std::lock_guard lock(mCloseZedMutex); + if (mObjDetRunning) { + std::lock_guard lock(mObjDetMutex); + stopObjDetect(); + } - if (mSensTimer) - { - mSensTimer->cancel(); - } - if (mPathTimer) - { - mPathTimer->cancel(); - } - if (mFusedPcTimer) - { - mFusedPcTimer->cancel(); - } - if (mVideoDepthTimer) - { - mVideoDepthTimer->cancel(); - } + if (mMappingRunning) { + std::lock_guard lock(mMappingMutex); + stop3dMapping(); + } - // ----> Verify that all the threads are not active - if (!mThreadStop) - { - mThreadStop = true; + std::lock_guard lock(mCloseZedMutex); - RCLCPP_DEBUG(get_logger(), "Stopping grab thread..."); - try - { - if (mGrabThread.joinable()) - { - mGrabThread.join(); - } + if (mSensTimer) { + mSensTimer->cancel(); } - catch (std::system_error& e) - { - RCLCPP_WARN(get_logger(), "Grab thread joining exception: %s", e.what()); + if (mPathTimer) { + mPathTimer->cancel(); } - RCLCPP_DEBUG(get_logger(), "... grab thread stopped"); - - RCLCPP_DEBUG(get_logger(), "Stopping Point Cloud thread..."); - try - { - if (mPcThread.joinable()) - { - mPcThread.join(); - } + if (mFusedPcTimer) { + mFusedPcTimer->cancel(); } - catch (std::system_error& e) - { - RCLCPP_WARN(get_logger(), "Pointcloud thread joining exception: %s", e.what()); + if (mVideoDepthTimer) { + mVideoDepthTimer->cancel(); } - RCLCPP_DEBUG(get_logger(), "... Point Cloud thread stopped"); - } - // <---- Verify that the grab thread is not active + + // ----> Verify that all the threads are not active + if (!mThreadStop) { + mThreadStop = true; + + RCLCPP_DEBUG(get_logger(), "Stopping grab thread..."); + try { + if (mGrabThread.joinable()) { + mGrabThread.join(); + } + } catch (std::system_error& e) { + RCLCPP_WARN(get_logger(), "Grab thread joining exception: %s", e.what()); + } + RCLCPP_DEBUG(get_logger(), "... grab thread stopped"); + + RCLCPP_DEBUG(get_logger(), "Stopping Point Cloud thread..."); + try { + if (mPcThread.joinable()) { + mPcThread.join(); + } + } catch (std::system_error& e) { + RCLCPP_WARN( + get_logger(), "Pointcloud thread joining exception: %s", e.what()); + } + RCLCPP_DEBUG(get_logger(), "... Point Cloud thread stopped"); + } + // <---- Verify that the grab thread is not active } void ZedCamera::initServices() { - RCLCPP_INFO(get_logger(), "*** SERVICES ***"); + RCLCPP_INFO(get_logger(), "*** SERVICES ***"); - std::string srv_name; + std::string srv_name; - /*std::string srv_name; + /*std::string srv_name; std::string srv_prefix = get_namespace(); if (srv_prefix.length() > 1) { @@ -205,4124 +192,4326 @@ void ZedCamera::initServices() } srv_prefix += get_name();*/ - std::string srv_prefix = "~/"; - - // ResetOdometry - srv_name = srv_prefix + mSrvResetOdomName; - mResetOdomSrv = - create_service(srv_name, std::bind(&ZedCamera::callback_resetOdometry, this, _1, _2, _3)); - RCLCPP_INFO(get_logger(), " * '%s'", mResetOdomSrv->get_service_name()); - - srv_name = srv_prefix + mSrvResetPoseName; - mResetPosTrkSrv = create_service( - srv_name, std::bind(&ZedCamera::callback_resetPosTracking, this, _1, _2, _3)); - RCLCPP_INFO(get_logger(), " * '%s'", mResetPosTrkSrv->get_service_name()); - srv_name = srv_prefix + mSrvSetPoseName; - mSetPoseSrv = - create_service(srv_name, std::bind(&ZedCamera::callback_setPose, this, _1, _2, _3)); - RCLCPP_INFO(get_logger(), " * '%s'", mSetPoseSrv->get_service_name()); - - srv_name = srv_prefix + mSrvEnableObjDetName; - mEnableObjDetSrv = - create_service(srv_name, std::bind(&ZedCamera::callback_enableObjDet, this, _1, _2, _3)); - RCLCPP_INFO(get_logger(), " * '%s'", mEnableObjDetSrv->get_service_name()); - - srv_name = srv_prefix + mSrvEnableMappingName; - mEnableMappingSrv = - create_service(srv_name, std::bind(&ZedCamera::callback_enableMapping, this, _1, _2, _3)); - RCLCPP_INFO(get_logger(), " * '%s'", mEnableMappingSrv->get_service_name()); - - srv_name = srv_prefix + mSrvStartSvoRecName; - mStartSvoRecSrv = create_service( - srv_name, std::bind(&ZedCamera::callback_startSvoRec, this, _1, _2, _3)); - RCLCPP_INFO(get_logger(), " * '%s'", mStartSvoRecSrv->get_service_name()); - srv_name = srv_prefix + mSrvStopSvoRecName; - mStopSvoRecSrv = - create_service(srv_name, std::bind(&ZedCamera::callback_stopSvoRec, this, _1, _2, _3)); - RCLCPP_INFO(get_logger(), " * '%s'", mStopSvoRecSrv->get_service_name()); - srv_name = srv_prefix + mSrvToggleSvoPauseName; - mPauseSvoSrv = - create_service(srv_name, std::bind(&ZedCamera::callback_pauseSvoInput, this, _1, _2, _3)); - RCLCPP_INFO(get_logger(), " * '%s'", mPauseSvoSrv->get_service_name()); + std::string srv_prefix = "~/"; + + // ResetOdometry + srv_name = srv_prefix + mSrvResetOdomName; + mResetOdomSrv = create_service( + srv_name, std::bind(&ZedCamera::callback_resetOdometry, this, _1, _2, _3)); + RCLCPP_INFO(get_logger(), " * '%s'", mResetOdomSrv->get_service_name()); + + srv_name = srv_prefix + mSrvResetPoseName; + mResetPosTrkSrv = create_service( + srv_name, + std::bind(&ZedCamera::callback_resetPosTracking, this, _1, _2, _3)); + RCLCPP_INFO(get_logger(), " * '%s'", mResetPosTrkSrv->get_service_name()); + srv_name = srv_prefix + mSrvSetPoseName; + mSetPoseSrv = create_service( + srv_name, std::bind(&ZedCamera::callback_setPose, this, _1, _2, _3)); + RCLCPP_INFO(get_logger(), " * '%s'", mSetPoseSrv->get_service_name()); + + srv_name = srv_prefix + mSrvEnableObjDetName; + mEnableObjDetSrv = create_service( + srv_name, std::bind(&ZedCamera::callback_enableObjDet, this, _1, _2, _3)); + RCLCPP_INFO(get_logger(), " * '%s'", mEnableObjDetSrv->get_service_name()); + + srv_name = srv_prefix + mSrvEnableMappingName; + mEnableMappingSrv = create_service( + srv_name, std::bind(&ZedCamera::callback_enableMapping, this, _1, _2, _3)); + RCLCPP_INFO(get_logger(), " * '%s'", mEnableMappingSrv->get_service_name()); + + srv_name = srv_prefix + mSrvStartSvoRecName; + mStartSvoRecSrv = create_service( + srv_name, std::bind(&ZedCamera::callback_startSvoRec, this, _1, _2, _3)); + RCLCPP_INFO(get_logger(), " * '%s'", mStartSvoRecSrv->get_service_name()); + srv_name = srv_prefix + mSrvStopSvoRecName; + mStopSvoRecSrv = create_service( + srv_name, std::bind(&ZedCamera::callback_stopSvoRec, this, _1, _2, _3)); + RCLCPP_INFO(get_logger(), " * '%s'", mStopSvoRecSrv->get_service_name()); + srv_name = srv_prefix + mSrvToggleSvoPauseName; + mPauseSvoSrv = create_service( + srv_name, std::bind(&ZedCamera::callback_pauseSvoInput, this, _1, _2, _3)); + RCLCPP_INFO(get_logger(), " * '%s'", mPauseSvoSrv->get_service_name()); } template -void ZedCamera::getParam(std::string paramName, T defValue, T& outVal, std::string log_info) +void ZedCamera::getParam(std::string paramName, + T defValue, + T& outVal, + std::string log_info) { - declare_parameter(paramName, rclcpp::ParameterValue(defValue)); + declare_parameter(paramName, rclcpp::ParameterValue(defValue)); - if (!get_parameter(paramName, outVal)) - { - RCLCPP_WARN_STREAM(get_logger(), "The parameter '" - << paramName << "' is not available or is not valid, using the default value: " - << defValue); - } + if (!get_parameter(paramName, outVal)) { + RCLCPP_WARN_STREAM( + get_logger(), + "The parameter '" + << paramName + << "' is not available or is not valid, using the default value: " + << defValue); + } - if (!log_info.empty()) - { - RCLCPP_INFO_STREAM(get_logger(), log_info << outVal); - } + if (!log_info.empty()) { + RCLCPP_INFO_STREAM(get_logger(), log_info << outVal); + } } void ZedCamera::initParameters() { - // DEBUG parameters - getDebugParams(); + // DEBUG parameters + getDebugParams(); - // GENERAL parameters - getGeneralParams(); + // GENERAL parameters + getGeneralParams(); - // VIDEO parameters - getVideoParams(); + // VIDEO parameters + getVideoParams(); - // DEPTH parameters - getDepthParams(); + // DEPTH parameters + getDepthParams(); - // POS. TRACKING parameters - getPosTrackingParams(); + // POS. TRACKING parameters + getPosTrackingParams(); - // SENSORS parameters - if(mCamUserModel!=sl::MODEL::ZED) - getSensorsParams(); + // SENSORS parameters + if (mCamUserModel != sl::MODEL::ZED) + getSensorsParams(); - getMappingParams(); + getMappingParams(); - // OD PARAMETERS - if(sl_tools::isZED2OrZED2i(mCamUserModel)) - getOdParams(); + // OD PARAMETERS + if (sl_tools::isZED2OrZED2i(mCamUserModel)) + getOdParams(); - // Dynamic parameters callback - set_on_parameters_set_callback(std::bind(&ZedCamera::callback_paramChange, this, _1)); + // Dynamic parameters callback + set_on_parameters_set_callback( + std::bind(&ZedCamera::callback_paramChange, this, _1)); } void ZedCamera::getDebugParams() { - rclcpp::Parameter paramVal; - std::string paramName; + rclcpp::Parameter paramVal; + std::string paramName; - RCLCPP_INFO(get_logger(), "*** DEBUG parameters ***"); + RCLCPP_INFO(get_logger(), "*** DEBUG parameters ***"); } void ZedCamera::getGeneralParams() { - rclcpp::Parameter paramVal; - std::string paramName; + rclcpp::Parameter paramVal; + std::string paramName; - RCLCPP_INFO(get_logger(), "*** GENERAL parameters ***"); + RCLCPP_INFO(get_logger(), "*** GENERAL parameters ***"); - std::string camera_model = "zed"; - getParam("general.camera_model", camera_model, camera_model); - if (camera_model == "zed") - { - mCamUserModel = sl::MODEL::ZED; - } - else if (camera_model == "zedm") - { - mCamUserModel = sl::MODEL::ZED_M; - } - else if (camera_model == "zed2") - { - mCamUserModel = sl::MODEL::ZED2; - } + std::string camera_model = "zed"; + getParam("general.camera_model", camera_model, camera_model); + if (camera_model == "zed") { + mCamUserModel = sl::MODEL::ZED; + } else if (camera_model == "zedm") { + mCamUserModel = sl::MODEL::ZED_M; + } else if (camera_model == "zed2") { + mCamUserModel = sl::MODEL::ZED2; + } #if ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION >= 5 - else if (camera_model == "zed2i") - { - mCamUserModel = sl::MODEL::ZED2i; - } + else if (camera_model == "zed2i") { + mCamUserModel = sl::MODEL::ZED2i; + } #endif - else - { - RCLCPP_ERROR_STREAM(get_logger(), "Camera model not valid in parameter values: " << camera_model); - } - RCLCPP_INFO_STREAM(get_logger(), " * Camera model: " << camera_model << " - " << mCamUserModel); - - getParam("general.sdk_verbose", mVerbose, mVerbose, " * SDK Verbose: "); - getParam("general.svo_file", std::string(), mSvoFilepath, " * SVO: "); - getParam("general.svo_loop", mSvoLoop, mSvoLoop); - RCLCPP_INFO(get_logger(), " * SVO Loop: %s", mSvoLoop ? "TRUE" : "FALSE"); - getParam("general.svo_realtime", mSvoRealtime, mSvoRealtime); - RCLCPP_INFO(get_logger(), " * SVO Realtime: %s", mSvoRealtime ? "TRUE" : "FALSE"); - getParam("general.camera_name", mCameraName, mCameraName, " * Camera name: "); - getParam("general.zed_id", mCamId, mCamId, " * Camera ID: "); - getParam("general.serial_number", mCamSerialNumber, mCamSerialNumber, " * Camera SN: "); - getParam("general.camera_timeout_sec", mCamTimeoutSec, mCamTimeoutSec, " * Camera timeout [sec]: "); - getParam("general.camera_max_reconnect", mMaxReconnectTemp, mMaxReconnectTemp, - " * Camera reconnection temptatives: "); - getParam("general.grab_frame_rate", mCamGrabFrameRate, mCamGrabFrameRate, " * Camera framerate: "); - getParam("general.gpu_id", mGpuId, mGpuId, " * GPU ID: "); - - // TODO ADD SVO SAVE COMPRESSION PARAMETERS - - int resol = static_cast(mCamResol); - getParam("general.resolution", resol, resol); - mCamResol = static_cast(resol); - RCLCPP_INFO_STREAM(get_logger(), " * Camera resolution: " << resol << " - " << mCamResol); - - getParam("general.self_calib", mCameraSelfCalib, mCameraSelfCalib); - RCLCPP_INFO(get_logger(), " * Camera self calibration: %s", mCameraSelfCalib ? "TRUE" : "FALSE"); - getParam("general.camera_flip", mCameraFlip, mCameraFlip); - RCLCPP_INFO(get_logger(), " * Camera flip: %s", mCameraFlip ? "TRUE" : "FALSE"); - - // Dynamic parameters - - getParam("general.pub_frame_rate", mPubFrameRate, mPubFrameRate); - if (mPubFrameRate > mCamGrabFrameRate) - { - RCLCPP_WARN(get_logger(), "'pub_frame_rate' cannot be bigger than 'grab_frame_rate'", paramName.c_str()); - } - RCLCPP_INFO(get_logger(), " * [DYN] Publish framerate [Hz]: %g ", mPubFrameRate); + else { + RCLCPP_ERROR_STREAM( + get_logger(), + "Camera model not valid in parameter values: " << camera_model); + } + RCLCPP_INFO_STREAM(get_logger(), + " * Camera model: " << camera_model << " - " + << mCamUserModel); + + getParam("general.sdk_verbose", mVerbose, mVerbose, " * SDK Verbose: "); + getParam("general.svo_file", std::string(), mSvoFilepath, " * SVO: "); + getParam("general.svo_loop", mSvoLoop, mSvoLoop); + RCLCPP_INFO(get_logger(), " * SVO Loop: %s", mSvoLoop ? "TRUE" : "FALSE"); + getParam("general.svo_realtime", mSvoRealtime, mSvoRealtime); + RCLCPP_INFO( + get_logger(), " * SVO Realtime: %s", mSvoRealtime ? "TRUE" : "FALSE"); + getParam("general.camera_name", mCameraName, mCameraName, " * Camera name: "); + getParam("general.zed_id", mCamId, mCamId, " * Camera ID: "); + getParam("general.serial_number", + mCamSerialNumber, + mCamSerialNumber, + " * Camera SN: "); + getParam("general.camera_timeout_sec", + mCamTimeoutSec, + mCamTimeoutSec, + " * Camera timeout [sec]: "); + getParam("general.camera_max_reconnect", + mMaxReconnectTemp, + mMaxReconnectTemp, + " * Camera reconnection temptatives: "); + getParam("general.grab_frame_rate", + mCamGrabFrameRate, + mCamGrabFrameRate, + " * Camera framerate: "); + getParam("general.gpu_id", mGpuId, mGpuId, " * GPU ID: "); + + // TODO ADD SVO SAVE COMPRESSION PARAMETERS + + int resol = static_cast(mCamResol); + getParam("general.resolution", resol, resol); + mCamResol = static_cast(resol); + RCLCPP_INFO_STREAM(get_logger(), + " * Camera resolution: " << resol << " - " << mCamResol); + + getParam("general.self_calib", mCameraSelfCalib, mCameraSelfCalib); + RCLCPP_INFO(get_logger(), + " * Camera self calibration: %s", + mCameraSelfCalib ? "TRUE" : "FALSE"); + getParam("general.camera_flip", mCameraFlip, mCameraFlip); + RCLCPP_INFO( + get_logger(), " * Camera flip: %s", mCameraFlip ? "TRUE" : "FALSE"); + + // Dynamic parameters + + getParam("general.pub_frame_rate", mPubFrameRate, mPubFrameRate); + if (mPubFrameRate > mCamGrabFrameRate) { + RCLCPP_WARN(get_logger(), + "'pub_frame_rate' cannot be bigger than 'grab_frame_rate'", + paramName.c_str()); + } + RCLCPP_INFO( + get_logger(), " * [DYN] Publish framerate [Hz]: %g ", mPubFrameRate); } void ZedCamera::getVideoParams() { - rclcpp::Parameter paramVal; - std::string paramName; + rclcpp::Parameter paramVal; + std::string paramName; + + RCLCPP_INFO(get_logger(), "*** VIDEO parameters ***"); + + rmw_qos_history_policy_t qos_hist = RMW_QOS_POLICY_HISTORY_KEEP_LAST; + int qos_depth = 1; + rmw_qos_reliability_policy_t qos_reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE; + rmw_qos_durability_policy_t qos_durability = RMW_QOS_POLICY_DURABILITY_VOLATILE; + + getParam("video.extrinsic_in_camera_frame", + mUseOldExtrinsic, + mUseOldExtrinsic, + " * Use old extrinsic parameters: "); + + getParam( + "video.img_downsample_factor", mImgDownsampleFactor, mImgDownsampleFactor); + if (mImgDownsampleFactor < 0.1) { + mImgDownsampleFactor = 0.1; + RCLCPP_WARN(get_logger(), + "The minimum value allowed for '%s' is 0.1", + paramName.c_str()); + } else if (mImgDownsampleFactor > 1.0) { + mImgDownsampleFactor = 1.0; + RCLCPP_WARN(get_logger(), + "The maximum value allowed for '%s' is 1.0", + paramName.c_str()); + } + RCLCPP_INFO(get_logger(), + " * [DYN] Image downsample factor: %g ", + mImgDownsampleFactor); + + getParam("video.brightness", + mCamBrightness, + mCamBrightness, + " * [DYN] Brightness: "); + getParam("video.contrast", mCamContrast, mCamContrast, " * [DYN] Contrast: "); + getParam("video.hue", mCamHue, mCamHue, " * [DYN] Hue: "); + getParam("video.saturation", + mCamSaturation, + mCamSaturation, + " * [DYN] Saturation: "); + getParam( + "video.sharpness", mCamSharpness, mCamSharpness, " * [DYN] Sharpness: "); + getParam("video.gamma", mCamGamma, mCamGamma, " * [DYN] Gamma: "); + getParam("video.auto_exposure_gain", mCamAutoExpGain, mCamAutoExpGain); + RCLCPP_INFO(get_logger(), + " * [DYN] Auto Exposure/Gain: %s", + mCamAutoExpGain ? "TRUE" : "FALSE"); + if (mCamAutoExpGain) { + mTriggerAutoExpGain = true; + } + getParam("video.exposure", mCamExposure, mCamExposure, " * [DYN] Exposure: "); + getParam("video.gain", mCamGain, mCamGain, " * [DYN] Gain: "); + getParam("video.auto_whitebalance", mCamAutoWB, mCamAutoWB); + RCLCPP_INFO(get_logger(), + " * [DYN] Auto White Balance: %s", + mCamAutoWB ? "TRUE" : "FALSE"); + if (mCamAutoWB) { + mTriggerAutoWB = true; + } + int wb = 42; + getParam("video.whitebalance_temperature", + wb, + wb, + " * [DYN] White Balance Temperature: "); + mCamWBTemp = wb * 100; - RCLCPP_INFO(get_logger(), "*** VIDEO parameters ***"); + // ------------------------------------------ - rmw_qos_history_policy_t qos_hist = RMW_QOS_POLICY_HISTORY_KEEP_LAST; - int qos_depth = 1; - rmw_qos_reliability_policy_t qos_reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE; - rmw_qos_durability_policy_t qos_durability = RMW_QOS_POLICY_DURABILITY_VOLATILE; + paramName = "video.qos_history"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_hist)); - getParam("video.extrinsic_in_camera_frame", mUseOldExtrinsic, mUseOldExtrinsic, " * Use old extrinsic parameters: "); + if (get_parameter(paramName, paramVal)) { + qos_hist = paramVal.as_int() == 1 ? RMW_QOS_POLICY_HISTORY_KEEP_LAST + : RMW_QOS_POLICY_HISTORY_KEEP_ALL; + mVideoQos.history(qos_hist); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - getParam("video.img_downsample_factor", mImgDownsampleFactor, mImgDownsampleFactor); - if (mImgDownsampleFactor < 0.1) - { - mImgDownsampleFactor = 0.1; - RCLCPP_WARN(get_logger(), "The minimum value allowed for '%s' is 0.1", paramName.c_str()); - } - else if (mImgDownsampleFactor > 1.0) - { - mImgDownsampleFactor = 1.0; - RCLCPP_WARN(get_logger(), "The maximum value allowed for '%s' is 1.0", paramName.c_str()); - } - RCLCPP_INFO(get_logger(), " * [DYN] Image downsample factor: %g ", mImgDownsampleFactor); - - getParam("video.brightness", mCamBrightness, mCamBrightness, " * [DYN] Brightness: "); - getParam("video.contrast", mCamContrast, mCamContrast, " * [DYN] Contrast: "); - getParam("video.hue", mCamHue, mCamHue, " * [DYN] Hue: "); - getParam("video.saturation", mCamSaturation, mCamSaturation, " * [DYN] Saturation: "); - getParam("video.sharpness", mCamSharpness, mCamSharpness, " * [DYN] Sharpness: "); - getParam("video.gamma", mCamGamma, mCamGamma, " * [DYN] Gamma: "); - getParam("video.auto_exposure_gain", mCamAutoExpGain, mCamAutoExpGain); - RCLCPP_INFO(get_logger(), " * [DYN] Auto Exposure/Gain: %s", mCamAutoExpGain ? "TRUE" : "FALSE"); - if (mCamAutoExpGain) - { - mTriggerAutoExpGain = true; - } - getParam("video.exposure", mCamExposure, mCamExposure, " * [DYN] Exposure: "); - getParam("video.gain", mCamGain, mCamGain, " * [DYN] Gain: "); - getParam("video.auto_whitebalance", mCamAutoWB, mCamAutoWB); - RCLCPP_INFO(get_logger(), " * [DYN] Auto White Balance: %s", mCamAutoWB ? "TRUE" : "FALSE"); - if (mCamAutoWB) - { - mTriggerAutoWB = true; - } - int wb = 42; - getParam("video.whitebalance_temperature", wb, wb, " * [DYN] White Balance Temperature: "); - mCamWBTemp = wb * 100; + RCLCPP_INFO(get_logger(), + " * Video QoS History: %s", + sl_tools::qos2str(qos_hist).c_str()); - // ------------------------------------------ + // ------------------------------------------ - paramName = "video.qos_history"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_hist)); + paramName = "video.qos_depth"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_depth)); - if (get_parameter(paramName, paramVal)) - { - qos_hist = paramVal.as_int() == 1 ? RMW_QOS_POLICY_HISTORY_KEEP_LAST : RMW_QOS_POLICY_HISTORY_KEEP_ALL; - mVideoQos.history(qos_hist); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } + if (get_parameter(paramName, paramVal)) { + qos_depth = paramVal.as_int(); + mVideoQos.keep_last(qos_depth); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - RCLCPP_INFO(get_logger(), " * Video QoS History: %s", sl_tools::qos2str(qos_hist).c_str()); + RCLCPP_INFO(get_logger(), " * Video QoS History depth: %d", qos_depth); - // ------------------------------------------ + // ------------------------------------------ - paramName = "video.qos_depth"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_depth)); + paramName = "video.qos_reliability"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_reliability)); - if (get_parameter(paramName, paramVal)) - { - qos_depth = paramVal.as_int(); - mVideoQos.keep_last(qos_depth); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } + if (get_parameter(paramName, paramVal)) { + qos_reliability = paramVal.as_int() == 1 + ? RMW_QOS_POLICY_RELIABILITY_RELIABLE + : RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; - RCLCPP_INFO(get_logger(), " * Video QoS History depth: %d", qos_depth); + mVideoQos.reliability(qos_reliability); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - // ------------------------------------------ + RCLCPP_INFO(get_logger(), + " * Video QoS Reliability: %s", + sl_tools::qos2str(qos_reliability).c_str()); - paramName = "video.qos_reliability"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_reliability)); + // ------------------------------------------ - if (get_parameter(paramName, paramVal)) - { - qos_reliability = - paramVal.as_int() == 1 ? RMW_QOS_POLICY_RELIABILITY_RELIABLE : RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; + paramName = "video.qos_durability"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_durability)); - mVideoQos.reliability(qos_reliability); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } + if (get_parameter(paramName, paramVal)) { + qos_durability = paramVal.as_int() == 0 + ? RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL + : RMW_QOS_POLICY_DURABILITY_VOLATILE; + mVideoQos.durability(qos_durability); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - RCLCPP_INFO(get_logger(), " * Video QoS Reliability: %s", sl_tools::qos2str(qos_reliability).c_str()); + RCLCPP_INFO(get_logger(), + " * Video QoS Durability: %s", + sl_tools::qos2str(qos_durability).c_str()); +} - // ------------------------------------------ +void ZedCamera::getDepthParams() +{ + rclcpp::Parameter paramVal; + std::string paramName; + + rmw_qos_history_policy_t qos_hist = RMW_QOS_POLICY_HISTORY_KEEP_LAST; + int qos_depth = 1; + rmw_qos_reliability_policy_t qos_reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE; + rmw_qos_durability_policy_t qos_durability = RMW_QOS_POLICY_DURABILITY_VOLATILE; + + RCLCPP_INFO(get_logger(), "*** DEPTH parameters ***"); + + int depth_quality = static_cast(mDepthQuality); + getParam("depth.quality", depth_quality, depth_quality); + mDepthQuality = static_cast(depth_quality); + + if (mDepthQuality == sl::DEPTH_MODE::NONE) { + mDepthDisabled = true; + RCLCPP_INFO_STREAM(get_logger(), + " * Depth quality: " << depth_quality << " - " + << mDepthQuality + << " - DEPTH DISABLED"); + } else { + mDepthDisabled = false; + RCLCPP_INFO_STREAM(get_logger(), + " * Depth quality: " << depth_quality << " - " + << mDepthQuality); + } + + if (!mDepthDisabled) { + getParam("depth.depth_downsample_factor", + mDepthDownsampleFactor, + mDepthDownsampleFactor); + if (mDepthDownsampleFactor < 0.1) { + mDepthDownsampleFactor = 0.1; + RCLCPP_WARN(get_logger(), + "The minimum value allowed for '%s' is 0.1", + paramName.c_str()); + } else if (mDepthDownsampleFactor > 1.0) { + mDepthDownsampleFactor = 1.0; + RCLCPP_WARN(get_logger(), + "The maximum value allowed for '%s' is 1.0", + paramName.c_str()); + } + RCLCPP_INFO( + get_logger(), " * Depth downsample factor: %g ", mDepthDownsampleFactor); + + getParam( + "depth.min_depth", mCamMinDepth, mCamMinDepth, " * Min depth [m]: "); + getParam( + "depth.max_depth", mCamMaxDepth, mCamMaxDepth, " * Max depth [m]: "); + + int sens_mode = static_cast(mDepthSensingMode); + getParam("depth.sensing_mode", sens_mode, sens_mode); + mDepthSensingMode = static_cast(sens_mode); + RCLCPP_INFO_STREAM(get_logger(), + " * Depth Sensing Mode: " << sens_mode << " - " + << mDepthSensingMode); + + getParam( + "depth.depth_stabilization", mDepthStabilization, mDepthStabilization); + RCLCPP_INFO(get_logger(), + " * Depth Stabilization: %s", + mDepthStabilization ? "TRUE" : "FALSE"); + + getParam("depth.openni_depth_mode", mOpenniDepthMode, mOpenniDepthMode); + RCLCPP_INFO(get_logger(), + " * OpenNI mode (16bit point cloud): %s", + mOpenniDepthMode ? "TRUE" : "FALSE"); + + getParam("depth.point_cloud_freq", + mPcPubRate, + mPcPubRate, + " * [DYN] Point cloud rate [Hz]: "); + + getParam("depth.depth_confidence", + mDepthConf, + mDepthConf, + " * [DYN] Depth Confidence: "); + getParam("depth.depth_texture_conf", + mDepthTextConf, + mDepthTextConf, + " * [DYN] Depth Texture Confidence: "); + + // ------------------------------------------ + + paramName = "depth.qos_history"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_hist)); + + if (get_parameter(paramName, paramVal)) { + qos_hist = paramVal.as_int() == 1 ? RMW_QOS_POLICY_HISTORY_KEEP_LAST + : RMW_QOS_POLICY_HISTORY_KEEP_ALL; + mDepthQos.history(qos_hist); + } else { + RCLCPP_WARN( + get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - paramName = "video.qos_durability"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_durability)); + RCLCPP_INFO(get_logger(), + " * Depth QoS History: %s", + sl_tools::qos2str(qos_hist).c_str()); - if (get_parameter(paramName, paramVal)) - { - qos_durability = - paramVal.as_int() == 0 ? RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL : RMW_QOS_POLICY_DURABILITY_VOLATILE; - mVideoQos.durability(qos_durability); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } + // ------------------------------------------ - RCLCPP_INFO(get_logger(), " * Video QoS Durability: %s", sl_tools::qos2str(qos_durability).c_str()); -} + paramName = "depth.qos_depth"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_depth)); -void ZedCamera::getDepthParams() -{ - rclcpp::Parameter paramVal; - std::string paramName; + if (get_parameter(paramName, paramVal)) { + qos_depth = paramVal.as_int(); + mDepthQos.keep_last(qos_depth); + } else { + RCLCPP_WARN( + get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - rmw_qos_history_policy_t qos_hist = RMW_QOS_POLICY_HISTORY_KEEP_LAST; - int qos_depth = 1; - rmw_qos_reliability_policy_t qos_reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE; - rmw_qos_durability_policy_t qos_durability = RMW_QOS_POLICY_DURABILITY_VOLATILE; + RCLCPP_INFO(get_logger(), " * Depth QoS History depth: %d", qos_depth); - RCLCPP_INFO(get_logger(), "*** DEPTH parameters ***"); + // ------------------------------------------ - int depth_quality = static_cast(mDepthQuality); - getParam("depth.quality", depth_quality, depth_quality); - mDepthQuality = static_cast(depth_quality); + paramName = "depth.qos_reliability"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_reliability)); - if (mDepthQuality == sl::DEPTH_MODE::NONE) - { - mDepthDisabled = true; - RCLCPP_INFO_STREAM(get_logger(), - " * Depth quality: " << depth_quality << " - " << mDepthQuality << " - DEPTH DISABLED"); - } - else - { - mDepthDisabled = false; - RCLCPP_INFO_STREAM(get_logger(), " * Depth quality: " << depth_quality << " - " << mDepthQuality); - } + if (get_parameter(paramName, paramVal)) { + qos_reliability = paramVal.as_int() == 1 + ? RMW_QOS_POLICY_RELIABILITY_RELIABLE + : RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; + mDepthQos.reliability(qos_reliability); + } else { + RCLCPP_WARN( + get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - if (!mDepthDisabled) - { - getParam("depth.depth_downsample_factor", mDepthDownsampleFactor, mDepthDownsampleFactor); - if (mDepthDownsampleFactor < 0.1) - { - mDepthDownsampleFactor = 0.1; - RCLCPP_WARN(get_logger(), "The minimum value allowed for '%s' is 0.1", paramName.c_str()); - } - else if (mDepthDownsampleFactor > 1.0) - { - mDepthDownsampleFactor = 1.0; - RCLCPP_WARN(get_logger(), "The maximum value allowed for '%s' is 1.0", paramName.c_str()); - } - RCLCPP_INFO(get_logger(), " * Depth downsample factor: %g ", mDepthDownsampleFactor); + RCLCPP_INFO(get_logger(), + " * Depth QoS Reliability: %s", + sl_tools::qos2str(qos_reliability).c_str()); + + // ------------------------------------------ + + paramName = "depth.qos_durability"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_durability)); + + if (get_parameter(paramName, paramVal)) { + qos_durability = paramVal.as_int() == 1 + ? RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL + : RMW_QOS_POLICY_DURABILITY_VOLATILE; + mDepthQos.durability(qos_durability); + } else { + RCLCPP_WARN( + get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - getParam("depth.min_depth", mCamMinDepth, mCamMinDepth, " * Min depth [m]: "); - getParam("depth.max_depth", mCamMaxDepth, mCamMaxDepth, " * Max depth [m]: "); + RCLCPP_INFO(get_logger(), + " * Depth QoS Durability: %s", + sl_tools::qos2str(qos_durability).c_str()); + } +} - int sens_mode = static_cast(mDepthSensingMode); - getParam("depth.sensing_mode", sens_mode, sens_mode); - mDepthSensingMode = static_cast(sens_mode); - RCLCPP_INFO_STREAM(get_logger(), " * Depth Sensing Mode: " << sens_mode << " - " << mDepthSensingMode); +void ZedCamera::getSensorsParams() +{ + rclcpp::Parameter paramVal; + std::string paramName; - getParam("depth.depth_stabilization", mDepthStabilization, mDepthStabilization); - RCLCPP_INFO(get_logger(), " * Depth Stabilization: %s", mDepthStabilization ? "TRUE" : "FALSE"); + rmw_qos_history_policy_t qos_hist = RMW_QOS_POLICY_HISTORY_KEEP_LAST; + int qos_depth = 1; + rmw_qos_reliability_policy_t qos_reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE; + rmw_qos_durability_policy_t qos_durability = RMW_QOS_POLICY_DURABILITY_VOLATILE; - getParam("depth.openni_depth_mode", mOpenniDepthMode, mOpenniDepthMode); - RCLCPP_INFO(get_logger(), " * OpenNI mode (16bit point cloud): %s", mOpenniDepthMode ? "TRUE" : "FALSE"); + RCLCPP_INFO(get_logger(), "*** SENSORS STACK parameters ***"); + if (mCamUserModel == sl::MODEL::ZED) { + RCLCPP_WARN(get_logger(), + "!!! SENSORS parameters are not used with ZED !!!"); + } - getParam("depth.point_cloud_freq", mPcPubRate, mPcPubRate, " * [DYN] Point cloud rate [Hz]: "); + getParam("sensors.sensors_image_sync", mSensCameraSync, mSensCameraSync); + RCLCPP_INFO_STREAM( + get_logger(), + " * Sensors Camera Sync: " << (mSensCameraSync ? "TRUE" : "FALSE")); - getParam("depth.depth_confidence", mDepthConf, mDepthConf, " * [DYN] Depth Confidence: "); - getParam("depth.depth_texture_conf", mDepthTextConf, mDepthTextConf, " * [DYN] Depth Texture Confidence: "); + getParam("sensors.sensors_pub_rate", mSensPubRate, mSensPubRate); + if (mSensPubRate < mCamGrabFrameRate) { + mSensPubRate = mCamGrabFrameRate; + } + RCLCPP_INFO_STREAM(get_logger(), + " * Sensors publishing rate: " << mSensPubRate << " Hz"); // ------------------------------------------ - paramName = "depth.qos_history"; + paramName = "sensors.qos_history"; declare_parameter(paramName, rclcpp::ParameterValue(qos_hist)); - if (get_parameter(paramName, paramVal)) - { - qos_hist = paramVal.as_int() == 1 ? RMW_QOS_POLICY_HISTORY_KEEP_LAST : RMW_QOS_POLICY_HISTORY_KEEP_ALL; - mDepthQos.history(qos_hist); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); + if (get_parameter(paramName, paramVal)) { + qos_hist = paramVal.as_int() == 1 ? RMW_QOS_POLICY_HISTORY_KEEP_LAST + : RMW_QOS_POLICY_HISTORY_KEEP_ALL; + mSensQos.history(qos_hist); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); } - RCLCPP_INFO(get_logger(), " * Depth QoS History: %s", sl_tools::qos2str(qos_hist).c_str()); + RCLCPP_INFO(get_logger(), + " * Sensors QoS History: %s", + sl_tools::qos2str(qos_hist).c_str()); // ------------------------------------------ - paramName = "depth.qos_depth"; + paramName = "sensors.qos_depth"; declare_parameter(paramName, rclcpp::ParameterValue(qos_depth)); - if (get_parameter(paramName, paramVal)) - { - qos_depth = paramVal.as_int(); - mDepthQos.keep_last(qos_depth); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); + if (get_parameter(paramName, paramVal)) { + qos_depth = paramVal.as_int(); + mSensQos.keep_last(qos_depth); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); } - RCLCPP_INFO(get_logger(), " * Depth QoS History depth: %d", qos_depth); + RCLCPP_INFO(get_logger(), " * Sensors QoS History depth: %d", qos_depth); // ------------------------------------------ - paramName = "depth.qos_reliability"; + paramName = "sensors.qos_reliability"; declare_parameter(paramName, rclcpp::ParameterValue(qos_reliability)); - if (get_parameter(paramName, paramVal)) - { - qos_reliability = - paramVal.as_int() == 1 ? RMW_QOS_POLICY_RELIABILITY_RELIABLE : RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; - mDepthQos.reliability(qos_reliability); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); + if (get_parameter(paramName, paramVal)) { + qos_reliability = paramVal.as_int() == 1 + ? RMW_QOS_POLICY_RELIABILITY_RELIABLE + : RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; + mSensQos.reliability(qos_reliability); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); } - RCLCPP_INFO(get_logger(), " * Depth QoS Reliability: %s", sl_tools::qos2str(qos_reliability).c_str()); + RCLCPP_INFO(get_logger(), + " * Sensors QoS Reliability: %s", + sl_tools::qos2str(qos_reliability).c_str()); // ------------------------------------------ - paramName = "depth.qos_durability"; + paramName = "sensors.qos_durability"; declare_parameter(paramName, rclcpp::ParameterValue(qos_durability)); - if (get_parameter(paramName, paramVal)) - { - qos_durability = - paramVal.as_int() == 1 ? RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL : RMW_QOS_POLICY_DURABILITY_VOLATILE; - mDepthQos.durability(qos_durability); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); + if (get_parameter(paramName, paramVal)) { + qos_durability = paramVal.as_int() == 1 + ? RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL + : RMW_QOS_POLICY_DURABILITY_VOLATILE; + mSensQos.durability(qos_durability); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); } - RCLCPP_INFO(get_logger(), " * Depth QoS Durability: %s", sl_tools::qos2str(qos_durability).c_str()); - } + RCLCPP_INFO(get_logger(), + " * Sensors QoS Durability: %s", + sl_tools::qos2str(qos_durability).c_str()); } -void ZedCamera::getSensorsParams() +void ZedCamera::getMappingParams() { - rclcpp::Parameter paramVal; - std::string paramName; - - rmw_qos_history_policy_t qos_hist = RMW_QOS_POLICY_HISTORY_KEEP_LAST; - int qos_depth = 1; - rmw_qos_reliability_policy_t qos_reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE; - rmw_qos_durability_policy_t qos_durability = RMW_QOS_POLICY_DURABILITY_VOLATILE; - - RCLCPP_INFO(get_logger(), "*** SENSORS STACK parameters ***"); - if (mCamUserModel == sl::MODEL::ZED) - { - RCLCPP_WARN(get_logger(), "!!! SENSORS parameters are not used with ZED !!!"); - } - - getParam("sensors.sensors_image_sync", mSensCameraSync, mSensCameraSync); - RCLCPP_INFO_STREAM(get_logger(), " * Sensors Camera Sync: " << (mSensCameraSync ? "TRUE" : "FALSE")); - - getParam("sensors.sensors_pub_rate", mSensPubRate, mSensPubRate); - if (mSensPubRate < mCamGrabFrameRate) - { - mSensPubRate = mCamGrabFrameRate; - } - RCLCPP_INFO_STREAM(get_logger(), " * Sensors publishing rate: " << mSensPubRate << " Hz"); + rclcpp::Parameter paramVal; + std::string paramName; + + rmw_qos_history_policy_t qos_hist = RMW_QOS_POLICY_HISTORY_KEEP_LAST; + int qos_depth = 1; + rmw_qos_reliability_policy_t qos_reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE; + rmw_qos_durability_policy_t qos_durability = RMW_QOS_POLICY_DURABILITY_VOLATILE; + + RCLCPP_INFO(get_logger(), "*** Spatial Mapping parameters ***"); + + getParam("mapping.mapping_enabled", mMappingEnabled, mMappingEnabled); + RCLCPP_INFO_STREAM( + get_logger(), + " * Spatial Mapping Enabled: " << (mMappingEnabled ? "TRUE" : "FALSE")); + + getParam("mapping.resolution", + mMappingRes, + mMappingRes, + " * Spatial Mapping resolution [m]: "); + getParam("mapping.max_mapping_range", mMappingRangeMax, mMappingRangeMax); + if (mMappingRangeMax == -1.0f) { + RCLCPP_INFO(get_logger(), " * 3D Max Mapping range: AUTO"); + } else { + RCLCPP_INFO_STREAM(get_logger(), + " * 3D Max Mapping range [m]: " << mMappingRangeMax); + } + getParam("mapping.fused_pointcloud_freq", + mFusedPcPubRate, + mFusedPcPubRate, + " * Map publishing rate [Hz]: "); - // ------------------------------------------ + // ------------------------------------------ - paramName = "sensors.qos_history"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_hist)); + paramName = "mapping.qos_history"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_hist)); - if (get_parameter(paramName, paramVal)) - { - qos_hist = paramVal.as_int() == 1 ? RMW_QOS_POLICY_HISTORY_KEEP_LAST : RMW_QOS_POLICY_HISTORY_KEEP_ALL; - mSensQos.history(qos_hist); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } + if (get_parameter(paramName, paramVal)) { + qos_hist = paramVal.as_int() == 1 ? RMW_QOS_POLICY_HISTORY_KEEP_LAST + : RMW_QOS_POLICY_HISTORY_KEEP_ALL; + mMappingQos.history(qos_hist); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - RCLCPP_INFO(get_logger(), " * Sensors QoS History: %s", sl_tools::qos2str(qos_hist).c_str()); + RCLCPP_INFO(get_logger(), + " * Sensors QoS History: %s", + sl_tools::qos2str(qos_hist).c_str()); - // ------------------------------------------ + // ------------------------------------------ - paramName = "sensors.qos_depth"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_depth)); + paramName = "mapping.qos_depth"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_depth)); - if (get_parameter(paramName, paramVal)) - { - qos_depth = paramVal.as_int(); - mSensQos.keep_last(qos_depth); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } + if (get_parameter(paramName, paramVal)) { + qos_depth = paramVal.as_int(); + mMappingQos.keep_last(qos_depth); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - RCLCPP_INFO(get_logger(), " * Sensors QoS History depth: %d", qos_depth); + RCLCPP_INFO(get_logger(), " * Sensors QoS History depth: %d", qos_depth); - // ------------------------------------------ + // ------------------------------------------ - paramName = "sensors.qos_reliability"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_reliability)); + paramName = "mapping.qos_reliability"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_reliability)); - if (get_parameter(paramName, paramVal)) - { - qos_reliability = - paramVal.as_int() == 1 ? RMW_QOS_POLICY_RELIABILITY_RELIABLE : RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; - mSensQos.reliability(qos_reliability); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } + if (get_parameter(paramName, paramVal)) { + qos_reliability = paramVal.as_int() == 1 + ? RMW_QOS_POLICY_RELIABILITY_RELIABLE + : RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; + mMappingQos.reliability(qos_reliability); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - RCLCPP_INFO(get_logger(), " * Sensors QoS Reliability: %s", sl_tools::qos2str(qos_reliability).c_str()); + RCLCPP_INFO(get_logger(), + " * Sensors QoS Reliability: %s", + sl_tools::qos2str(qos_reliability).c_str()); - // ------------------------------------------ + // ------------------------------------------ - paramName = "sensors.qos_durability"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_durability)); + paramName = "mapping.qos_durability"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_durability)); - if (get_parameter(paramName, paramVal)) - { - qos_durability = - paramVal.as_int() == 1 ? RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL : RMW_QOS_POLICY_DURABILITY_VOLATILE; - mSensQos.durability(qos_durability); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } + if (get_parameter(paramName, paramVal)) { + qos_durability = paramVal.as_int() == 1 + ? RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL + : RMW_QOS_POLICY_DURABILITY_VOLATILE; + mMappingQos.durability(qos_durability); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - RCLCPP_INFO(get_logger(), " * Sensors QoS Durability: %s", sl_tools::qos2str(qos_durability).c_str()); + RCLCPP_INFO(get_logger(), + " * Sensors QoS Durability: %s", + sl_tools::qos2str(qos_durability).c_str()); } -void ZedCamera::getMappingParams() +void ZedCamera::getPosTrackingParams() { - rclcpp::Parameter paramVal; - std::string paramName; + rclcpp::Parameter paramVal; + std::string paramName; - rmw_qos_history_policy_t qos_hist = RMW_QOS_POLICY_HISTORY_KEEP_LAST; - int qos_depth = 1; - rmw_qos_reliability_policy_t qos_reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE; - rmw_qos_durability_policy_t qos_durability = RMW_QOS_POLICY_DURABILITY_VOLATILE; + rmw_qos_history_policy_t qos_hist = RMW_QOS_POLICY_HISTORY_KEEP_LAST; + int qos_depth = 1; + rmw_qos_reliability_policy_t qos_reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE; + rmw_qos_durability_policy_t qos_durability = RMW_QOS_POLICY_DURABILITY_VOLATILE; - RCLCPP_INFO(get_logger(), "*** Spatial Mapping parameters ***"); + RCLCPP_INFO(get_logger(), "*** POSITIONAL TRACKING parameters ***"); - getParam("mapping.mapping_enabled", mMappingEnabled, mMappingEnabled); - RCLCPP_INFO_STREAM(get_logger(), " * Spatial Mapping Enabled: " << (mMappingEnabled ? "TRUE" : "FALSE")); - - getParam("mapping.resolution", mMappingRes, mMappingRes, " * Spatial Mapping resolution [m]: "); - getParam("mapping.max_mapping_range", mMappingRangeMax, mMappingRangeMax); - if (mMappingRangeMax == -1.0f) - { - RCLCPP_INFO(get_logger(), " * 3D Max Mapping range: AUTO"); - } - else - { - RCLCPP_INFO_STREAM(get_logger(), " * 3D Max Mapping range [m]: " << mMappingRangeMax); - } - getParam("mapping.fused_pointcloud_freq", mFusedPcPubRate, mFusedPcPubRate, " * Map publishing rate [Hz]: "); + getParam("pos_tracking.pos_tracking_enabled", + mPosTrackingEnabled, + mPosTrackingEnabled); + RCLCPP_INFO_STREAM(get_logger(), + " * Positional tracking enabled: " + << (mPosTrackingEnabled ? "TRUE" : "FALSE")); + + getParam("pos_tracking.base_frame", + mBaseFrameId, + mBaseFrameId, + " * Base frame id: "); + getParam( + "pos_tracking.map_frame", mMapFrameId, mMapFrameId, " * Map frame id: "); + getParam("pos_tracking.odometry_frame", + mOdomFrameId, + mOdomFrameId, + " * Odometry frame id: "); + + getParam("pos_tracking.publish_tf", mPublishTF, mPublishTF); + RCLCPP_INFO_STREAM( + get_logger(), + " * Broadcast Odometry TF: " << (mPublishTF ? "TRUE" : "FALSE")); + if (mPublishTF) { + getParam("pos_tracking.publish_map_tf", mPublishMapTF, mPublishMapTF); + RCLCPP_INFO_STREAM( + get_logger(), + " * Broadcast Pose TF: " << (mPublishMapTF ? "TRUE" : "FALSE")); + getParam("pos_tracking.publish_imu_tf", mPublishImuTF, mPublishImuTF); + RCLCPP_INFO_STREAM(get_logger(), + " * Broadcast Static IMU TF [not for ZED]: " + << (mPublishImuTF ? "TRUE" : "FALSE")); + } + + getParam("pos_tracking.path_pub_rate", + mPathPubRate, + mPathPubRate, + " * [DYN] Path publishing rate: "); + getParam("pos_tracking.path_max_count", mPathMaxCount, mPathMaxCount); + if (mPathMaxCount < 2 && mPathMaxCount != -1) { + mPathMaxCount = 2; + } + RCLCPP_INFO_STREAM(get_logger(), " * Path history lenght: " << mPathMaxCount); + + paramName = "pos_tracking.initial_base_pose"; + declare_parameter(paramName, rclcpp::ParameterValue(mInitialBasePose)); + if (!get_parameter(paramName, mInitialBasePose)) { + RCLCPP_WARN_STREAM( + get_logger(), + "The parameter '" + << paramName + << "' is not available or is not valid, using the default value"); + mInitialBasePose = std::vector(6, 0.0); + } + if (mInitialBasePose.size() < 6) { + RCLCPP_WARN_STREAM(get_logger(), + "The parameter '" + << paramName + << "' must be a vector of 6 values of double type"); + mInitialBasePose = std::vector(6, 0.0); + } + RCLCPP_INFO(get_logger(), + " * Initial pose: [%g,%g,%g,%g,%g,%g,]", + mInitialBasePose[0], + mInitialBasePose[1], + mInitialBasePose[2], + mInitialBasePose[3], + mInitialBasePose[4], + mInitialBasePose[5]); + + getParam("pos_tracking.area_memory", mAreaMemory, mAreaMemory); + RCLCPP_INFO_STREAM(get_logger(), + " * Area Memory: " << (mAreaMemory ? "TRUE" : "FALSE")); + getParam("pos_tracking.area_memory_db_path", + mAreaMemoryDbPath, + mAreaMemoryDbPath, + " * Area Memory DB: "); + getParam("pos_tracking.imu_fusion", mImuFusion, mImuFusion); + RCLCPP_INFO_STREAM( + get_logger(), + " * IMU Fusion [not for ZED]: " << (mImuFusion ? "TRUE" : "FALSE")); + getParam("pos_tracking.floor_alignment", mFloorAlignment, mFloorAlignment); + RCLCPP_INFO_STREAM( + get_logger(), + " * Floor Alignment: " << (mFloorAlignment ? "TRUE" : "FALSE")); + getParam("pos_tracking.init_odom_with_first_valid_pose", + mInitOdomWithPose, + mInitOdomWithPose); + RCLCPP_INFO_STREAM(get_logger(), + " * Init Odometry with first valid pose data: " + << (mInitOdomWithPose ? "TRUE" : "FALSE")); + getParam("pos_tracking.two_d_mode", mTwoDMode, mTwoDMode); + RCLCPP_INFO_STREAM(get_logger(), + " * 2D mode: " << (mTwoDMode ? "TRUE" : "FALSE")); + if (mTwoDMode) { + getParam("pos_tracking.fixed_z_value", + mFixedZValue, + mFixedZValue, + " * Fixed Z value: "); + } - // ------------------------------------------ + // ------------------------------------------ - paramName = "mapping.qos_history"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_hist)); + paramName = "pos_tracking.qos_history"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_hist)); - if (get_parameter(paramName, paramVal)) - { - qos_hist = paramVal.as_int() == 1 ? RMW_QOS_POLICY_HISTORY_KEEP_LAST : RMW_QOS_POLICY_HISTORY_KEEP_ALL; - mMappingQos.history(qos_hist); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } + if (get_parameter(paramName, paramVal)) { + qos_hist = paramVal.as_int() == 1 ? RMW_QOS_POLICY_HISTORY_KEEP_LAST + : RMW_QOS_POLICY_HISTORY_KEEP_ALL; + mPoseQos.history(qos_hist); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - RCLCPP_INFO(get_logger(), " * Sensors QoS History: %s", sl_tools::qos2str(qos_hist).c_str()); + RCLCPP_INFO(get_logger(), + " * Pose/Odometry QoS History: %s", + sl_tools::qos2str(qos_hist).c_str()); - // ------------------------------------------ + // ------------------------------------------ - paramName = "mapping.qos_depth"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_depth)); + paramName = "pos_tracking.qos_depth"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_depth)); - if (get_parameter(paramName, paramVal)) - { - qos_depth = paramVal.as_int(); - mMappingQos.keep_last(qos_depth); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } + if (get_parameter(paramName, paramVal)) { + qos_depth = paramVal.as_int(); + mPoseQos.keep_last(qos_depth); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - RCLCPP_INFO(get_logger(), " * Sensors QoS History depth: %d", qos_depth); + RCLCPP_INFO( + get_logger(), " * Pose/Odometry QoS History depth: %d", qos_depth); - // ------------------------------------------ + // ------------------------------------------ - paramName = "mapping.qos_reliability"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_reliability)); + paramName = "pos_tracking.qos_reliability"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_reliability)); - if (get_parameter(paramName, paramVal)) - { - qos_reliability = - paramVal.as_int() == 1 ? RMW_QOS_POLICY_RELIABILITY_RELIABLE : RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; - mMappingQos.reliability(qos_reliability); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } + if (get_parameter(paramName, paramVal)) { + qos_reliability = paramVal.as_int() == 1 + ? RMW_QOS_POLICY_RELIABILITY_RELIABLE + : RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; + mPoseQos.reliability(qos_reliability); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - RCLCPP_INFO(get_logger(), " * Sensors QoS Reliability: %s", sl_tools::qos2str(qos_reliability).c_str()); + RCLCPP_INFO(get_logger(), + " * Pose/Odometry QoS Reliability: %s", + sl_tools::qos2str(qos_reliability).c_str()); - // ------------------------------------------ + // ------------------------------------------ - paramName = "mapping.qos_durability"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_durability)); + paramName = "pos_tracking.qos_durability"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_durability)); - if (get_parameter(paramName, paramVal)) - { - qos_durability = - paramVal.as_int() == 1 ? RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL : RMW_QOS_POLICY_DURABILITY_VOLATILE; - mMappingQos.durability(qos_durability); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } + if (get_parameter(paramName, paramVal)) { + qos_durability = paramVal.as_int() == 1 + ? RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL + : RMW_QOS_POLICY_DURABILITY_VOLATILE; + mPoseQos.durability(qos_durability); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - RCLCPP_INFO(get_logger(), " * Sensors QoS Durability: %s", sl_tools::qos2str(qos_durability).c_str()); + RCLCPP_INFO(get_logger(), + " * Pose/Odometry QoS Durability: %s", + sl_tools::qos2str(qos_durability).c_str()); } -void ZedCamera::getPosTrackingParams() +void ZedCamera::getOdParams() { - rclcpp::Parameter paramVal; - std::string paramName; + rclcpp::Parameter paramVal; + std::string paramName; + + rmw_qos_history_policy_t qos_hist = RMW_QOS_POLICY_HISTORY_KEEP_LAST; + int qos_depth = 1; + rmw_qos_reliability_policy_t qos_reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE; + rmw_qos_durability_policy_t qos_durability = RMW_QOS_POLICY_DURABILITY_VOLATILE; + + RCLCPP_INFO(get_logger(), "*** OBJECT DETECTION parameters ***"); + if (mCamUserModel == sl::MODEL::ZED || mCamUserModel == sl::MODEL::ZED_M) { + RCLCPP_WARN(get_logger(), + "!!! OD parameters are not used with ZED and ZED Mini !!!"); + } + + getParam("object_detection.od_enabled", mObjDetEnabled, mObjDetEnabled); + RCLCPP_INFO_STREAM( + get_logger(), + " * Object Detection enabled: " << (mObjDetEnabled ? "TRUE" : "FALSE")); + getParam("object_detection.confidence_threshold", + mObjDetConfidence, + mObjDetConfidence, + " * OD min. confidence: "); + // getParam( "object_detection.object_tracking_enabled", mObjDetTracking, + // mObjDetTracking ); RCLCPP_INFO_STREAM(get_logger(), " * OD tracking: " << + // (mObjDetTracking?"TRUE":"FALSE") ); + int model = 0; + getParam("object_detection.model", model, model); + mObjDetModel = static_cast(model); + RCLCPP_INFO_STREAM(get_logger(), + " * Object Detection model: " << model << " - " + << mObjDetModel); + getParam( + "object_detection.mc_people", mObjDetPeopleEnable, mObjDetPeopleEnable); + RCLCPP_INFO_STREAM( + get_logger(), + " * MultiClassBox people: " << (mObjDetPeopleEnable ? "TRUE" : "FALSE")); + getParam("object_detection.mc_vehicle", + mObjDetVehiclesEnable, + mObjDetVehiclesEnable); + RCLCPP_INFO_STREAM(get_logger(), + " * MultiClassBox vehicles: " + << (mObjDetVehiclesEnable ? "TRUE" : "FALSE")); + getParam("object_detection.mc_bag", mObjDetBagsEnable, mObjDetBagsEnable); + RCLCPP_INFO_STREAM( + get_logger(), + " * MultiClassBox bags: " << (mObjDetBagsEnable ? "TRUE" : "FALSE")); + getParam( + "object_detection.mc_animal", mObjDetAnimalsEnable, mObjDetAnimalsEnable); + RCLCPP_INFO_STREAM( + get_logger(), + " * MultiClassBox animals: " << (mObjDetAnimalsEnable ? "TRUE" : "FALSE")); + getParam("object_detection.mc_electronics", + mObjDetElectronicsEnable, + mObjDetElectronicsEnable); + RCLCPP_INFO_STREAM(get_logger(), + " * MultiClassBox electronics: " + << (mObjDetElectronicsEnable ? "TRUE" : "FALSE")); + getParam("object_detection.mc_fruit_vegetable", + mObjDetFruitsEnable, + mObjDetFruitsEnable); + RCLCPP_INFO_STREAM(get_logger(), + " * MultiClassBox fruits and vegetables: " + << (mObjDetFruitsEnable ? "TRUE" : "FALSE")); + getParam("object_detection.body_fitting", mBodyFitting, mBodyFitting); + RCLCPP_INFO_STREAM( + get_logger(), " * Skeleton fitting: " << (mBodyFitting ? "TRUE" : "FALSE")); + // ------------------------------------------ - rmw_qos_history_policy_t qos_hist = RMW_QOS_POLICY_HISTORY_KEEP_LAST; - int qos_depth = 1; - rmw_qos_reliability_policy_t qos_reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE; - rmw_qos_durability_policy_t qos_durability = RMW_QOS_POLICY_DURABILITY_VOLATILE; + paramName = "object_detection.qos_history"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_hist)); - RCLCPP_INFO(get_logger(), "*** POSITIONAL TRACKING parameters ***"); + if (get_parameter(paramName, paramVal)) { + qos_hist = paramVal.as_int() == 1 ? RMW_QOS_POLICY_HISTORY_KEEP_LAST + : RMW_QOS_POLICY_HISTORY_KEEP_ALL; + mObjDetQos.history(qos_hist); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - getParam("pos_tracking.pos_tracking_enabled", mPosTrackingEnabled, mPosTrackingEnabled); - RCLCPP_INFO_STREAM(get_logger(), " * Positional tracking enabled: " << (mPosTrackingEnabled ? "TRUE" : "FALSE")); + RCLCPP_INFO(get_logger(), + " * Obj. Det. QoS History: %s", + sl_tools::qos2str(qos_hist).c_str()); - getParam("pos_tracking.base_frame", mBaseFrameId, mBaseFrameId, " * Base frame id: "); - getParam("pos_tracking.map_frame", mMapFrameId, mMapFrameId, " * Map frame id: "); - getParam("pos_tracking.odometry_frame", mOdomFrameId, mOdomFrameId, " * Odometry frame id: "); + // ------------------------------------------ - getParam("pos_tracking.publish_tf", mPublishTF, mPublishTF); - RCLCPP_INFO_STREAM(get_logger(), " * Broadcast Odometry TF: " << (mPublishTF ? "TRUE" : "FALSE")); - if (mPublishTF) - { - getParam("pos_tracking.publish_map_tf", mPublishMapTF, mPublishMapTF); - RCLCPP_INFO_STREAM(get_logger(), " * Broadcast Pose TF: " << (mPublishMapTF ? "TRUE" : "FALSE")); - getParam("pos_tracking.publish_imu_tf", mPublishImuTF, mPublishImuTF); - RCLCPP_INFO_STREAM(get_logger(), - " * Broadcast Static IMU TF [not for ZED]: " << (mPublishImuTF ? "TRUE" : "FALSE")); - } + paramName = "object_detection.qos_depth"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_depth)); - getParam("pos_tracking.path_pub_rate", mPathPubRate, mPathPubRate, " * [DYN] Path publishing rate: "); - getParam("pos_tracking.path_max_count", mPathMaxCount, mPathMaxCount); - if (mPathMaxCount < 2 && mPathMaxCount != -1) - { - mPathMaxCount = 2; - } - RCLCPP_INFO_STREAM(get_logger(), " * Path history lenght: " << mPathMaxCount); + if (get_parameter(paramName, paramVal)) { + qos_depth = paramVal.as_int(); + mObjDetQos.keep_last(qos_depth); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); + } - paramName = "pos_tracking.initial_base_pose"; - declare_parameter(paramName, rclcpp::ParameterValue(mInitialBasePose)); - if (!get_parameter(paramName, mInitialBasePose)) - { - RCLCPP_WARN_STREAM(get_logger(), - "The parameter '" << paramName << "' is not available or is not valid, using the default value"); - mInitialBasePose = std::vector(6, 0.0); - } - if (mInitialBasePose.size() < 6) - { - RCLCPP_WARN_STREAM(get_logger(), "The parameter '" << paramName << "' must be a vector of 6 values of double type"); - mInitialBasePose = std::vector(6, 0.0); - } - RCLCPP_INFO(get_logger(), " * Initial pose: [%g,%g,%g,%g,%g,%g,]", mInitialBasePose[0], mInitialBasePose[1], - mInitialBasePose[2], mInitialBasePose[3], mInitialBasePose[4], mInitialBasePose[5]); - - getParam("pos_tracking.area_memory", mAreaMemory, mAreaMemory); - RCLCPP_INFO_STREAM(get_logger(), " * Area Memory: " << (mAreaMemory ? "TRUE" : "FALSE")); - getParam("pos_tracking.area_memory_db_path", mAreaMemoryDbPath, mAreaMemoryDbPath, " * Area Memory DB: "); - getParam("pos_tracking.imu_fusion", mImuFusion, mImuFusion); - RCLCPP_INFO_STREAM(get_logger(), " * IMU Fusion [not for ZED]: " << (mImuFusion ? "TRUE" : "FALSE")); - getParam("pos_tracking.floor_alignment", mFloorAlignment, mFloorAlignment); - RCLCPP_INFO_STREAM(get_logger(), " * Floor Alignment: " << (mFloorAlignment ? "TRUE" : "FALSE")); - getParam("pos_tracking.init_odom_with_first_valid_pose", mInitOdomWithPose, mInitOdomWithPose); - RCLCPP_INFO_STREAM(get_logger(), - " * Init Odometry with first valid pose data: " << (mInitOdomWithPose ? "TRUE" : "FALSE")); - getParam("pos_tracking.two_d_mode", mTwoDMode, mTwoDMode); - RCLCPP_INFO_STREAM(get_logger(), " * 2D mode: " << (mTwoDMode ? "TRUE" : "FALSE")); - if (mTwoDMode) - { - getParam("pos_tracking.fixed_z_value", mFixedZValue, mFixedZValue, " * Fixed Z value: "); - } - - // ------------------------------------------ - - paramName = "pos_tracking.qos_history"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_hist)); - - if (get_parameter(paramName, paramVal)) - { - qos_hist = paramVal.as_int() == 1 ? RMW_QOS_POLICY_HISTORY_KEEP_LAST : RMW_QOS_POLICY_HISTORY_KEEP_ALL; - mPoseQos.history(qos_hist); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } - - RCLCPP_INFO(get_logger(), " * Pose/Odometry QoS History: %s", sl_tools::qos2str(qos_hist).c_str()); - - // ------------------------------------------ - - paramName = "pos_tracking.qos_depth"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_depth)); - - if (get_parameter(paramName, paramVal)) - { - qos_depth = paramVal.as_int(); - mPoseQos.keep_last(qos_depth); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } - - RCLCPP_INFO(get_logger(), " * Pose/Odometry QoS History depth: %d", qos_depth); - - // ------------------------------------------ - - paramName = "pos_tracking.qos_reliability"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_reliability)); - - if (get_parameter(paramName, paramVal)) - { - qos_reliability = - paramVal.as_int() == 1 ? RMW_QOS_POLICY_RELIABILITY_RELIABLE : RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; - mPoseQos.reliability(qos_reliability); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } - - RCLCPP_INFO(get_logger(), " * Pose/Odometry QoS Reliability: %s", sl_tools::qos2str(qos_reliability).c_str()); - - // ------------------------------------------ - - paramName = "pos_tracking.qos_durability"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_durability)); - - if (get_parameter(paramName, paramVal)) - { - qos_durability = - paramVal.as_int() == 1 ? RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL : RMW_QOS_POLICY_DURABILITY_VOLATILE; - mPoseQos.durability(qos_durability); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } - - RCLCPP_INFO(get_logger(), " * Pose/Odometry QoS Durability: %s", sl_tools::qos2str(qos_durability).c_str()); -} - -void ZedCamera::getOdParams() -{ - rclcpp::Parameter paramVal; - std::string paramName; - - rmw_qos_history_policy_t qos_hist = RMW_QOS_POLICY_HISTORY_KEEP_LAST; - int qos_depth = 1; - rmw_qos_reliability_policy_t qos_reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE; - rmw_qos_durability_policy_t qos_durability = RMW_QOS_POLICY_DURABILITY_VOLATILE; - - RCLCPP_INFO(get_logger(), "*** OBJECT DETECTION parameters ***"); - if (mCamUserModel == sl::MODEL::ZED || mCamUserModel == sl::MODEL::ZED_M) - { - RCLCPP_WARN(get_logger(), "!!! OD parameters are not used with ZED and ZED Mini !!!"); - } - - getParam("object_detection.od_enabled", mObjDetEnabled, mObjDetEnabled); - RCLCPP_INFO_STREAM(get_logger(), " * Object Detection enabled: " << (mObjDetEnabled ? "TRUE" : "FALSE")); - getParam("object_detection.confidence_threshold", mObjDetConfidence, mObjDetConfidence, " * OD min. confidence: "); - // getParam( "object_detection.object_tracking_enabled", mObjDetTracking, mObjDetTracking ); - // RCLCPP_INFO_STREAM(get_logger(), " * OD tracking: " << (mObjDetTracking?"TRUE":"FALSE") ); - int model = 0; - getParam("object_detection.model", model, model); - mObjDetModel = static_cast(model); - RCLCPP_INFO_STREAM(get_logger(), " * Object Detection model: " << model << " - " << mObjDetModel); - getParam("object_detection.mc_people", mObjDetPeopleEnable, mObjDetPeopleEnable); - RCLCPP_INFO_STREAM(get_logger(), " * MultiClassBox people: " << (mObjDetPeopleEnable ? "TRUE" : "FALSE")); - getParam("object_detection.mc_vehicle", mObjDetVehiclesEnable, mObjDetVehiclesEnable); - RCLCPP_INFO_STREAM(get_logger(), " * MultiClassBox vehicles: " << (mObjDetVehiclesEnable ? "TRUE" : "FALSE")); - getParam("object_detection.mc_bag", mObjDetBagsEnable, mObjDetBagsEnable); - RCLCPP_INFO_STREAM(get_logger(), " * MultiClassBox bags: " << (mObjDetBagsEnable ? "TRUE" : "FALSE")); - getParam("object_detection.mc_animal", mObjDetAnimalsEnable, mObjDetAnimalsEnable); - RCLCPP_INFO_STREAM(get_logger(), " * MultiClassBox animals: " << (mObjDetAnimalsEnable ? "TRUE" : "FALSE")); - getParam("object_detection.mc_electronics", mObjDetElectronicsEnable, mObjDetElectronicsEnable); - RCLCPP_INFO_STREAM(get_logger(), " * MultiClassBox electronics: " << (mObjDetElectronicsEnable ? "TRUE" : "FALSE")); - getParam("object_detection.mc_fruit_vegetable", mObjDetFruitsEnable, mObjDetFruitsEnable); - RCLCPP_INFO_STREAM(get_logger(), - " * MultiClassBox fruits and vegetables: " << (mObjDetFruitsEnable ? "TRUE" : "FALSE")); - getParam("object_detection.body_fitting", mBodyFitting, mBodyFitting); - RCLCPP_INFO_STREAM(get_logger(), " * Skeleton fitting: " << (mBodyFitting ? "TRUE" : "FALSE")); - // ------------------------------------------ - - paramName = "object_detection.qos_history"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_hist)); - - if (get_parameter(paramName, paramVal)) - { - qos_hist = paramVal.as_int() == 1 ? RMW_QOS_POLICY_HISTORY_KEEP_LAST : RMW_QOS_POLICY_HISTORY_KEEP_ALL; - mObjDetQos.history(qos_hist); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } - - RCLCPP_INFO(get_logger(), " * Obj. Det. QoS History: %s", sl_tools::qos2str(qos_hist).c_str()); - - // ------------------------------------------ - - paramName = "object_detection.qos_depth"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_depth)); - - if (get_parameter(paramName, paramVal)) - { - qos_depth = paramVal.as_int(); - mObjDetQos.keep_last(qos_depth); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } - - RCLCPP_INFO(get_logger(), " * Obj. Det. QoS History depth: %d", qos_depth); - - // ------------------------------------------ - - paramName = "object_detection.qos_reliability"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_reliability)); - - if (get_parameter(paramName, paramVal)) - { - qos_reliability = - paramVal.as_int() == 1 ? RMW_QOS_POLICY_RELIABILITY_RELIABLE : RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; - mObjDetQos.reliability(qos_reliability); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } - - RCLCPP_INFO(get_logger(), " * Obj. Det. QoS Reliability: %s", sl_tools::qos2str(qos_reliability).c_str()); - - // ------------------------------------------ - - paramName = "object_detection.qos_durability"; - declare_parameter(paramName, rclcpp::ParameterValue(qos_durability)); - - if (get_parameter(paramName, paramVal)) - { - qos_durability = - paramVal.as_int() == 1 ? RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL : RMW_QOS_POLICY_DURABILITY_VOLATILE; - mObjDetQos.durability(qos_durability); - } - else - { - RCLCPP_WARN(get_logger(), "The parameter '%s' is not available, using the default value", paramName.c_str()); - } - - RCLCPP_INFO(get_logger(), " * Obj. Det. QoS Durability: %s", sl_tools::qos2str(qos_durability).c_str()); -} - -rcl_interfaces::msg::SetParametersResult ZedCamera::callback_paramChange(std::vector parameters) -{ - rcl_interfaces::msg::SetParametersResult result; - result.successful = false; - - for (const auto& param : parameters) - { - if (param.get_name() == "general.pub_frame_rate") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_DOUBLE; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - double val = param.as_double(); - - if ((val <= 0.0) || (val > mCamGrabFrameRate)) - { - result.successful = false; - result.reason = param.get_name() + " must be positive and minor or equal to `grab_frame_rate`"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mPubFrameRate = val; - startVideoDepthTimer(mPubFrameRate); - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "video.brightness") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - int val = param.as_int(); - - if ((val < 0) || (val > 8)) - { - result.successful = false; - result.reason = param.get_name() + " must be a positive integer in the range [0,8]"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mCamBrightness = val; - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "video.contrast") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - int val = param.as_int(); - - if ((val < 0) || (val > 8)) - { - result.successful = false; - result.reason = param.get_name() + " must be a positive integer in the range [0,8]"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mCamContrast = val; - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "video.hue") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - int val = param.as_int(); - - if ((val < 0) || (val > 11)) - { - result.successful = false; - result.reason = param.get_name() + " must be a positive integer in the range [0,11]"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mCamHue = val; - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "video.sharpness") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - int val = param.as_int(); - - if ((val < 0) || (val > 8)) - { - result.successful = false; - result.reason = param.get_name() + " must be a positive integer in the range [0,8]"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mCamSharpness = val; - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "video.gamma") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - int val = param.as_int(); - - if ((val < 0) || (val > 8)) - { - result.successful = false; - result.reason = param.get_name() + " must be a positive integer in the range [0,8]"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mCamGamma = val; - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "video.auto_exposure_gain") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - bool val = param.as_bool(); - - if (val && !mCamAutoExpGain) - { - mTriggerAutoExpGain = true; - } - - mCamAutoExpGain = val; - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "video.exposure") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - int val = param.as_int(); - - if ((val < 0) || (val > 100)) - { - result.successful = false; - result.reason = param.get_name() + " must be a positive integer in the range [0,100]"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mCamExposure = val; - mCamAutoExpGain = false; - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "video.gain") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - int val = param.as_int(); - - if ((val < 0) || (val > 100)) - { - result.successful = false; - result.reason = param.get_name() + " must be a positive integer in the range [0,100]"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mCamGain = val; - mCamAutoExpGain = false; - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "video.auto_whitebalance") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - bool val = param.as_bool(); - - if (val && !mCamAutoWB) - { - mTriggerAutoWB = true; - } - - mCamAutoWB = val; - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "video.whitebalance_temperature") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - int val = param.as_int(); - - if ((val < 28) || (val > 65)) - { - result.successful = false; - result.reason = param.get_name() + " must be a positive integer in the range [28,65]"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mCamWBTemp = val * 100; - mCamAutoWB = false; - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "depth.point_cloud_freq") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_DOUBLE; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - double val = param.as_double(); - - if ((val <= 0.0) || (val > mCamGrabFrameRate)) - { - result.successful = false; - result.reason = param.get_name() + " must be positive and minor of `grab_frame_rate`"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mPubFrameRate = val; - startVideoDepthTimer(mPubFrameRate); - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "depth.depth_confidence") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - int val = param.as_int(); - - if ((val < 0) || (val > 100)) - { - result.successful = false; - result.reason = param.get_name() + " must be a positive integer in the range [0,100]"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mDepthConf = val; - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "depth.depth_texture_conf") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - int val = param.as_int(); - - if ((val < 0) || (val > 100)) - { - result.successful = false; - result.reason = param.get_name() + " must be a positive integer in the range [0,100]"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mDepthTextConf = val; - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "mapping.fused_pointcloud_freq") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_DOUBLE; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - double val = param.as_double(); - - if ((val <= 0.0) || (val > mPcPubRate)) - { - result.successful = false; - result.reason = param.get_name() + " must be positive and minor of `point_cloud_freq`"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mFusedPcPubRate = val; - startFusedPcTimer(mFusedPcPubRate); - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "object_detection.confidence_threshold") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_DOUBLE; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - double val = param.as_double(); - - if ((val < 0.0) || (val > 100.0)) - { - result.successful = false; - result.reason = param.get_name() + " must be positive double value in the range [0.0,100.0]"; - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mObjDetConfidence = val; - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " << val); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "object_detection.mc_people") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } + RCLCPP_INFO(get_logger(), " * Obj. Det. QoS History depth: %d", qos_depth); - mObjDetPeopleEnable = param.as_bool(); - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " - << (mObjDetPeopleEnable ? "TRUE" : "FALSE")); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "object_detection.mc_vehicle") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - - mObjDetVehiclesEnable = param.as_bool(); - - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " - << (mObjDetVehiclesEnable ? "TRUE" : "FALSE")); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "object_detection.mc_bag") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } + // ------------------------------------------ - mObjDetBagsEnable = param.as_bool(); + paramName = "object_detection.qos_reliability"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_reliability)); - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " - << (mObjDetBagsEnable ? "TRUE" : "FALSE")); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; + if (get_parameter(paramName, paramVal)) { + qos_reliability = paramVal.as_int() == 1 + ? RMW_QOS_POLICY_RELIABILITY_RELIABLE + : RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; + mObjDetQos.reliability(qos_reliability); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); } - else if (param.get_name() == "object_detection.mc_animal") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - mObjDetAnimalsEnable = param.as_bool(); + RCLCPP_INFO(get_logger(), + " * Obj. Det. QoS Reliability: %s", + sl_tools::qos2str(qos_reliability).c_str()); - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " - << (mObjDetAnimalsEnable ? "TRUE" : "FALSE")); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else if (param.get_name() == "object_detection.mc_electronics") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } + // ------------------------------------------ - mObjDetElectronicsEnable = param.as_bool(); + paramName = "object_detection.qos_durability"; + declare_parameter(paramName, rclcpp::ParameterValue(qos_durability)); - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " - << (mObjDetElectronicsEnable ? "TRUE" : "FALSE")); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; + if (get_parameter(paramName, paramVal)) { + qos_durability = paramVal.as_int() == 1 + ? RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL + : RMW_QOS_POLICY_DURABILITY_VOLATILE; + mObjDetQos.durability(qos_durability); + } else { + RCLCPP_WARN(get_logger(), + "The parameter '%s' is not available, using the default value", + paramName.c_str()); } - else if (param.get_name() == "object_detection.mc_fruit_vegetable") - { - rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; - if (param.get_type() != correctType) - { - result.successful = false; - result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; - } - mObjDetFruitsEnable = param.as_bool(); + RCLCPP_INFO(get_logger(), + " * Obj. Det. QoS Durability: %s", + sl_tools::qos2str(qos_durability).c_str()); +} - RCLCPP_INFO_STREAM(get_logger(), "Parameter '" << param.get_name() << "' correctly set to " - << (mObjDetFruitsEnable ? "TRUE" : "FALSE")); - result.successful = true; - result.reason = param.get_name() + " correctly set."; - return result; - } - else - { - result.reason = param.get_name() + " is not a dynamic parameter"; +rcl_interfaces::msg::SetParametersResult +ZedCamera::callback_paramChange(std::vector parameters) +{ + rcl_interfaces::msg::SetParametersResult result; + result.successful = false; + + for (const auto& param : parameters) { + if (param.get_name() == "general.pub_frame_rate") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_DOUBLE; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + double val = param.as_double(); + + if ((val <= 0.0) || (val > mCamGrabFrameRate)) { + result.successful = false; + result.reason = param.get_name() + " must be positive and minor or equal to `grab_frame_rate`"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mPubFrameRate = val; + startVideoDepthTimer(mPubFrameRate); + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "video.brightness") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + int val = param.as_int(); + + if ((val < 0) || (val > 8)) { + result.successful = false; + result.reason = param.get_name() + " must be a positive integer in the range [0,8]"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mCamBrightness = val; + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "video.contrast") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + int val = param.as_int(); + + if ((val < 0) || (val > 8)) { + result.successful = false; + result.reason = param.get_name() + " must be a positive integer in the range [0,8]"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mCamContrast = val; + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "video.hue") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + int val = param.as_int(); + + if ((val < 0) || (val > 11)) { + result.successful = false; + result.reason = param.get_name() + " must be a positive integer in the range [0,11]"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mCamHue = val; + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "video.sharpness") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + int val = param.as_int(); + + if ((val < 0) || (val > 8)) { + result.successful = false; + result.reason = param.get_name() + " must be a positive integer in the range [0,8]"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mCamSharpness = val; + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "video.gamma") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + int val = param.as_int(); + + if ((val < 0) || (val > 8)) { + result.successful = false; + result.reason = param.get_name() + " must be a positive integer in the range [0,8]"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mCamGamma = val; + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "video.auto_exposure_gain") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + bool val = param.as_bool(); + + if (val && !mCamAutoExpGain) { + mTriggerAutoExpGain = true; + } + + mCamAutoExpGain = val; + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "video.exposure") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + int val = param.as_int(); + + if ((val < 0) || (val > 100)) { + result.successful = false; + result.reason = param.get_name() + " must be a positive integer in the range [0,100]"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mCamExposure = val; + mCamAutoExpGain = false; + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "video.gain") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + int val = param.as_int(); + + if ((val < 0) || (val > 100)) { + result.successful = false; + result.reason = param.get_name() + " must be a positive integer in the range [0,100]"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mCamGain = val; + mCamAutoExpGain = false; + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "video.auto_whitebalance") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + bool val = param.as_bool(); + + if (val && !mCamAutoWB) { + mTriggerAutoWB = true; + } + + mCamAutoWB = val; + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "video.whitebalance_temperature") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + int val = param.as_int(); + + if ((val < 28) || (val > 65)) { + result.successful = false; + result.reason = param.get_name() + " must be a positive integer in the range [28,65]"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mCamWBTemp = val * 100; + mCamAutoWB = false; + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "depth.point_cloud_freq") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_DOUBLE; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + double val = param.as_double(); + + if ((val <= 0.0) || (val > mCamGrabFrameRate)) { + result.successful = false; + result.reason = param.get_name() + " must be positive and minor of `grab_frame_rate`"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mPubFrameRate = val; + startVideoDepthTimer(mPubFrameRate); + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "depth.depth_confidence") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + int val = param.as_int(); + + if ((val < 0) || (val > 100)) { + result.successful = false; + result.reason = param.get_name() + " must be a positive integer in the range [0,100]"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mDepthConf = val; + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "depth.depth_texture_conf") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_INTEGER; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + int val = param.as_int(); + + if ((val < 0) || (val > 100)) { + result.successful = false; + result.reason = param.get_name() + " must be a positive integer in the range [0,100]"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mDepthTextConf = val; + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "mapping.fused_pointcloud_freq") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_DOUBLE; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + double val = param.as_double(); + + if ((val <= 0.0) || (val > mPcPubRate)) { + result.successful = false; + result.reason = param.get_name() + " must be positive and minor of `point_cloud_freq`"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mFusedPcPubRate = val; + startFusedPcTimer(mFusedPcPubRate); + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "object_detection.confidence_threshold") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_DOUBLE; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + double val = param.as_double(); + + if ((val < 0.0) || (val > 100.0)) { + result.successful = false; + result.reason = param.get_name() + " must be positive double value in the range [0.0,100.0]"; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mObjDetConfidence = val; + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" << param.get_name() + << "' correctly set to " << val); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "object_detection.mc_people") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mObjDetPeopleEnable = param.as_bool(); + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" + << param.get_name() << "' correctly set to " + << (mObjDetPeopleEnable ? "TRUE" : "FALSE")); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "object_detection.mc_vehicle") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mObjDetVehiclesEnable = param.as_bool(); + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" + << param.get_name() << "' correctly set to " + << (mObjDetVehiclesEnable ? "TRUE" : "FALSE")); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "object_detection.mc_bag") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mObjDetBagsEnable = param.as_bool(); + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" + << param.get_name() << "' correctly set to " + << (mObjDetBagsEnable ? "TRUE" : "FALSE")); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "object_detection.mc_animal") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mObjDetAnimalsEnable = param.as_bool(); + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" + << param.get_name() << "' correctly set to " + << (mObjDetAnimalsEnable ? "TRUE" : "FALSE")); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "object_detection.mc_electronics") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mObjDetElectronicsEnable = param.as_bool(); + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" + << param.get_name() << "' correctly set to " + << (mObjDetElectronicsEnable ? "TRUE" : "FALSE")); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else if (param.get_name() == "object_detection.mc_fruit_vegetable") { + rclcpp::ParameterType correctType = rclcpp::ParameterType::PARAMETER_BOOL; + if (param.get_type() != correctType) { + result.successful = false; + result.reason = param.get_name() + " must be a " + rclcpp::to_string(correctType); + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; + } + + mObjDetFruitsEnable = param.as_bool(); + + RCLCPP_INFO_STREAM(get_logger(), + "Parameter '" + << param.get_name() << "' correctly set to " + << (mObjDetFruitsEnable ? "TRUE" : "FALSE")); + result.successful = true; + result.reason = param.get_name() + " correctly set."; + return result; + } else { + result.reason = param.get_name() + " is not a dynamic parameter"; + } } - } - RCLCPP_WARN_STREAM(get_logger(), result.reason); - return result; + RCLCPP_WARN_STREAM(get_logger(), result.reason); + return result; } void ZedCamera::setTFCoordFrameNames() { - // ----> Coordinate frames - mCameraFrameId = mCameraName + "_camera_center"; - mLeftCamFrameId = mCameraName + "_left_camera_frame"; - mLeftCamOptFrameId = mCameraName + "_left_camera_optical_frame"; - mRightCamFrameId = mCameraName + "_right_camera_frame"; - mRightCamOptFrameId = mCameraName + "_right_camera_optical_frame"; - - mImuFrameId = mCameraName + "_imu_link"; - mBaroFrameId = mCameraFrameId; // mCameraName + "_baro_link"; // TODO fix when XACRO is available - mMagFrameId = mImuFrameId; // mCameraName + "_mag_link"; // TODO fix when XACRO is available - mTempLeftFrameId = mLeftCamFrameId; // mCameraName + "_temp_left_link"; // TODO fix when XACRO is available - mTempRightFrameId = mRightCamFrameId; // mCameraName + "_temp_right_link"; // TODO fix when XACRO is available - - mDepthFrameId = mLeftCamFrameId; - mDepthOptFrameId = mLeftCamOptFrameId; - mPointCloudFrameId = mDepthFrameId; - - // Note: Depth image frame id must match color image frame id - mCloudFrameId = mDepthOptFrameId; - mRgbFrameId = mDepthFrameId; - mRgbOptFrameId = mCloudFrameId; - mDisparityFrameId = mDepthFrameId; - mDisparityOptFrameId = mDepthOptFrameId; - mConfidenceFrameId = mDepthFrameId; - mConfidenceOptFrameId = mDepthOptFrameId; - - // Print TF frames - RCLCPP_INFO_STREAM(get_logger(), "*** TF FRAMES ***"); - RCLCPP_INFO_STREAM(get_logger(), " * Map\t\t\t-> " << mMapFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Odometry\t\t\t-> " << mOdomFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Base\t\t\t-> " << mBaseFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Camera\t\t\t-> " << mCameraFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Left\t\t\t-> " << mLeftCamFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Left Optical\t\t-> " << mLeftCamOptFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * RGB\t\t\t-> " << mRgbFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * RGB Optical\t\t-> " << mRgbFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Right\t\t\t-> " << mRightCamFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Right Optical\t\t-> " << mRightCamOptFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Depth\t\t\t-> " << mDepthFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Depth Optical\t\t-> " << mDepthOptFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Point Cloud\t\t-> " << mCloudFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Disparity\t\t-> " << mDisparityFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Disparity Optical\t-> " << mDisparityOptFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Confidence\t\t-> " << mConfidenceFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Confidence Optical\t-> " << mConfidenceOptFrameId); - if (mCamRealModel != sl::MODEL::ZED) - { - RCLCPP_INFO_STREAM(get_logger(), " * IMU\t\t\t-> " << mImuFrameId); - - if (sl_tools::isZED2OrZED2i(mCamUserModel)) - { - RCLCPP_INFO_STREAM(get_logger(), " * Barometer\t\t-> " << mBaroFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Magnetometer\t\t-> " << mMagFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Left Temperature\t\t-> " << mTempLeftFrameId); - RCLCPP_INFO_STREAM(get_logger(), " * Right Temperature\t-> " << mTempRightFrameId); + // ----> Coordinate frames + mCameraFrameId = mCameraName + "_camera_center"; + mLeftCamFrameId = mCameraName + "_left_camera_frame"; + mLeftCamOptFrameId = mCameraName + "_left_camera_optical_frame"; + mRightCamFrameId = mCameraName + "_right_camera_frame"; + mRightCamOptFrameId = mCameraName + "_right_camera_optical_frame"; + + mImuFrameId = mCameraName + "_imu_link"; + mBaroFrameId = mCameraFrameId; // mCameraName + "_baro_link"; // TODO fix + // when XACRO is available + mMagFrameId = mImuFrameId; // mCameraName + "_mag_link"; // TODO fix when + // XACRO is available + mTempLeftFrameId = mLeftCamFrameId; // mCameraName + "_temp_left_link"; // + // TODO fix when XACRO is available + mTempRightFrameId = mRightCamFrameId; // mCameraName + "_temp_right_link"; // + // TODO fix when XACRO is available + + mDepthFrameId = mLeftCamFrameId; + mDepthOptFrameId = mLeftCamOptFrameId; + mPointCloudFrameId = mDepthFrameId; + + // Note: Depth image frame id must match color image frame id + mCloudFrameId = mDepthOptFrameId; + mRgbFrameId = mDepthFrameId; + mRgbOptFrameId = mCloudFrameId; + mDisparityFrameId = mDepthFrameId; + mDisparityOptFrameId = mDepthOptFrameId; + mConfidenceFrameId = mDepthFrameId; + mConfidenceOptFrameId = mDepthOptFrameId; + + // Print TF frames + RCLCPP_INFO_STREAM(get_logger(), "*** TF FRAMES ***"); + RCLCPP_INFO_STREAM(get_logger(), " * Map\t\t\t-> " << mMapFrameId); + RCLCPP_INFO_STREAM(get_logger(), " * Odometry\t\t\t-> " << mOdomFrameId); + RCLCPP_INFO_STREAM(get_logger(), " * Base\t\t\t-> " << mBaseFrameId); + RCLCPP_INFO_STREAM(get_logger(), " * Camera\t\t\t-> " << mCameraFrameId); + RCLCPP_INFO_STREAM(get_logger(), " * Left\t\t\t-> " << mLeftCamFrameId); + RCLCPP_INFO_STREAM(get_logger(), + " * Left Optical\t\t-> " << mLeftCamOptFrameId); + RCLCPP_INFO_STREAM(get_logger(), " * RGB\t\t\t-> " << mRgbFrameId); + RCLCPP_INFO_STREAM(get_logger(), " * RGB Optical\t\t-> " << mRgbFrameId); + RCLCPP_INFO_STREAM(get_logger(), " * Right\t\t\t-> " << mRightCamFrameId); + RCLCPP_INFO_STREAM(get_logger(), + " * Right Optical\t\t-> " << mRightCamOptFrameId); + RCLCPP_INFO_STREAM(get_logger(), " * Depth\t\t\t-> " << mDepthFrameId); + RCLCPP_INFO_STREAM(get_logger(), + " * Depth Optical\t\t-> " << mDepthOptFrameId); + RCLCPP_INFO_STREAM(get_logger(), " * Point Cloud\t\t-> " << mCloudFrameId); + RCLCPP_INFO_STREAM(get_logger(), " * Disparity\t\t-> " << mDisparityFrameId); + RCLCPP_INFO_STREAM(get_logger(), + " * Disparity Optical\t-> " << mDisparityOptFrameId); + RCLCPP_INFO_STREAM(get_logger(), + " * Confidence\t\t-> " << mConfidenceFrameId); + RCLCPP_INFO_STREAM(get_logger(), + " * Confidence Optical\t-> " << mConfidenceOptFrameId); + if (mCamRealModel != sl::MODEL::ZED) { + RCLCPP_INFO_STREAM(get_logger(), " * IMU\t\t\t-> " << mImuFrameId); + + if (sl_tools::isZED2OrZED2i(mCamUserModel)) { + RCLCPP_INFO_STREAM(get_logger(), " * Barometer\t\t-> " << mBaroFrameId); + RCLCPP_INFO_STREAM(get_logger(), " * Magnetometer\t\t-> " << mMagFrameId); + RCLCPP_INFO_STREAM(get_logger(), + " * Left Temperature\t\t-> " << mTempLeftFrameId); + RCLCPP_INFO_STREAM(get_logger(), + " * Right Temperature\t-> " << mTempRightFrameId); + } } - } - // <---- Coordinate frames + // <---- Coordinate frames } -void ZedCamera::fillCamInfo(sl::Camera& zed, std::shared_ptr leftCamInfoMsg, - std::shared_ptr rightCamInfoMsg, std::string leftFrameId, - std::string rightFrameId, bool rawParam /*= false*/) +void ZedCamera::fillCamInfo( + sl::Camera& zed, + std::shared_ptr leftCamInfoMsg, + std::shared_ptr rightCamInfoMsg, + std::string leftFrameId, + std::string rightFrameId, + bool rawParam /*= false*/) { - sl::CalibrationParameters zedParam; + sl::CalibrationParameters zedParam; #if ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION < 1 - if (rawParam) - { - zedParam = zed.getCameraInformation(mMatResolVideo).calibration_parameters_raw; // ok - } - else - { - zedParam = zed.getCameraInformation(mMatResolVideo).calibration_parameters; // ok - } + if (rawParam) { + zedParam = zed.getCameraInformation(mMatResolVideo).calibration_parameters_raw; // ok + } else { + zedParam = zed.getCameraInformation(mMatResolVideo).calibration_parameters; // ok + } #else - if (rawParam) - { - zedParam = zed.getCameraInformation(mMatResolVideo).camera_configuration.calibration_parameters_raw; - } - else - { - zedParam = zed.getCameraInformation(mMatResolVideo).camera_configuration.calibration_parameters; - } + if (rawParam) { + zedParam = zed.getCameraInformation(mMatResolVideo) + .camera_configuration.calibration_parameters_raw; + } else { + zedParam = zed.getCameraInformation(mMatResolVideo) + .camera_configuration.calibration_parameters; + } #endif - float baseline = zedParam.getCameraBaseline(); - leftCamInfoMsg->distortion_model = sensor_msgs::distortion_models::PLUMB_BOB; - rightCamInfoMsg->distortion_model = sensor_msgs::distortion_models::PLUMB_BOB; - leftCamInfoMsg->d.resize(5); - rightCamInfoMsg->d.resize(5); - leftCamInfoMsg->d[0] = zedParam.left_cam.disto[0]; // k1 - leftCamInfoMsg->d[1] = zedParam.left_cam.disto[1]; // k2 - leftCamInfoMsg->d[2] = zedParam.left_cam.disto[4]; // k3 - leftCamInfoMsg->d[3] = zedParam.left_cam.disto[2]; // p1 - leftCamInfoMsg->d[4] = zedParam.left_cam.disto[3]; // p2 - rightCamInfoMsg->d[0] = zedParam.right_cam.disto[0]; // k1 - rightCamInfoMsg->d[1] = zedParam.right_cam.disto[1]; // k2 - rightCamInfoMsg->d[2] = zedParam.right_cam.disto[4]; // k3 - rightCamInfoMsg->d[3] = zedParam.right_cam.disto[2]; // p1 - rightCamInfoMsg->d[4] = zedParam.right_cam.disto[3]; // p2 - leftCamInfoMsg->k.fill(0.0); - rightCamInfoMsg->k.fill(0.0); - leftCamInfoMsg->k[0] = static_cast(zedParam.left_cam.fx); - leftCamInfoMsg->k[2] = static_cast(zedParam.left_cam.cx); - leftCamInfoMsg->k[4] = static_cast(zedParam.left_cam.fy); - leftCamInfoMsg->k[5] = static_cast(zedParam.left_cam.cy); - leftCamInfoMsg->k[8] = 1.0; - rightCamInfoMsg->k[0] = static_cast(zedParam.right_cam.fx); - rightCamInfoMsg->k[2] = static_cast(zedParam.right_cam.cx); - rightCamInfoMsg->k[4] = static_cast(zedParam.right_cam.fy); - rightCamInfoMsg->k[5] = static_cast(zedParam.right_cam.cy); - rightCamInfoMsg->k[8] = 1.0; - leftCamInfoMsg->r.fill(0.0); - rightCamInfoMsg->r.fill(0.0); - - for (size_t i = 0; i < 3; i++) - { - // identity - rightCamInfoMsg->r[i + i * 3] = 1; - leftCamInfoMsg->r[i + i * 3] = 1; - } + float baseline = zedParam.getCameraBaseline(); + leftCamInfoMsg->distortion_model = sensor_msgs::distortion_models::PLUMB_BOB; + rightCamInfoMsg->distortion_model = sensor_msgs::distortion_models::PLUMB_BOB; + leftCamInfoMsg->d.resize(5); + rightCamInfoMsg->d.resize(5); + leftCamInfoMsg->d[0] = zedParam.left_cam.disto[0]; // k1 + leftCamInfoMsg->d[1] = zedParam.left_cam.disto[1]; // k2 + leftCamInfoMsg->d[2] = zedParam.left_cam.disto[4]; // k3 + leftCamInfoMsg->d[3] = zedParam.left_cam.disto[2]; // p1 + leftCamInfoMsg->d[4] = zedParam.left_cam.disto[3]; // p2 + rightCamInfoMsg->d[0] = zedParam.right_cam.disto[0]; // k1 + rightCamInfoMsg->d[1] = zedParam.right_cam.disto[1]; // k2 + rightCamInfoMsg->d[2] = zedParam.right_cam.disto[4]; // k3 + rightCamInfoMsg->d[3] = zedParam.right_cam.disto[2]; // p1 + rightCamInfoMsg->d[4] = zedParam.right_cam.disto[3]; // p2 + leftCamInfoMsg->k.fill(0.0); + rightCamInfoMsg->k.fill(0.0); + leftCamInfoMsg->k[0] = static_cast(zedParam.left_cam.fx); + leftCamInfoMsg->k[2] = static_cast(zedParam.left_cam.cx); + leftCamInfoMsg->k[4] = static_cast(zedParam.left_cam.fy); + leftCamInfoMsg->k[5] = static_cast(zedParam.left_cam.cy); + leftCamInfoMsg->k[8] = 1.0; + rightCamInfoMsg->k[0] = static_cast(zedParam.right_cam.fx); + rightCamInfoMsg->k[2] = static_cast(zedParam.right_cam.cx); + rightCamInfoMsg->k[4] = static_cast(zedParam.right_cam.fy); + rightCamInfoMsg->k[5] = static_cast(zedParam.right_cam.cy); + rightCamInfoMsg->k[8] = 1.0; + leftCamInfoMsg->r.fill(0.0); + rightCamInfoMsg->r.fill(0.0); + + for (size_t i = 0; i < 3; i++) { + // identity + rightCamInfoMsg->r[i + i * 3] = 1; + leftCamInfoMsg->r[i + i * 3] = 1; + } #if ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION < 1 - if (rawParam) - { - std::vector R_ = sl_tools::convertRodrigues(zedParam.R); - float* p = R_.data(); + if (rawParam) { + std::vector R_ = sl_tools::convertRodrigues(zedParam.R); + float* p = R_.data(); - for (int i = 0; i < 9; i++) - { - rightCamInfoMsg->r[i] = p[i]; + for (int i = 0; i < 9; i++) { + rightCamInfoMsg->r[i] = p[i]; + } } - } #else - if (rawParam) - { - if (mUseOldExtrinsic) - { // Camera frame (Z forward, Y down, X right) - - std::vector R_ = sl_tools::convertRodrigues(zedParam.R); - float* p = R_.data(); - - for (int i = 0; i < 9; i++) - { - rightCamInfoMsg->r[i] = p[i]; - } - } - else - { // ROS frame (X forward, Z up, Y left) - for (int i = 0; i < 9; i++) - { - rightCamInfoMsg->r[i] = zedParam.stereo_transform.getRotationMatrix().r[i]; - } + if (rawParam) { + if (mUseOldExtrinsic) { // Camera frame (Z forward, Y down, X right) + + std::vector R_ = sl_tools::convertRodrigues(zedParam.R); + float* p = R_.data(); + + for (int i = 0; i < 9; i++) { + rightCamInfoMsg->r[i] = p[i]; + } + } else { // ROS frame (X forward, Z up, Y left) + for (int i = 0; i < 9; i++) { + rightCamInfoMsg->r[i] = zedParam.stereo_transform.getRotationMatrix().r[i]; + } + } } - } #endif - leftCamInfoMsg->p.fill(0.0); - rightCamInfoMsg->p.fill(0.0); - leftCamInfoMsg->p[0] = static_cast(zedParam.left_cam.fx); - leftCamInfoMsg->p[2] = static_cast(zedParam.left_cam.cx); - leftCamInfoMsg->p[5] = static_cast(zedParam.left_cam.fy); - leftCamInfoMsg->p[6] = static_cast(zedParam.left_cam.cy); - leftCamInfoMsg->p[10] = 1.0; - // http://docs.ros.org/api/sensor_msgs/html/msg/CameraInfo.html - rightCamInfoMsg->p[3] = static_cast(-1 * zedParam.left_cam.fx * baseline); - rightCamInfoMsg->p[0] = static_cast(zedParam.right_cam.fx); - rightCamInfoMsg->p[2] = static_cast(zedParam.right_cam.cx); - rightCamInfoMsg->p[5] = static_cast(zedParam.right_cam.fy); - rightCamInfoMsg->p[6] = static_cast(zedParam.right_cam.cy); - rightCamInfoMsg->p[10] = 1.0; - leftCamInfoMsg->width = rightCamInfoMsg->width = static_cast(mMatResolVideo.width); - leftCamInfoMsg->height = rightCamInfoMsg->height = static_cast(mMatResolVideo.height); - leftCamInfoMsg->header.frame_id = leftFrameId; - rightCamInfoMsg->header.frame_id = rightFrameId; + leftCamInfoMsg->p.fill(0.0); + rightCamInfoMsg->p.fill(0.0); + leftCamInfoMsg->p[0] = static_cast(zedParam.left_cam.fx); + leftCamInfoMsg->p[2] = static_cast(zedParam.left_cam.cx); + leftCamInfoMsg->p[5] = static_cast(zedParam.left_cam.fy); + leftCamInfoMsg->p[6] = static_cast(zedParam.left_cam.cy); + leftCamInfoMsg->p[10] = 1.0; + // http://docs.ros.org/api/sensor_msgs/html/msg/CameraInfo.html + rightCamInfoMsg->p[3] = static_cast(-1 * zedParam.left_cam.fx * baseline); + rightCamInfoMsg->p[0] = static_cast(zedParam.right_cam.fx); + rightCamInfoMsg->p[2] = static_cast(zedParam.right_cam.cx); + rightCamInfoMsg->p[5] = static_cast(zedParam.right_cam.fy); + rightCamInfoMsg->p[6] = static_cast(zedParam.right_cam.cy); + rightCamInfoMsg->p[10] = 1.0; + leftCamInfoMsg->width = rightCamInfoMsg->width = static_cast(mMatResolVideo.width); + leftCamInfoMsg->height = rightCamInfoMsg->height = static_cast(mMatResolVideo.height); + leftCamInfoMsg->header.frame_id = leftFrameId; + rightCamInfoMsg->header.frame_id = rightFrameId; } void ZedCamera::initPublishers() { - RCLCPP_INFO(get_logger(), "*** PUBLISHED TOPICS ***"); - - std::string topicPrefix = get_namespace(); + RCLCPP_INFO(get_logger(), "*** PUBLISHED TOPICS ***"); - if (topicPrefix.length() > 1) - { - topicPrefix += "/"; - } + std::string topicPrefix = get_namespace(); - topicPrefix += get_name(); - topicPrefix += "/"; - // ----> Topics names definition - std::string rgbTopicRoot = "rgb"; - std::string rightTopicRoot = "right"; - std::string leftTopicRoot = "left"; - std::string stereoTopicRoot = "stereo"; - std::string img_topic = "/image_rect_color"; - std::string img_raw_topic = "/image_raw_color"; - std::string img_gray_topic = "/image_rect_gray"; - std::string img_raw_gray_topic_ = "/image_raw_gray"; - std::string raw_suffix = "_raw"; - std::string left_topic = mTopicRoot + leftTopicRoot + img_topic; - std::string left_raw_topic = mTopicRoot + leftTopicRoot + raw_suffix + img_raw_topic; - std::string right_topic = mTopicRoot + rightTopicRoot + img_topic; - std::string right_raw_topic = mTopicRoot + rightTopicRoot + raw_suffix + img_raw_topic; - std::string rgb_topic = mTopicRoot + rgbTopicRoot + img_topic; - std::string rgb_raw_topic = mTopicRoot + rgbTopicRoot + raw_suffix + img_raw_topic; - std::string stereo_topic = mTopicRoot + stereoTopicRoot + img_topic; - std::string stereo_raw_topic = mTopicRoot + stereoTopicRoot + raw_suffix + img_raw_topic; - std::string left_gray_topic = mTopicRoot + leftTopicRoot + img_gray_topic; - std::string left_raw_gray_topic = mTopicRoot + leftTopicRoot + raw_suffix + img_raw_gray_topic_; - std::string right_gray_topic = mTopicRoot + rightTopicRoot + img_gray_topic; - std::string right_raw_gray_topic = mTopicRoot + rightTopicRoot + raw_suffix + img_raw_gray_topic_; - std::string rgb_gray_topic = mTopicRoot + rgbTopicRoot + img_gray_topic; - std::string rgb_raw_gray_topic = mTopicRoot + rgbTopicRoot + raw_suffix + img_raw_gray_topic_; - - // Set the disparity topic name - std::string disparity_topic = mTopicRoot + "disparity/disparity_image"; - - // Set the depth topic names - std::string depth_topic_root = "depth"; - - if (mOpenniDepthMode) - { - RCLCPP_INFO_STREAM(get_logger(), "Openni depth mode activated -> Units: mm, Encoding: MONO16"); - } - std::string depth_topic = mTopicRoot + depth_topic_root + "/depth_registered"; - - std::string pointcloud_topic = mTopicRoot + "point_cloud/cloud_registered"; - mPointcloudFusedTopic = mTopicRoot + "mapping/fused_cloud"; - - std::string object_det_topic_root = "obj_det"; - mObjectDetTopic = mTopicRoot + object_det_topic_root + "/objects"; - - std::string confImgRoot = "confidence"; - std::string conf_map_topic_name = "confidence_map"; - std::string conf_map_topic = mTopicRoot + confImgRoot + "/" + conf_map_topic_name; - - // Set the positional tracking topic names - mPoseTopic = mTopicRoot + "pose"; - mPoseCovTopic = mPoseTopic + "_with_covariance"; - - mOdomTopic = mTopicRoot + "odom"; - mOdomPathTopic = mTopicRoot + "path_odom"; - mMapPathTopic = mTopicRoot + "path_map"; - - // Set the Sensors topic names - std::string temp_topic_root = "temperature"; - std::string imuTopicRoot = "imu"; - std::string imu_topic_name = "data"; - std::string imu_topic_raw_name = "data_raw"; - std::string imu_topic_mag_name = "mag"; - // std::string imu_topic_mag_raw_name = "mag_raw"; - std::string pressure_topic_name = "atm_press"; - - std::string imu_topic = mTopicRoot + imuTopicRoot + "/" + imu_topic_name; - std::string imu_topic_raw = mTopicRoot + imuTopicRoot + "/" + imu_topic_raw_name; - std::string imu_temp_topic = mTopicRoot + temp_topic_root + "/" + imuTopicRoot; - std::string imu_mag_topic = mTopicRoot + imuTopicRoot + "/" + imu_topic_mag_name; - // std::string imu_mag_topic_raw = imuTopicRoot + "/" + imu_topic_mag_raw_name; - std::string pressure_topic = mTopicRoot + /*imuTopicRoot + "/" +*/ pressure_topic_name; - std::string temp_topic_left = mTopicRoot + temp_topic_root + "/left"; - std::string temp_topic_right = mTopicRoot + temp_topic_root + "/right"; - // <---- Topics names definition - - // ----> Camera publishers - mPubRgb = image_transport::create_camera_publisher(this, rgb_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRgb.getTopic()); - mPubRgbGray = image_transport::create_camera_publisher(this, rgb_gray_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRgbGray.getTopic()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRgb.getInfoTopic()); - mPubRawRgb = image_transport::create_camera_publisher(this, rgb_raw_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRawRgb.getTopic()); - mPubRawRgbGray = image_transport::create_camera_publisher(this, rgb_raw_gray_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRawRgbGray.getTopic()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRawRgb.getInfoTopic()); - mPubLeft = image_transport::create_camera_publisher(this, left_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubLeft.getTopic()); - mPubLeftGray = image_transport::create_camera_publisher(this, left_gray_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubLeftGray.getTopic()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubLeft.getInfoTopic()); - mPubRawLeft = image_transport::create_camera_publisher(this, left_raw_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRawLeft.getTopic()); - mPubRawLeftGray = - image_transport::create_camera_publisher(this, left_raw_gray_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRawLeftGray.getTopic()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRawLeft.getInfoTopic()); - mPubRight = image_transport::create_camera_publisher(this, right_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRight.getTopic()); - mPubRightGray = image_transport::create_camera_publisher(this, right_gray_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRightGray.getTopic()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRight.getInfoTopic()); - mPubRawRight = image_transport::create_camera_publisher(this, right_raw_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRawRight.getTopic()); - mPubRawRightGray = - image_transport::create_camera_publisher(this, right_raw_gray_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRawRightGray.getTopic()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRawRight.getInfoTopic()); - - if (!mDepthDisabled) - { - mPubDepth = image_transport::create_camera_publisher(this, depth_topic, mDepthQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubDepth.getTopic()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubDepth.getInfoTopic()); - } - - mPubStereo = image_transport::create_publisher(this, stereo_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubStereo.getTopic()); - mPubRawStereo = image_transport::create_publisher(this, stereo_raw_topic, mVideoQos.get_rmw_qos_profile()); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubRawStereo.getTopic()); - // <---- Camera publishers + if (topicPrefix.length() > 1) { + topicPrefix += "/"; + } - // ----> Depth publishers - if (!mDepthDisabled) - { - mPubConfMap = create_publisher(conf_map_topic, mDepthQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubConfMap->get_topic_name()); - mPubDisparity = create_publisher(disparity_topic, mDepthQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubDisparity->get_topic_name()); - mPubCloud = create_publisher(pointcloud_topic, mDepthQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubCloud->get_topic_name()); - } - // <---- Depth publishers + topicPrefix += get_name(); + topicPrefix += "/"; + // ----> Topics names definition + std::string rgbTopicRoot = "rgb"; + std::string rightTopicRoot = "right"; + std::string leftTopicRoot = "left"; + std::string stereoTopicRoot = "stereo"; + std::string img_topic = "/image_rect_color"; + std::string img_raw_topic = "/image_raw_color"; + std::string img_gray_topic = "/image_rect_gray"; + std::string img_raw_gray_topic_ = "/image_raw_gray"; + std::string raw_suffix = "_raw"; + std::string left_topic = mTopicRoot + leftTopicRoot + img_topic; + std::string left_raw_topic = mTopicRoot + leftTopicRoot + raw_suffix + img_raw_topic; + std::string right_topic = mTopicRoot + rightTopicRoot + img_topic; + std::string right_raw_topic = mTopicRoot + rightTopicRoot + raw_suffix + img_raw_topic; + std::string rgb_topic = mTopicRoot + rgbTopicRoot + img_topic; + std::string rgb_raw_topic = mTopicRoot + rgbTopicRoot + raw_suffix + img_raw_topic; + std::string stereo_topic = mTopicRoot + stereoTopicRoot + img_topic; + std::string stereo_raw_topic = mTopicRoot + stereoTopicRoot + raw_suffix + img_raw_topic; + std::string left_gray_topic = mTopicRoot + leftTopicRoot + img_gray_topic; + std::string left_raw_gray_topic = mTopicRoot + leftTopicRoot + raw_suffix + img_raw_gray_topic_; + std::string right_gray_topic = mTopicRoot + rightTopicRoot + img_gray_topic; + std::string right_raw_gray_topic = mTopicRoot + rightTopicRoot + raw_suffix + img_raw_gray_topic_; + std::string rgb_gray_topic = mTopicRoot + rgbTopicRoot + img_gray_topic; + std::string rgb_raw_gray_topic = mTopicRoot + rgbTopicRoot + raw_suffix + img_raw_gray_topic_; + + // Set the disparity topic name + std::string disparity_topic = mTopicRoot + "disparity/disparity_image"; + + // Set the depth topic names + std::string depth_topic_root = "depth"; + + if (mOpenniDepthMode) { + RCLCPP_INFO_STREAM( + get_logger(), + "Openni depth mode activated -> Units: mm, Encoding: MONO16"); + } + std::string depth_topic = mTopicRoot + depth_topic_root + "/depth_registered"; + + std::string pointcloud_topic = mTopicRoot + "point_cloud/cloud_registered"; + mPointcloudFusedTopic = mTopicRoot + "mapping/fused_cloud"; + + std::string object_det_topic_root = "obj_det"; + mObjectDetTopic = mTopicRoot + object_det_topic_root + "/objects"; + + std::string confImgRoot = "confidence"; + std::string conf_map_topic_name = "confidence_map"; + std::string conf_map_topic = mTopicRoot + confImgRoot + "/" + conf_map_topic_name; + + // Set the positional tracking topic names + mPoseTopic = mTopicRoot + "pose"; + mPoseCovTopic = mPoseTopic + "_with_covariance"; + + mOdomTopic = mTopicRoot + "odom"; + mOdomPathTopic = mTopicRoot + "path_odom"; + mMapPathTopic = mTopicRoot + "path_map"; + + // Set the Sensors topic names + std::string temp_topic_root = "temperature"; + std::string imuTopicRoot = "imu"; + std::string imu_topic_name = "data"; + std::string imu_topic_raw_name = "data_raw"; + std::string imu_topic_mag_name = "mag"; + // std::string imu_topic_mag_raw_name = "mag_raw"; + std::string pressure_topic_name = "atm_press"; + + std::string imu_topic = mTopicRoot + imuTopicRoot + "/" + imu_topic_name; + std::string imu_topic_raw = mTopicRoot + imuTopicRoot + "/" + imu_topic_raw_name; + std::string imu_temp_topic = mTopicRoot + temp_topic_root + "/" + imuTopicRoot; + std::string imu_mag_topic = mTopicRoot + imuTopicRoot + "/" + imu_topic_mag_name; + // std::string imu_mag_topic_raw = imuTopicRoot + "/" + + // imu_topic_mag_raw_name; + std::string pressure_topic = mTopicRoot + /*imuTopicRoot + "/" +*/ pressure_topic_name; + std::string temp_topic_left = mTopicRoot + temp_topic_root + "/left"; + std::string temp_topic_right = mTopicRoot + temp_topic_root + "/right"; + // <---- Topics names definition + + // ----> Camera publishers + mPubRgb = image_transport::create_camera_publisher( + this, rgb_topic, mVideoQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRgb.getTopic()); + mPubRgbGray = image_transport::create_camera_publisher( + this, rgb_gray_topic, mVideoQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRgbGray.getTopic()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRgb.getInfoTopic()); + mPubRawRgb = image_transport::create_camera_publisher( + this, rgb_raw_topic, mVideoQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRawRgb.getTopic()); + mPubRawRgbGray = image_transport::create_camera_publisher( + this, rgb_raw_gray_topic, mVideoQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRawRgbGray.getTopic()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRawRgb.getInfoTopic()); + mPubLeft = image_transport::create_camera_publisher( + this, left_topic, mVideoQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubLeft.getTopic()); + mPubLeftGray = image_transport::create_camera_publisher( + this, left_gray_topic, mVideoQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubLeftGray.getTopic()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubLeft.getInfoTopic()); + mPubRawLeft = image_transport::create_camera_publisher( + this, left_raw_topic, mVideoQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRawLeft.getTopic()); + mPubRawLeftGray = image_transport::create_camera_publisher( + this, left_raw_gray_topic, mVideoQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRawLeftGray.getTopic()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRawLeft.getInfoTopic()); + mPubRight = image_transport::create_camera_publisher( + this, right_topic, mVideoQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRight.getTopic()); + mPubRightGray = image_transport::create_camera_publisher( + this, right_gray_topic, mVideoQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRightGray.getTopic()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRight.getInfoTopic()); + mPubRawRight = image_transport::create_camera_publisher( + this, right_raw_topic, mVideoQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRawRight.getTopic()); + mPubRawRightGray = image_transport::create_camera_publisher( + this, right_raw_gray_topic, mVideoQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRawRightGray.getTopic()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRawRight.getInfoTopic()); - // ----> Pos Tracking - if (!mDepthDisabled) - { - mPubPose = create_publisher(mPoseTopic, mPoseQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubPose->get_topic_name()); - mPubPoseCov = create_publisher(mPoseCovTopic, mPoseQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubPoseCov->get_topic_name()); - mPubOdom = create_publisher(mOdomTopic, mPoseQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubOdom->get_topic_name()); - mPubPosePath = create_publisher(mMapPathTopic, mPoseQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubPosePath->get_topic_name()); - mPubOdomPath = create_publisher(mOdomPathTopic, mPoseQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubOdomPath->get_topic_name()); - } - // <---- Pos Tracking + if (!mDepthDisabled) { + mPubDepth = image_transport::create_camera_publisher( + this, depth_topic, mDepthQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubDepth.getTopic()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubDepth.getInfoTopic()); + } - // ----> Mapping - if (!mDepthDisabled && mMappingEnabled) - { - mPubFusedCloud = create_publisher(mPointcloudFusedTopic, mMappingQos); + mPubStereo = image_transport::create_publisher( + this, stereo_topic, mVideoQos.get_rmw_qos_profile()); RCLCPP_INFO_STREAM(get_logger(), - "Advertised on topic " << mPubFusedCloud->get_topic_name() << " @ " << mFusedPcPubRate << " Hz"); - } - // <---- Mapping + "Advertised on topic: " << mPubStereo.getTopic()); + mPubRawStereo = image_transport::create_publisher( + this, stereo_raw_topic, mVideoQos.get_rmw_qos_profile()); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubRawStereo.getTopic()); + // <---- Camera publishers + + // ----> Depth publishers + if (!mDepthDisabled) { + mPubConfMap = create_publisher(conf_map_topic, mDepthQos); + RCLCPP_INFO_STREAM( + get_logger(), "Advertised on topic: " << mPubConfMap->get_topic_name()); + mPubDisparity = create_publisher( + disparity_topic, mDepthQos); + RCLCPP_INFO_STREAM( + get_logger(), "Advertised on topic: " << mPubDisparity->get_topic_name()); + mPubCloud = create_publisher( + pointcloud_topic, mDepthQos); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubCloud->get_topic_name()); + } + // <---- Depth publishers + + // ----> Pos Tracking + if (!mDepthDisabled) { + mPubPose = create_publisher(mPoseTopic, mPoseQos); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubPose->get_topic_name()); + mPubPoseCov = create_publisher( + mPoseCovTopic, mPoseQos); + RCLCPP_INFO_STREAM( + get_logger(), "Advertised on topic: " << mPubPoseCov->get_topic_name()); + mPubOdom = create_publisher(mOdomTopic, mPoseQos); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubOdom->get_topic_name()); + mPubPosePath = create_publisher(mMapPathTopic, mPoseQos); + RCLCPP_INFO_STREAM( + get_logger(), "Advertised on topic: " << mPubPosePath->get_topic_name()); + mPubOdomPath = create_publisher(mOdomPathTopic, mPoseQos); + RCLCPP_INFO_STREAM( + get_logger(), "Advertised on topic: " << mPubOdomPath->get_topic_name()); + } + // <---- Pos Tracking + + // ----> Mapping + if (!mDepthDisabled && mMappingEnabled) { + mPubFusedCloud = create_publisher( + mPointcloudFusedTopic, mMappingQos); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic " + << mPubFusedCloud->get_topic_name() << " @ " + << mFusedPcPubRate << " Hz"); + } + // <---- Mapping + + // ----> Sensors + if (mCamRealModel != sl::MODEL::ZED) { + mPubImu = create_publisher(imu_topic, mSensQos); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubImu->get_topic_name()); + mPubImuRaw = create_publisher(imu_topic_raw, mSensQos); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " << mPubImuRaw->get_topic_name()); + mPubImuTemp = create_publisher(imu_temp_topic, mSensQos); + RCLCPP_INFO_STREAM( + get_logger(), "Advertised on topic: " << mPubImuTemp->get_topic_name()); + + if (sl_tools::isZED2OrZED2i(mCamRealModel)) { + mPubImuMag = create_publisher( + imu_mag_topic, mSensQos); + RCLCPP_INFO_STREAM( + get_logger(), "Advertised on topic: " << mPubImuMag->get_topic_name()); + mPubPressure = create_publisher( + pressure_topic, mSensQos); + RCLCPP_INFO_STREAM( + get_logger(), + "Advertised on topic: " << mPubPressure->get_topic_name()); + mPubTempL = create_publisher( + temp_topic_left, mSensQos); + RCLCPP_INFO_STREAM( + get_logger(), "Advertised on topic: " << mPubTempL->get_topic_name()); + mPubTempR = create_publisher( + temp_topic_right, mSensQos); + RCLCPP_INFO_STREAM( + get_logger(), "Advertised on topic: " << mPubTempR->get_topic_name()); + } - // ----> Sensors - if (mCamRealModel != sl::MODEL::ZED) - { - mPubImu = create_publisher(imu_topic, mSensQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubImu->get_topic_name()); - mPubImuRaw = create_publisher(imu_topic_raw, mSensQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubImuRaw->get_topic_name()); - mPubImuTemp = create_publisher(imu_temp_topic, mSensQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubImuTemp->get_topic_name()); - - if (sl_tools::isZED2OrZED2i(mCamRealModel)) - { - mPubImuMag = create_publisher(imu_mag_topic, mSensQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubImuMag->get_topic_name()); - mPubPressure = create_publisher(pressure_topic, mSensQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubPressure->get_topic_name()); - mPubTempL = create_publisher(temp_topic_left, mSensQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubTempL->get_topic_name()); - mPubTempR = create_publisher(temp_topic_right, mSensQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubTempR->get_topic_name()); - } - - // ----> Publish latched camera/imu transform message - publishStaticImuFrameAndTopic(); - // <---- Publish latched camera/imu transform message - } - // <---- Sensors + // ----> Publish latched camera/imu transform message + publishStaticImuFrameAndTopic(); + // <---- Publish latched camera/imu transform message + } + // <---- Sensors } bool ZedCamera::startCamera() { - RCLCPP_INFO(get_logger(), "***** STARTING CAMERA *****"); - - // ----> SDK version - RCLCPP_INFO(get_logger(), "SDK Version: %d.%d.%d - Build %s", ZED_SDK_MAJOR_VERSION, ZED_SDK_MINOR_VERSION, - ZED_SDK_PATCH_VERSION, ZED_SDK_BUILD_ID); - // <---- SDK version - - // ----> TF2 Transform - mTfBuffer = std::make_unique(this->get_clock()); - mTfListener = std::make_shared(*mTfBuffer); // Start TF Listener thread - mTfBroadcaster = std::make_shared(this); - mStaticTfBroadcaster = std::make_shared(this); - // <---- TF2 Transform - - // ----> ZED configuration - if (!mSvoFilepath.empty()) - { - RCLCPP_INFO(get_logger(), "*** SVO OPENING ***"); - - mInitParams.input.setFromSVOFile(mSvoFilepath.c_str()); - mInitParams.svo_real_time_mode = mSvoRealtime; - mSvoMode = true; - } - else - { - RCLCPP_INFO(get_logger(), "*** CAMERA OPENING ***"); - - mInitParams.camera_fps = mCamGrabFrameRate; - mInitParams.camera_resolution = static_cast(mCamResol); - - if (mCamSerialNumber == 0) - { - mInitParams.input.setFromCameraID(mCamId); - } - else - { - mInitParams.input.setFromSerialNumber(mCamSerialNumber); + RCLCPP_INFO(get_logger(), "***** STARTING CAMERA *****"); + + // ----> SDK version + RCLCPP_INFO(get_logger(), + "SDK Version: %d.%d.%d - Build %s", + ZED_SDK_MAJOR_VERSION, + ZED_SDK_MINOR_VERSION, + ZED_SDK_PATCH_VERSION, + ZED_SDK_BUILD_ID); + // <---- SDK version + + // ----> TF2 Transform + mTfBuffer = std::make_unique(this->get_clock()); + mTfListener = std::make_shared( + *mTfBuffer); // Start TF Listener thread + mTfBroadcaster = std::make_shared(this); + mStaticTfBroadcaster = std::make_shared(this); + // <---- TF2 Transform + + // ----> ZED configuration + if (!mSvoFilepath.empty()) { + RCLCPP_INFO(get_logger(), "*** SVO OPENING ***"); + + mInitParams.input.setFromSVOFile(mSvoFilepath.c_str()); + mInitParams.svo_real_time_mode = mSvoRealtime; + mSvoMode = true; + } else { + RCLCPP_INFO(get_logger(), "*** CAMERA OPENING ***"); + + mInitParams.camera_fps = mCamGrabFrameRate; + mInitParams.camera_resolution = static_cast(mCamResol); + + if (mCamSerialNumber == 0) { + mInitParams.input.setFromCameraID(mCamId); + } else { + mInitParams.input.setFromSerialNumber(mCamSerialNumber); + } } - } - mInitParams.coordinate_system = sl::COORDINATE_SYSTEM::RIGHT_HANDED_Z_UP_X_FWD; - mInitParams.coordinate_units = sl::UNIT::METER; - mInitParams.depth_mode = mDepthQuality; - mInitParams.sdk_verbose = mVerbose; - mInitParams.sdk_gpu_id = mGpuId; - mInitParams.depth_stabilization = static_cast(mDepthStabilization); - mInitParams.camera_image_flip = mCameraFlip; - mInitParams.depth_minimum_distance = mCamMinDepth; - mInitParams.depth_maximum_distance = mCamMaxDepth; - - mInitParams.camera_disable_self_calib = !mCameraSelfCalib; - mInitParams.enable_image_enhancement = true; - mInitParams.enable_right_side_measure = false; - // <---- ZED configuration - - // ----> Try to open ZED camera or to load SVO - //INIT_TIMER; - //START_TIMER; - sl_tools::StopWatch connectTimer; - - mThreadStop = false; - - if (!mSvoMode) - { - if (mCamSerialNumber == 0) - { - mInitParams.input.setFromCameraID(mCamId); + mInitParams.coordinate_system = sl::COORDINATE_SYSTEM::RIGHT_HANDED_Z_UP_X_FWD; + mInitParams.coordinate_units = sl::UNIT::METER; + mInitParams.depth_mode = mDepthQuality; + mInitParams.sdk_verbose = mVerbose; + mInitParams.sdk_gpu_id = mGpuId; + mInitParams.depth_stabilization = static_cast(mDepthStabilization); + mInitParams.camera_image_flip = mCameraFlip; + mInitParams.depth_minimum_distance = mCamMinDepth; + mInitParams.depth_maximum_distance = mCamMaxDepth; + + mInitParams.camera_disable_self_calib = !mCameraSelfCalib; + mInitParams.enable_image_enhancement = true; + mInitParams.enable_right_side_measure = false; + // <---- ZED configuration + + // ----> Try to open ZED camera or to load SVO + // INIT_TIMER; + // START_TIMER; + sl_tools::StopWatch connectTimer; + + mThreadStop = false; + + if (!mSvoMode) { + if (mCamSerialNumber == 0) { + mInitParams.input.setFromCameraID(mCamId); + } else { + bool waiting_for_camera = true; + + while (waiting_for_camera) { + // Ctrl+C check + if (!rclcpp::ok()) { + return false; + } + + sl::DeviceProperties prop = sl_tools::getZEDFromSN(mCamSerialNumber); + + if (prop.id < -1 || prop.camera_state == sl::CAMERA_STATE::NOT_AVAILABLE) { + std::string msg = "Camera with SN " + std::to_string(mCamSerialNumber) + " not detected! Please verify the connection."; + RCLCPP_INFO(get_logger(), msg.c_str()); + } else { + waiting_for_camera = false; + mInitParams.input.setFromCameraID(prop.id); + } + + if (connectTimer.toc() >= mMaxReconnectTemp * mCamTimeoutSec) { + RCLCPP_ERROR(get_logger(), "Camera detection timeout"); + + return false; + } + + std::this_thread::sleep_for(std::chrono::seconds(mCamTimeoutSec)); + } + } } - else - { - bool waiting_for_camera = true; - while (waiting_for_camera) - { - // Ctrl+C check - if (!rclcpp::ok()) - { - return false; - } + while (1) { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); - sl::DeviceProperties prop = sl_tools::getZEDFromSN(mCamSerialNumber); + mConnStatus = mZed.open(mInitParams); - if (prop.id < -1 || prop.camera_state == sl::CAMERA_STATE::NOT_AVAILABLE) - { - std::string msg = - "Camera with SN " + std::to_string(mCamSerialNumber) + " not detected! Please verify the connection."; - RCLCPP_INFO(get_logger(), msg.c_str()); - } - else - { - waiting_for_camera = false; - mInitParams.input.setFromCameraID(prop.id); + if (mConnStatus == sl::ERROR_CODE::SUCCESS) { + RCLCPP_DEBUG(get_logger(), "Opening successfull"); + break; } - if (connectTimer.toc() >= mMaxReconnectTemp * mCamTimeoutSec) - { - RCLCPP_ERROR(get_logger(), "Camera detection timeout"); + if (mSvoMode) { + RCLCPP_WARN(get_logger(), + "Error opening SVO: %s", + sl::toString(mConnStatus).c_str()); - return false; + return false; } - std::this_thread::sleep_for(std::chrono::seconds(mCamTimeoutSec)); - } - } - } - - while (1) - { - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - - mConnStatus = mZed.open(mInitParams); - - if (mConnStatus == sl::ERROR_CODE::SUCCESS) - { - RCLCPP_DEBUG(get_logger(), "Opening successfull"); - break; - } + RCLCPP_WARN(get_logger(), + "Error opening camera: %s", + sl::toString(mConnStatus).c_str()); - if (mSvoMode) - { - RCLCPP_WARN(get_logger(), "Error opening SVO: %s", sl::toString(mConnStatus).c_str()); + if (mConnStatus == sl::ERROR_CODE::CAMERA_DETECTION_ISSUE && mCamUserModel == sl::MODEL::ZED_M) { + RCLCPP_INFO(get_logger(), "Try to flip the USB3 Type-C connector"); + } else { + RCLCPP_INFO(get_logger(), "Please verify the USB3 connection"); + } - return false; - } + if (!rclcpp::ok() || mThreadStop) { + RCLCPP_INFO(get_logger(), "ZED activation interrupted"); - RCLCPP_WARN(get_logger(), "Error opening camera: %s", sl::toString(mConnStatus).c_str()); + return false; + } - if (mConnStatus == sl::ERROR_CODE::CAMERA_DETECTION_ISSUE && mCamUserModel == sl::MODEL::ZED_M) - { - RCLCPP_INFO(get_logger(), "Try to flip the USB3 Type-C connector"); - } - else - { - RCLCPP_INFO(get_logger(), "Please verify the USB3 connection"); - } + if (connectTimer.toc() > mMaxReconnectTemp * mCamTimeoutSec) { + RCLCPP_ERROR(get_logger(), "Camera detection timeout"); - if (!rclcpp::ok() || mThreadStop) - { - RCLCPP_INFO(get_logger(), "ZED activation interrupted"); + return false; + } - return false; + std::this_thread::sleep_for(std::chrono::seconds(mCamTimeoutSec)); } + // <---- Try to open ZED camera or to load SVO - if (connectTimer.toc() > mMaxReconnectTemp * mCamTimeoutSec) - { - RCLCPP_ERROR(get_logger(), "Camera detection timeout"); + // ----> Camera information + sl::CameraInformation camInfo = mZed.getCameraInformation(); - return false; + float realFps = camInfo.camera_configuration.fps; + if (realFps != static_cast(mCamGrabFrameRate)) { + RCLCPP_WARN_STREAM(get_logger(), + "!!! `general.grab_frame_rate` value is not valid: '" + << mCamGrabFrameRate + << "'. Automatically replaced with '" << realFps + << "'. Please fix the parameter !!!"); + mCamGrabFrameRate = realFps; } - std::this_thread::sleep_for(std::chrono::seconds(mCamTimeoutSec)); - } - // <---- Try to open ZED camera or to load SVO - - // ----> Camera information - sl::CameraInformation camInfo = mZed.getCameraInformation(); - - float realFps = camInfo.camera_configuration.fps; - if (realFps != static_cast(mCamGrabFrameRate)) - { - RCLCPP_WARN_STREAM(get_logger(), "!!! `general.grab_frame_rate` value is not valid: '" - << mCamGrabFrameRate << "'. Automatically replaced with '" << realFps - << "'. Please fix the parameter !!!"); - mCamGrabFrameRate = realFps; - } + // CUdevice devid; + cuCtxGetDevice(&mGpuId); + RCLCPP_INFO_STREAM(get_logger(), "ZED SDK running on GPU #" << mGpuId); - // CUdevice devid; - cuCtxGetDevice(&mGpuId); - RCLCPP_INFO_STREAM(get_logger(), "ZED SDK running on GPU #" << mGpuId); + // Camera model + mCamRealModel = camInfo.camera_model; - // Camera model - mCamRealModel = camInfo.camera_model; - - if (mCamRealModel == sl::MODEL::ZED) - { - if (mCamUserModel != sl::MODEL::ZED) - { - RCLCPP_WARN(get_logger(), "Camera model does not match user parameter. Please modify " - "the value of the parameter 'general.camera_model' to 'zed'"); - } - } - else if (mCamRealModel == sl::MODEL::ZED_M) - { - if (mCamUserModel != sl::MODEL::ZED_M) - { - RCLCPP_WARN(get_logger(), "Camera model does not match user parameter. Please modify " - "the value of the parameter 'general.camera_model' to 'zedm'"); - } - } - else if (mCamRealModel == sl::MODEL::ZED2) - { - if (mCamUserModel != sl::MODEL::ZED2) - { - RCLCPP_WARN(get_logger(), "Camera model does not match user parameter. Please modify " - "the value of the parameter 'general.camera_model' to 'zed2'"); - } - } - else if (mCamRealModel == sl::MODEL::ZED2i) - { - if (mCamUserModel != sl::MODEL::ZED2i) - { - RCLCPP_WARN(get_logger(), "Camera model does not match user parameter. Please modify " - "the value of the parameter 'general.camera_model' to 'zed2i'"); + if (mCamRealModel == sl::MODEL::ZED) { + if (mCamUserModel != sl::MODEL::ZED) { + RCLCPP_WARN(get_logger(), + "Camera model does not match user parameter. Please modify " + "the value of the parameter 'general.camera_model' to 'zed'"); + } + } else if (mCamRealModel == sl::MODEL::ZED_M) { + if (mCamUserModel != sl::MODEL::ZED_M) { + RCLCPP_WARN( + get_logger(), + "Camera model does not match user parameter. Please modify " + "the value of the parameter 'general.camera_model' to 'zedm'"); + } + } else if (mCamRealModel == sl::MODEL::ZED2) { + if (mCamUserModel != sl::MODEL::ZED2) { + RCLCPP_WARN( + get_logger(), + "Camera model does not match user parameter. Please modify " + "the value of the parameter 'general.camera_model' to 'zed2'"); + } + } else if (mCamRealModel == sl::MODEL::ZED2i) { + if (mCamUserModel != sl::MODEL::ZED2i) { + RCLCPP_WARN( + get_logger(), + "Camera model does not match user parameter. Please modify " + "the value of the parameter 'general.camera_model' to 'zed2i'"); + } } - } #if ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION >= 5 - else if (mCamRealModel == sl::MODEL::ZED2i) - { - if (mCamUserModel != sl::MODEL::ZED2i) - { - RCLCPP_WARN(get_logger(), "Camera model does not match user parameter. Please modify " - "the value of the parameter 'general.camera_model' to 'zed2i'"); + else if (mCamRealModel == sl::MODEL::ZED2i) { + if (mCamUserModel != sl::MODEL::ZED2i) { + RCLCPP_WARN( + get_logger(), + "Camera model does not match user parameter. Please modify " + "the value of the parameter 'general.camera_model' to 'zed2i'"); + } } - } #endif - RCLCPP_INFO_STREAM(get_logger(), " * Camera Model\t-> " << sl::toString(mCamRealModel).c_str()); - mCamSerialNumber = camInfo.serial_number; - RCLCPP_INFO_STREAM(get_logger(), " * Serial Number\t-> " << mCamSerialNumber); - - RCLCPP_INFO_STREAM(get_logger(), - " * Input type\t-> " << sl::toString(mZed.getCameraInformation().input_type).c_str()); - if (mSvoMode) - { - RCLCPP_INFO(get_logger(), " * SVO resolution\t-> %ldx%ld", - mZed.getCameraInformation().camera_configuration.resolution.width, - mZed.getCameraInformation().camera_configuration.resolution.height); - RCLCPP_INFO_STREAM(get_logger(), " * SVO framerate\t-> " << (mZed.getCameraInformation().camera_configuration.fps)); - } - - // Firmwares - if (!mSvoMode) - { + RCLCPP_INFO_STREAM(get_logger(), + " * Camera Model\t-> " + << sl::toString(mCamRealModel).c_str()); + mCamSerialNumber = camInfo.serial_number; + RCLCPP_INFO_STREAM(get_logger(), " * Serial Number\t-> " << mCamSerialNumber); + + RCLCPP_INFO_STREAM( + get_logger(), + " * Input type\t-> " + << sl::toString(mZed.getCameraInformation().input_type).c_str()); + if (mSvoMode) { + RCLCPP_INFO( + get_logger(), + " * SVO resolution\t-> %ldx%ld", + mZed.getCameraInformation().camera_configuration.resolution.width, + mZed.getCameraInformation().camera_configuration.resolution.height); + RCLCPP_INFO_STREAM( + get_logger(), + " * SVO framerate\t-> " + << (mZed.getCameraInformation().camera_configuration.fps)); + } + + // Firmwares + if (!mSvoMode) { #if ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION < 1 - mCamFwVersion = camInfo.camera_firmware_version; + mCamFwVersion = camInfo.camera_firmware_version; #else - mCamFwVersion = camInfo.camera_configuration.firmware_version; + mCamFwVersion = camInfo.camera_configuration.firmware_version; #endif - RCLCPP_INFO_STREAM(get_logger(), " * Camera FW Version -> " << mCamFwVersion); - if (mCamRealModel != sl::MODEL::ZED) - { + RCLCPP_INFO_STREAM(get_logger(), + " * Camera FW Version -> " << mCamFwVersion); + if (mCamRealModel != sl::MODEL::ZED) { #if ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION < 1 - mSensFwVersion = camInfo.sensors_firmware_version; + mSensFwVersion = camInfo.sensors_firmware_version; #else - mSensFwVersion = camInfo.sensors_configuration.firmware_version; + mSensFwVersion = camInfo.sensors_configuration.firmware_version; #endif - RCLCPP_INFO_STREAM(get_logger(), " * Sensors FW Version -> " << mSensFwVersion); + RCLCPP_INFO_STREAM(get_logger(), + " * Sensors FW Version -> " << mSensFwVersion); + } } - } - // Camera/IMU transform - if (mCamRealModel != sl::MODEL::ZED) - { + // Camera/IMU transform + if (mCamRealModel != sl::MODEL::ZED) { #if ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION < 1 - mSlCamImuTransf = camInfo.camera_imu_transform; + mSlCamImuTransf = camInfo.camera_imu_transform; #else - mSlCamImuTransf = camInfo.sensors_configuration.camera_imu_transform; + mSlCamImuTransf = camInfo.sensors_configuration.camera_imu_transform; #endif - RCLCPP_DEBUG(get_logger(), "Camera-IMU Transform: \n %s", mSlCamImuTransf.getInfos().c_str()); - } - - mCamWidth = camInfo.camera_configuration.resolution.width; - mCamHeight = camInfo.camera_configuration.resolution.height; - - RCLCPP_DEBUG_STREAM(get_logger(), "Camera Frame size : " << mCamWidth << "x" << mCamHeight); - int v_w = static_cast(mCamWidth * mImgDownsampleFactor); - int v_h = static_cast(mCamHeight * mImgDownsampleFactor); - mMatResolVideo = sl::Resolution(v_w, v_h); - RCLCPP_DEBUG_STREAM(get_logger(), "Image Mat size : " << mMatResolVideo.width << "x" << mMatResolVideo.height); - int d_w = static_cast(mCamWidth * mDepthDownsampleFactor); - int d_h = static_cast(mCamHeight * mDepthDownsampleFactor); - mMatResolDepth = sl::Resolution(d_w, d_h); - RCLCPP_DEBUG_STREAM(get_logger(), "Depth Mat size : " << mMatResolDepth.width << "x" << mMatResolDepth.height); - // <---- Camera information - - // ----> Camera Info messages - mRgbCamInfoMsg = std::make_shared(); - mRgbCamInfoRawMsg = std::make_shared(); - mLeftCamInfoMsg = std::make_shared(); - mLeftCamInfoRawMsg = std::make_shared(); - mRightCamInfoMsg = std::make_shared(); - mRightCamInfoRawMsg = std::make_shared(); - mDepthCamInfoMsg = std::make_shared(); - - fillCamInfo(mZed, mLeftCamInfoMsg, mRightCamInfoMsg, mLeftCamOptFrameId, mRightCamOptFrameId); - fillCamInfo(mZed, mLeftCamInfoRawMsg, mRightCamInfoRawMsg, mLeftCamOptFrameId, mRightCamOptFrameId, true); - mRgbCamInfoMsg = mLeftCamInfoMsg; - mRgbCamInfoRawMsg = mLeftCamInfoRawMsg; - mDepthCamInfoMsg = mLeftCamInfoMsg; - // <---- Camera Info messages - - setTFCoordFrameNames(); // Requires mZedRealCamModel available only after camera opening - initPublishers(); // Requires mZedRealCamModel available only after camera opening - - // Disable AEC_AGC and Auto Whitebalance to trigger it if user set it to automatic - mZed.setCameraSettings(sl::VIDEO_SETTINGS::AEC_AGC, 0); - mZed.setCameraSettings(sl::VIDEO_SETTINGS::WHITEBALANCE_AUTO, 0); - // Force parameters with a dummy grab - mZed.grab(); - - // Initialialized timestamp to avoid wrong initial data - // ----> Timestamp - if (mSvoMode) - { - mFrameTimestamp = sl_tools::slTime2Ros(mZed.getTimestamp(sl::TIME_REFERENCE::CURRENT)); - } - else - { - mFrameTimestamp = sl_tools::slTime2Ros(mZed.getTimestamp(sl::TIME_REFERENCE::IMAGE)); - } - // <---- Timestamp - - // ----> Initialize Diagnostic statistics - mElabPeriodMean_sec = std::make_unique(mCamGrabFrameRate); - mGrabPeriodMean_sec = std::make_unique(mCamGrabFrameRate); - mVideoDepthPeriodMean_sec = std::make_unique(mCamGrabFrameRate); - mVideoDepthElabMean_sec = std::make_unique(mCamGrabFrameRate); - mPcPeriodMean_sec = std::make_unique(mCamGrabFrameRate); - mPcProcMean_sec = std::make_unique(mCamGrabFrameRate); - mObjDetPeriodMean_sec = std::make_unique(mCamGrabFrameRate); - mObjDetElabMean_sec = std::make_unique(mCamGrabFrameRate); - mImuPeriodMean_sec = std::make_unique(mSensPubRate); - mBaroPeriodMean_sec = std::make_unique(mSensPubRate); - mMagPeriodMean_sec = std::make_unique(mSensPubRate); - mPubFusedCloudPeriodMean_sec = std::make_unique(10); - // <---- Initialize Diagnostic statistics - - // ----> Start Pointcloud thread - if (!mDepthDisabled) - { - mPcDataReady = false; - // RCLCPP_DEBUG(get_logger(), "on_activate -> mPcDataReady FALSE") - mPcThread = std::thread(&ZedCamera::threadFunc_pointcloudElab, this); - } - // <---- Start Pointcloud thread - - // Start pool thread - mGrabThread = std::thread(&ZedCamera::threadFunc_zedGrab, this); + RCLCPP_DEBUG(get_logger(), + "Camera-IMU Transform: \n %s", + mSlCamImuTransf.getInfos().c_str()); + } - // Start data publishing timer - startVideoDepthTimer(mPubFrameRate); + mCamWidth = camInfo.camera_configuration.resolution.width; + mCamHeight = camInfo.camera_configuration.resolution.height; - if (!mSvoMode && mCamRealModel != sl::MODEL::ZED) - { - if (mSensPubRate == 400.) - { - mSensPubRate *= TIMER_TIME_FACTOR; + RCLCPP_DEBUG_STREAM(get_logger(), + "Camera Frame size : " << mCamWidth << "x" << mCamHeight); + int v_w = static_cast(mCamWidth * mImgDownsampleFactor); + int v_h = static_cast(mCamHeight * mImgDownsampleFactor); + mMatResolVideo = sl::Resolution(v_w, v_h); + RCLCPP_DEBUG_STREAM(get_logger(), + "Image Mat size : " << mMatResolVideo.width << "x" + << mMatResolVideo.height); + int d_w = static_cast(mCamWidth * mDepthDownsampleFactor); + int d_h = static_cast(mCamHeight * mDepthDownsampleFactor); + mMatResolDepth = sl::Resolution(d_w, d_h); + RCLCPP_DEBUG_STREAM(get_logger(), + "Depth Mat size : " << mMatResolDepth.width << "x" + << mMatResolDepth.height); + // <---- Camera information + + // ----> Camera Info messages + mRgbCamInfoMsg = std::make_shared(); + mRgbCamInfoRawMsg = std::make_shared(); + mLeftCamInfoMsg = std::make_shared(); + mLeftCamInfoRawMsg = std::make_shared(); + mRightCamInfoMsg = std::make_shared(); + mRightCamInfoRawMsg = std::make_shared(); + mDepthCamInfoMsg = std::make_shared(); + + fillCamInfo(mZed, + mLeftCamInfoMsg, + mRightCamInfoMsg, + mLeftCamOptFrameId, + mRightCamOptFrameId); + fillCamInfo(mZed, + mLeftCamInfoRawMsg, + mRightCamInfoRawMsg, + mLeftCamOptFrameId, + mRightCamOptFrameId, + true); + mRgbCamInfoMsg = mLeftCamInfoMsg; + mRgbCamInfoRawMsg = mLeftCamInfoRawMsg; + mDepthCamInfoMsg = mLeftCamInfoMsg; + // <---- Camera Info messages + + setTFCoordFrameNames(); // Requires mZedRealCamModel available only after + // camera opening + initPublishers(); // Requires mZedRealCamModel available only after camera + // opening + + // Disable AEC_AGC and Auto Whitebalance to trigger it if user set it to + // automatic + mZed.setCameraSettings(sl::VIDEO_SETTINGS::AEC_AGC, 0); + mZed.setCameraSettings(sl::VIDEO_SETTINGS::WHITEBALANCE_AUTO, 0); + // Force parameters with a dummy grab + mZed.grab(); + + // Initialialized timestamp to avoid wrong initial data + // ----> Timestamp + if (mSvoMode) { + mFrameTimestamp = sl_tools::slTime2Ros(mZed.getTimestamp(sl::TIME_REFERENCE::CURRENT)); + } else { + mFrameTimestamp = sl_tools::slTime2Ros(mZed.getTimestamp(sl::TIME_REFERENCE::IMAGE)); } + // <---- Timestamp + + // ----> Initialize Diagnostic statistics + mElabPeriodMean_sec = std::make_unique(mCamGrabFrameRate); + mGrabPeriodMean_sec = std::make_unique(mCamGrabFrameRate); + mVideoDepthPeriodMean_sec = std::make_unique(mCamGrabFrameRate); + mVideoDepthElabMean_sec = std::make_unique(mCamGrabFrameRate); + mPcPeriodMean_sec = std::make_unique(mCamGrabFrameRate); + mPcProcMean_sec = std::make_unique(mCamGrabFrameRate); + mObjDetPeriodMean_sec = std::make_unique(mCamGrabFrameRate); + mObjDetElabMean_sec = std::make_unique(mCamGrabFrameRate); + mImuPeriodMean_sec = std::make_unique(mSensPubRate); + mBaroPeriodMean_sec = std::make_unique(mSensPubRate); + mMagPeriodMean_sec = std::make_unique(mSensPubRate); + mPubFusedCloudPeriodMean_sec = std::make_unique(10); + // <---- Initialize Diagnostic statistics + + // ----> Start Pointcloud thread + if (!mDepthDisabled) { + mPcDataReady = false; + // RCLCPP_DEBUG(get_logger(), "on_activate -> mPcDataReady FALSE") + mPcThread = std::thread(&ZedCamera::threadFunc_pointcloudElab, this); + } + // <---- Start Pointcloud thread + + // Start pool thread + mGrabThread = std::thread(&ZedCamera::threadFunc_zedGrab, this); + + // Start data publishing timer + startVideoDepthTimer(mPubFrameRate); + + if (!mSvoMode && mCamRealModel != sl::MODEL::ZED) { + if (mSensPubRate == 400.) { + mSensPubRate *= TIMER_TIME_FACTOR; + } - std::chrono::milliseconds sensorsPubPeriod_msec(static_cast(1000.0 / (mSensPubRate))); - mSensTimer = create_wall_timer(std::chrono::duration_cast(sensorsPubPeriod_msec), - std::bind(&ZedCamera::callback_pubSensorsData, this)); - } + std::chrono::milliseconds sensorsPubPeriod_msec( + static_cast(1000.0 / (mSensPubRate))); + mSensTimer = create_wall_timer(std::chrono::duration_cast( + sensorsPubPeriod_msec), + std::bind(&ZedCamera::callback_pubSensorsData, this)); + } - return true; + return true; } void ZedCamera::startVideoDepthTimer(double pubFrameRate) { - if (mVideoDepthTimer != nullptr) - { - mVideoDepthTimer->cancel(); - } + if (mVideoDepthTimer != nullptr) { + mVideoDepthTimer->cancel(); + } - std::chrono::milliseconds videoDepthPubPeriod_msec(static_cast(1000.0 / (pubFrameRate))); - mVideoDepthTimer = create_wall_timer(std::chrono::duration_cast(videoDepthPubPeriod_msec), - std::bind(&ZedCamera::callback_pubVideoDepth, this)); + std::chrono::milliseconds videoDepthPubPeriod_msec( + static_cast(1000.0 / (pubFrameRate))); + mVideoDepthTimer = create_wall_timer(std::chrono::duration_cast( + videoDepthPubPeriod_msec), + std::bind(&ZedCamera::callback_pubVideoDepth, this)); } void ZedCamera::startFusedPcTimer(double fusedPcRate) { - if (mFusedPcTimer != nullptr) - { - mFusedPcTimer->cancel(); - } + if (mFusedPcTimer != nullptr) { + mFusedPcTimer->cancel(); + } - std::chrono::milliseconds pubPeriod_msec(static_cast(1000.0 / (fusedPcRate))); - mFusedPcTimer = create_wall_timer(std::chrono::duration_cast(pubPeriod_msec), - std::bind(&ZedCamera::callback_pubFusedPc, this)); + std::chrono::milliseconds pubPeriod_msec( + static_cast(1000.0 / (fusedPcRate))); + mFusedPcTimer = create_wall_timer( + std::chrono::duration_cast(pubPeriod_msec), + std::bind(&ZedCamera::callback_pubFusedPc, this)); } void ZedCamera::startPathPubTimer(double pathTimerRate) { - if (mPathTimer != nullptr) - { - mPathTimer->cancel(); - } - - if (pathTimerRate > 0) - { - std::chrono::milliseconds pubPeriod_msec(static_cast(1000.0 / (pathTimerRate))); - mPathTimer = create_wall_timer(std::chrono::duration_cast(pubPeriod_msec), - std::bind(&ZedCamera::callback_pubPaths, this)); - - if (mOdomPath.size() == 0 && mMapPath.size() == 0) - { - if (mPathMaxCount != -1) - { - RCLCPP_DEBUG_STREAM(get_logger(), "Path vectors reserved " << mPathMaxCount << " poses."); - mOdomPath.reserve(mPathMaxCount); - mMapPath.reserve(mPathMaxCount); - - RCLCPP_DEBUG_STREAM(get_logger(), "Path vector sizes: " << mOdomPath.size() << " " << mMapPath.size()); - } + if (mPathTimer != nullptr) { + mPathTimer->cancel(); + } + + if (pathTimerRate > 0) { + std::chrono::milliseconds pubPeriod_msec( + static_cast(1000.0 / (pathTimerRate))); + mPathTimer = create_wall_timer( + std::chrono::duration_cast(pubPeriod_msec), + std::bind(&ZedCamera::callback_pubPaths, this)); + + if (mOdomPath.size() == 0 && mMapPath.size() == 0) { + if (mPathMaxCount != -1) { + RCLCPP_DEBUG_STREAM( + get_logger(), "Path vectors reserved " << mPathMaxCount << " poses."); + mOdomPath.reserve(mPathMaxCount); + mMapPath.reserve(mPathMaxCount); + + RCLCPP_DEBUG_STREAM(get_logger(), + "Path vector sizes: " << mOdomPath.size() << " " + << mMapPath.size()); + } + } + } else { + mOdomPath.clear(); + mMapPath.clear(); + mPathTimer->cancel(); + RCLCPP_INFO_STREAM( + get_logger(), + "Path topics not published -> Pub. rate: " << pathTimerRate << " Hz"); } - } - else - { - mOdomPath.clear(); - mMapPath.clear(); - mPathTimer->cancel(); - RCLCPP_INFO_STREAM(get_logger(), "Path topics not published -> Pub. rate: " << pathTimerRate << " Hz"); - } } bool ZedCamera::startPosTracking() { - if (mDepthDisabled) - { - RCLCPP_WARN(get_logger(), "Cannot start Positional Tracking if `depth.quality` is set to `0` [NONE]"); - return false; - } + if (mDepthDisabled) { + RCLCPP_WARN(get_logger(), + "Cannot start Positional Tracking if " + "`depth.quality` is set to `0` [NONE]"); + return false; + } - RCLCPP_INFO_STREAM(get_logger(), "*** Starting Positional Tracking ***"); + RCLCPP_INFO_STREAM(get_logger(), "*** Starting Positional Tracking ***"); - RCLCPP_INFO(get_logger(), " * Waiting for valid static transformations..."); + RCLCPP_INFO(get_logger(), " * Waiting for valid static transformations..."); - bool transformOk = false; - double elapsed = 0.0; - mPosTrackingReady = false; + bool transformOk = false; + double elapsed = 0.0; + mPosTrackingReady = false; - auto start = std::chrono::high_resolution_clock::now(); + auto start = std::chrono::high_resolution_clock::now(); - do - { - transformOk = setPose(mInitialBasePose[0], mInitialBasePose[1], mInitialBasePose[2], mInitialBasePose[3], - mInitialBasePose[4], mInitialBasePose[5]); + do { + transformOk = setPose(mInitialBasePose[0], + mInitialBasePose[1], + mInitialBasePose[2], + mInitialBasePose[3], + mInitialBasePose[4], + mInitialBasePose[5]); - elapsed = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start) - .count(); + elapsed = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - start) + .count(); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); - if (elapsed > 10000) - { - RCLCPP_WARN(get_logger(), " !!! Failed to get static transforms. Is the 'ROBOT STATE PUBLISHER' node correctly " - "working? "); - break; - } + if (elapsed > 10000) { + RCLCPP_WARN(get_logger(), + " !!! Failed to get static transforms. Is the " + "'ROBOT STATE PUBLISHER' node correctly " + "working? "); + break; + } - } while (transformOk == false); + } while (transformOk == false); - if (transformOk) - { - RCLCPP_DEBUG(get_logger(), "Time required to get valid static transforms: %g sec", elapsed / 1000.); - } + if (transformOk) { + RCLCPP_DEBUG(get_logger(), + "Time required to get valid static transforms: %g sec", + elapsed / 1000.); + } - RCLCPP_INFO(get_logger(), "Initial ZED left camera pose (ZED pos. tracking): "); - RCLCPP_INFO(get_logger(), " * T: [%g,%g,%g]", mInitialPoseSl.getTranslation().x, mInitialPoseSl.getTranslation().y, - mInitialPoseSl.getTranslation().z); - RCLCPP_INFO(get_logger(), " * Q: [%g,%g,%g,%g]", mInitialPoseSl.getOrientation().ox, - mInitialPoseSl.getOrientation().oy, mInitialPoseSl.getOrientation().oz, - mInitialPoseSl.getOrientation().ow); + RCLCPP_INFO(get_logger(), + "Initial ZED left camera pose (ZED pos. tracking): "); + RCLCPP_INFO(get_logger(), + " * T: [%g,%g,%g]", + mInitialPoseSl.getTranslation().x, + mInitialPoseSl.getTranslation().y, + mInitialPoseSl.getTranslation().z); + RCLCPP_INFO(get_logger(), + " * Q: [%g,%g,%g,%g]", + mInitialPoseSl.getOrientation().ox, + mInitialPoseSl.getOrientation().oy, + mInitialPoseSl.getOrientation().oz, + mInitialPoseSl.getOrientation().ow); - if (mAreaMemoryDbPath != "" && !sl_tools::file_exist(mAreaMemoryDbPath)) - { - mAreaMemoryDbPath = ""; - RCLCPP_WARN_STREAM(get_logger(), - "'area_memory_db_path' path doesn't exist or is unreachable: " << mAreaMemoryDbPath); - } + if (mAreaMemoryDbPath != "" && !sl_tools::file_exist(mAreaMemoryDbPath)) { + mAreaMemoryDbPath = ""; + RCLCPP_WARN_STREAM( + get_logger(), + "'area_memory_db_path' path doesn't exist or is unreachable: " + << mAreaMemoryDbPath); + } - // Tracking parameters - sl::PositionalTrackingParameters trackParams; + // Tracking parameters + sl::PositionalTrackingParameters trackParams; - trackParams.area_file_path = mAreaMemoryDbPath.c_str(); + trackParams.area_file_path = mAreaMemoryDbPath.c_str(); - mPoseSmoothing = false; // Always false. Pose Smoothing is to be enabled only for VR/AR applications - trackParams.enable_pose_smoothing = mPoseSmoothing; + mPoseSmoothing = false; // Always false. Pose Smoothing is to be enabled only + // for VR/AR applications + trackParams.enable_pose_smoothing = mPoseSmoothing; - trackParams.enable_area_memory = mAreaMemory; - trackParams.enable_imu_fusion = mImuFusion; - trackParams.initial_world_transform = mInitialPoseSl; + trackParams.enable_area_memory = mAreaMemory; + trackParams.enable_imu_fusion = mImuFusion; + trackParams.initial_world_transform = mInitialPoseSl; - trackParams.set_floor_as_origin = mFloorAlignment; + trackParams.set_floor_as_origin = mFloorAlignment; - sl::ERROR_CODE err = mZed.enablePositionalTracking(trackParams); + sl::ERROR_CODE err = mZed.enablePositionalTracking(trackParams); - if (err == sl::ERROR_CODE::SUCCESS) - { - mPosTrackingStarted = true; - } - else - { - mPosTrackingStarted = false; + if (err == sl::ERROR_CODE::SUCCESS) { + mPosTrackingStarted = true; + } else { + mPosTrackingStarted = false; - RCLCPP_WARN(get_logger(), "Tracking not started: %s", sl::toString(err).c_str()); - } + RCLCPP_WARN( + get_logger(), "Tracking not started: %s", sl::toString(err).c_str()); + } - if (mPosTrackingStarted) - { - startPathPubTimer(mPathPubRate); - } + if (mPosTrackingStarted) { + startPathPubTimer(mPathPubRate); + } - return mPosTrackingStarted; + return mPosTrackingStarted; } bool ZedCamera::start3dMapping() { - if (mDepthDisabled) - { - RCLCPP_WARN(get_logger(), "Cannot start 3D Mapping if `depth.quality` is set to `0` [NONE]"); - return false; - } - if (!mMappingEnabled) - { - return false; - } - - RCLCPP_INFO_STREAM(get_logger(), "*** Starting Spatial Mapping ***"); - - sl::SpatialMappingParameters params; - params.map_type = sl::SpatialMappingParameters::SPATIAL_MAP_TYPE::FUSED_POINT_CLOUD; - params.use_chunk_only = true; - - sl::SpatialMappingParameters spMapPar; - - float lRes = spMapPar.allowed_resolution.first; - float hRes = spMapPar.allowed_resolution.second; - - if (mMappingRes < lRes) - { - RCLCPP_WARN_STREAM(get_logger(), "'mapping.resolution' value (" << mMappingRes - << " m) is lower than the allowed resolution " - "values. Fixed automatically"); - mMappingRes = lRes; - } - if (mMappingRes > hRes) - { - RCLCPP_WARN_STREAM(get_logger(), "'mapping.resolution' value (" << mMappingRes - << " m) is higher than the allowed resolution " - "values. Fixed automatically"); - mMappingRes = hRes; - } - - params.resolution_meter = mMappingRes; - - float lRng = spMapPar.allowed_range.first; - float hRng = spMapPar.allowed_range.second; - - if (mMappingRangeMax < 0) - { - mMappingRangeMax = sl::SpatialMappingParameters::getRecommendedRange(mMappingRes, mZed); - RCLCPP_INFO_STREAM(get_logger(), "Mapping: max range set to " << mMappingRangeMax << " m for a resolution of " - << mMappingRes << " m"); - } - else if (mMappingRangeMax < lRng) - { - RCLCPP_WARN_STREAM(get_logger(), "'mapping.max_mapping_range_m' value (" << mMappingRangeMax - << " m) is lower than the allowed " - "resolution values. Fixed " - "automatically"); - mMappingRangeMax = lRng; - } - else if (mMappingRangeMax > hRng) - { - RCLCPP_WARN_STREAM(get_logger(), "'mapping.max_mapping_range_m' value (" << mMappingRangeMax - << " m) is higher than the allowed " - "resolution values. Fixed " - "automatically"); - mMappingRangeMax = hRng; - } - - params.range_meter = mMappingRangeMax; + if (mDepthDisabled) { + RCLCPP_WARN( + get_logger(), + "Cannot start 3D Mapping if `depth.quality` is set to `0` [NONE]"); + return false; + } + if (!mMappingEnabled) { + return false; + } + + RCLCPP_INFO_STREAM(get_logger(), "*** Starting Spatial Mapping ***"); + + sl::SpatialMappingParameters params; + params.map_type = sl::SpatialMappingParameters::SPATIAL_MAP_TYPE::FUSED_POINT_CLOUD; + params.use_chunk_only = true; + + sl::SpatialMappingParameters spMapPar; + + float lRes = spMapPar.allowed_resolution.first; + float hRes = spMapPar.allowed_resolution.second; + + if (mMappingRes < lRes) { + RCLCPP_WARN_STREAM(get_logger(), + "'mapping.resolution' value (" + << mMappingRes + << " m) is lower than the allowed resolution " + "values. Fixed automatically"); + mMappingRes = lRes; + } + if (mMappingRes > hRes) { + RCLCPP_WARN_STREAM(get_logger(), + "'mapping.resolution' value (" + << mMappingRes + << " m) is higher than the allowed resolution " + "values. Fixed automatically"); + mMappingRes = hRes; + } + + params.resolution_meter = mMappingRes; + + float lRng = spMapPar.allowed_range.first; + float hRng = spMapPar.allowed_range.second; + + if (mMappingRangeMax < 0) { + mMappingRangeMax = sl::SpatialMappingParameters::getRecommendedRange(mMappingRes, mZed); + RCLCPP_INFO_STREAM(get_logger(), + "Mapping: max range set to " << mMappingRangeMax + << " m for a resolution of " + << mMappingRes << " m"); + } else if (mMappingRangeMax < lRng) { + RCLCPP_WARN_STREAM(get_logger(), + "'mapping.max_mapping_range_m' value (" + << mMappingRangeMax + << " m) is lower than the allowed " + "resolution values. Fixed " + "automatically"); + mMappingRangeMax = lRng; + } else if (mMappingRangeMax > hRng) { + RCLCPP_WARN_STREAM(get_logger(), + "'mapping.max_mapping_range_m' value (" + << mMappingRangeMax + << " m) is higher than the allowed " + "resolution values. Fixed " + "automatically"); + mMappingRangeMax = hRng; + } + + params.range_meter = mMappingRangeMax; + + sl::ERROR_CODE err = mZed.enableSpatialMapping(params); + + if (err == sl::ERROR_CODE::SUCCESS) { + if (mPubFusedCloud == nullptr) { + ; + mPubFusedCloud = create_publisher( + mPointcloudFusedTopic, mMappingQos); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic " + << mPubFusedCloud->get_topic_name() << " @ " + << mFusedPcPubRate << " Hz"); + } - sl::ERROR_CODE err = mZed.enableSpatialMapping(params); + mMappingRunning = true; - if (err == sl::ERROR_CODE::SUCCESS) - { - if (mPubFusedCloud == nullptr) - { - ; - mPubFusedCloud = create_publisher(mPointcloudFusedTopic, mMappingQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic " << mPubFusedCloud->get_topic_name() << " @ " - << mFusedPcPubRate << " Hz"); - } + startFusedPcTimer(mFusedPcPubRate); - mMappingRunning = true; + RCLCPP_INFO_STREAM(get_logger(), + " * Resolution: " << params.resolution_meter << " m"); + RCLCPP_INFO_STREAM(get_logger(), + " * Max Mapping Range: " << params.range_meter << " m"); + RCLCPP_INFO_STREAM(get_logger(), + " * Map point cloud publishing rate: " << mFusedPcPubRate + << " Hz"); - startFusedPcTimer(mFusedPcPubRate); + return true; + } else { + mMappingRunning = false; + if (mFusedPcTimer) { + mFusedPcTimer->cancel(); + } - RCLCPP_INFO_STREAM(get_logger(), " * Resolution: " << params.resolution_meter << " m"); - RCLCPP_INFO_STREAM(get_logger(), " * Max Mapping Range: " << params.range_meter << " m"); - RCLCPP_INFO_STREAM(get_logger(), " * Map point cloud publishing rate: " << mFusedPcPubRate << " Hz"); + RCLCPP_WARN( + get_logger(), "Mapping not activated: %s", sl::toString(err).c_str()); - return true; - } - else - { - mMappingRunning = false; - if (mFusedPcTimer) - { - mFusedPcTimer->cancel(); + return false; } - - RCLCPP_WARN(get_logger(), "Mapping not activated: %s", sl::toString(err).c_str()); - - return false; - } } void ZedCamera::stop3dMapping() { - if (mFusedPcTimer) - { - mFusedPcTimer->cancel(); - } - mMappingRunning = false; - mMappingEnabled = false; - mZed.disableSpatialMapping(); + if (mFusedPcTimer) { + mFusedPcTimer->cancel(); + } + mMappingRunning = false; + mMappingEnabled = false; + mZed.disableSpatialMapping(); - RCLCPP_INFO(get_logger(), "*** Spatial Mapping stopped ***"); + RCLCPP_INFO(get_logger(), "*** Spatial Mapping stopped ***"); } bool ZedCamera::startObjDetect() { - if (!sl_tools::isZED2OrZED2i(mCamRealModel)) - { - RCLCPP_ERROR(get_logger(), "Object detection not started. The module is available only using a ZED2 and ZED2i " - "cameras"); - return false; - } + if (!sl_tools::isZED2OrZED2i(mCamRealModel)) { + RCLCPP_ERROR(get_logger(), + "Object detection not started. The module is " + "available only using a ZED2 and ZED2i " + "cameras"); + return false; + } - if (mDepthDisabled) - { - RCLCPP_WARN(get_logger(), "Cannot start Object Detection if `depth.quality` is set to `0` [NONE]"); - return false; - } + if (mDepthDisabled) { + RCLCPP_WARN(get_logger(), + "Cannot start Object Detection if " + "`depth.quality` is set to `0` [NONE]"); + return false; + } - if (!mObjDetEnabled) - { - return false; - } + if (!mObjDetEnabled) { + return false; + } - if (!mCamera2BaseTransfValid || !mSensor2CameraTransfValid || !mSensor2BaseTransfValid) - { - RCLCPP_DEBUG(get_logger(), "Tracking transforms not yet ready, OD starting postponed"); - return false; - } + if (!mCamera2BaseTransfValid || !mSensor2CameraTransfValid || !mSensor2BaseTransfValid) { + RCLCPP_DEBUG(get_logger(), + "Tracking transforms not yet ready, OD starting postponed"); + return false; + } - RCLCPP_INFO(get_logger(), "*** Starting Object Detection ***"); + RCLCPP_INFO(get_logger(), "*** Starting Object Detection ***"); - sl::ObjectDetectionParameters od_p; - od_p.enable_mask_output = false; - od_p.enable_tracking = mObjDetTracking; - od_p.image_sync = true; - od_p.detection_model = mObjDetModel; - od_p.enable_body_fitting = mBodyFitting; + sl::ObjectDetectionParameters od_p; + od_p.enable_mask_output = false; + od_p.enable_tracking = mObjDetTracking; + od_p.image_sync = true; + od_p.detection_model = mObjDetModel; + od_p.enable_body_fitting = mBodyFitting; - mObjDetFilter.clear(); - if (mObjDetPeopleEnable) - { - mObjDetFilter.push_back(sl::OBJECT_CLASS::PERSON); - } - if (mObjDetVehiclesEnable) - { - mObjDetFilter.push_back(sl::OBJECT_CLASS::VEHICLE); - } - if (mObjDetBagsEnable) - { - mObjDetFilter.push_back(sl::OBJECT_CLASS::BAG); - } - if (mObjDetAnimalsEnable) - { - mObjDetFilter.push_back(sl::OBJECT_CLASS::ANIMAL); - } - if (mObjDetElectronicsEnable) - { - mObjDetFilter.push_back(sl::OBJECT_CLASS::ELECTRONICS); - } - if (mObjDetFruitsEnable) - { - mObjDetFilter.push_back(sl::OBJECT_CLASS::FRUIT_VEGETABLE); - } + mObjDetFilter.clear(); + if (mObjDetPeopleEnable) { + mObjDetFilter.push_back(sl::OBJECT_CLASS::PERSON); + } + if (mObjDetVehiclesEnable) { + mObjDetFilter.push_back(sl::OBJECT_CLASS::VEHICLE); + } + if (mObjDetBagsEnable) { + mObjDetFilter.push_back(sl::OBJECT_CLASS::BAG); + } + if (mObjDetAnimalsEnable) { + mObjDetFilter.push_back(sl::OBJECT_CLASS::ANIMAL); + } + if (mObjDetElectronicsEnable) { + mObjDetFilter.push_back(sl::OBJECT_CLASS::ELECTRONICS); + } + if (mObjDetFruitsEnable) { + mObjDetFilter.push_back(sl::OBJECT_CLASS::FRUIT_VEGETABLE); + } - sl::ERROR_CODE objDetError = mZed.enableObjectDetection(od_p); + sl::ERROR_CODE objDetError = mZed.enableObjectDetection(od_p); - if (objDetError != sl::ERROR_CODE::SUCCESS) - { - RCLCPP_ERROR_STREAM(get_logger(), "Object detection error: " << sl::toString(objDetError)); + if (objDetError != sl::ERROR_CODE::SUCCESS) { + RCLCPP_ERROR_STREAM( + get_logger(), "Object detection error: " << sl::toString(objDetError)); - mObjDetRunning = false; - return false; - } + mObjDetRunning = false; + return false; + } - if (!mPubObjDet) - { - mPubObjDet = create_publisher(mObjectDetTopic, mObjDetQos); - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic " << mPubObjDet->get_topic_name()); - } + if (!mPubObjDet) { + mPubObjDet = create_publisher( + mObjectDetTopic, mObjDetQos); + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic " << mPubObjDet->get_topic_name()); + } - mObjDetRunning = true; - return true; + mObjDetRunning = true; + return true; } void ZedCamera::stopObjDetect() { - if (mObjDetRunning) - { - RCLCPP_INFO(get_logger(), "*** Stopping Object Detection ***"); - mObjDetRunning = false; - mObjDetEnabled = false; - mZed.disableObjectDetection(); + if (mObjDetRunning) { + RCLCPP_INFO(get_logger(), "*** Stopping Object Detection ***"); + mObjDetRunning = false; + mObjDetEnabled = false; + mZed.disableObjectDetection(); - // ----> Send an empty message to indicate that no more objects are tracked (e.g clean Rviz2) - objDetMsgPtr objMsg = std::make_unique(); + // ----> Send an empty message to indicate that no more objects are tracked + // (e.g clean Rviz2) + objDetMsgPtr objMsg = std::make_unique(); - objMsg->header.stamp = mFrameTimestamp; - objMsg->header.frame_id = mLeftCamFrameId; + objMsg->header.stamp = mFrameTimestamp; + objMsg->header.frame_id = mLeftCamFrameId; - objMsg->objects.clear(); + objMsg->objects.clear(); - mPubObjDet->publish(std::move(objMsg)); - // <---- Send an empty message to indicate that no more objects are tracked (e.g clean Rviz2) - } + mPubObjDet->publish(std::move(objMsg)); + // <---- Send an empty message to indicate that no more objects are tracked + // (e.g clean Rviz2) + } } bool ZedCamera::startSvoRecording(std::string& errMsg) { - sl::RecordingParameters params; + sl::RecordingParameters params; - params.bitrate = mSvoRecBitrate; - params.compression_mode = mSvoRecCompr; - params.target_framerate = mSvoRecFramerate; - params.transcode_streaming_input = mSvoRecTranscode; - params.video_filename = mSvoRecFilename.c_str(); + params.bitrate = mSvoRecBitrate; + params.compression_mode = mSvoRecCompr; + params.target_framerate = mSvoRecFramerate; + params.transcode_streaming_input = mSvoRecTranscode; + params.video_filename = mSvoRecFilename.c_str(); - sl::ERROR_CODE err = mZed.enableRecording(params); - errMsg = sl::toString(err); + sl::ERROR_CODE err = mZed.enableRecording(params); + errMsg = sl::toString(err); - if (err != sl::ERROR_CODE::SUCCESS) - { - RCLCPP_ERROR_STREAM(get_logger(), "Error starting SVO recording: " << errMsg); - return false; - } + if (err != sl::ERROR_CODE::SUCCESS) { + RCLCPP_ERROR_STREAM(get_logger(), + "Error starting SVO recording: " << errMsg); + return false; + } - mRecording = true; + mRecording = true; - return true; + return true; } void ZedCamera::stopSvoRecording() { - if (mRecording) - { - mRecording = false; - mZed.disableRecording(); - } + if (mRecording) { + mRecording = false; + mZed.disableRecording(); + } } void ZedCamera::initTransforms() { - // According to REP 105 -> http://www.ros.org/reps/rep-0105.html - - // base_link <- odom <- map - // ^ | - // | | - // ------------------- - - // ----> Dynamic transforms - mOdom2BaseTransf.setIdentity(); // broadcasted if `publish_tf` is true - mMap2OdomTransf.setIdentity(); // broadcasted if `publish_map_tf` is true - mMap2BaseTransf.setIdentity(); // used internally, but not broadcasted - // <---- Dynamic transforms + // According to REP 105 -> http://www.ros.org/reps/rep-0105.html + + // base_link <- odom <- map + // ^ | + // | | + // ------------------- + + // ----> Dynamic transforms + mOdom2BaseTransf.setIdentity(); // broadcasted if `publish_tf` is true + mMap2OdomTransf.setIdentity(); // broadcasted if `publish_map_tf` is true + mMap2BaseTransf.setIdentity(); // used internally, but not broadcasted + // <---- Dynamic transforms } bool ZedCamera::getCamera2BaseTransform() { - RCLCPP_DEBUG(get_logger(), "Getting static TF from '%s' to '%s'", mCameraFrameId.c_str(), mBaseFrameId.c_str()); - - mCamera2BaseTransfValid = false; - static bool first_error = true; - - // ----> Static transforms - // Sensor to Base link - try - { - // Save the transformation - geometry_msgs::msg::TransformStamped c2b = - mTfBuffer->lookupTransform(mCameraFrameId, mBaseFrameId, TIMEZERO_SYS, rclcpp::Duration(0.1)); - - // Get the TF2 transformation - // tf2::fromMsg(c2b.transform, mCamera2BaseTransf); - geometry_msgs::msg::Transform in = c2b.transform; - mCamera2BaseTransf.setOrigin(tf2::Vector3(in.translation.x, in.translation.y, in.translation.z)); - // w at the end in the constructor - mCamera2BaseTransf.setRotation(tf2::Quaternion(in.rotation.x, in.rotation.y, in.rotation.z, in.rotation.w)); - - double roll, pitch, yaw; - tf2::Matrix3x3(mCamera2BaseTransf.getRotation()).getRPY(roll, pitch, yaw); + RCLCPP_DEBUG(get_logger(), + "Getting static TF from '%s' to '%s'", + mCameraFrameId.c_str(), + mBaseFrameId.c_str()); + + mCamera2BaseTransfValid = false; + static bool first_error = true; + + // ----> Static transforms + // Sensor to Base link + try { + // Save the transformation + geometry_msgs::msg::TransformStamped c2b = mTfBuffer->lookupTransform( + mCameraFrameId, mBaseFrameId, TIMEZERO_SYS, rclcpp::Duration(0.1)); + + // Get the TF2 transformation + // tf2::fromMsg(c2b.transform, mCamera2BaseTransf); + geometry_msgs::msg::Transform in = c2b.transform; + mCamera2BaseTransf.setOrigin( + tf2::Vector3(in.translation.x, in.translation.y, in.translation.z)); + // w at the end in the constructor + mCamera2BaseTransf.setRotation(tf2::Quaternion( + in.rotation.x, in.rotation.y, in.rotation.z, in.rotation.w)); - RCLCPP_INFO(get_logger(), "Static transform Camera Center to Base [%s -> %s]", mCameraFrameId.c_str(), + double roll, pitch, yaw; + tf2::Matrix3x3(mCamera2BaseTransf.getRotation()).getRPY(roll, pitch, yaw); + + RCLCPP_INFO(get_logger(), + "Static transform Camera Center to Base [%s -> %s]", + mCameraFrameId.c_str(), + mBaseFrameId.c_str()); + RCLCPP_INFO(get_logger(), + " * Translation: {%.3f,%.3f,%.3f}", + mCamera2BaseTransf.getOrigin().x(), + mCamera2BaseTransf.getOrigin().y(), + mCamera2BaseTransf.getOrigin().z()); + RCLCPP_INFO(get_logger(), + " * Rotation: {%.3f,%.3f,%.3f}", + roll * RAD2DEG, + pitch * RAD2DEG, + yaw * RAD2DEG); + } catch (tf2::TransformException& ex) { + if (!first_error) { + rclcpp::Clock steady_clock(RCL_STEADY_TIME); + RCLCPP_DEBUG_THROTTLE( + get_logger(), steady_clock, 1.0, "Transform error: %s", ex.what()); + RCLCPP_WARN_THROTTLE(get_logger(), + steady_clock, + 1.0, + "The tf from '%s' to '%s' is not available.", + mCameraFrameId.c_str(), mBaseFrameId.c_str()); - RCLCPP_INFO(get_logger(), " * Translation: {%.3f,%.3f,%.3f}", mCamera2BaseTransf.getOrigin().x(), - mCamera2BaseTransf.getOrigin().y(), mCamera2BaseTransf.getOrigin().z()); - RCLCPP_INFO(get_logger(), " * Rotation: {%.3f,%.3f,%.3f}", roll * RAD2DEG, pitch * RAD2DEG, yaw * RAD2DEG); - } - catch (tf2::TransformException& ex) - { - if (!first_error) - { - rclcpp::Clock steady_clock(RCL_STEADY_TIME); - RCLCPP_DEBUG_THROTTLE(get_logger(), steady_clock, 1.0, "Transform error: %s", ex.what()); - RCLCPP_WARN_THROTTLE(get_logger(), steady_clock, 1.0, "The tf from '%s' to '%s' is not available.", - mCameraFrameId.c_str(), mBaseFrameId.c_str()); - RCLCPP_WARN_THROTTLE(get_logger(), steady_clock, 1.0, - "Note: one of the possible cause of the problem is the absense of an instance " - "of the `robot_state_publisher` node publishing the correct static TF transformations " - "or a modified URDF not correctly reproducing the ZED " - "TF chain '%s' -> '%s' -> '%s'", - mBaseFrameId.c_str(), mCameraFrameId.c_str(), mDepthFrameId.c_str()); - first_error = false; - } - - mCamera2BaseTransf.setIdentity(); - return false; - } + RCLCPP_WARN_THROTTLE( + get_logger(), + steady_clock, + 1.0, + "Note: one of the possible cause of the problem is the absense of an " + "instance " + "of the `robot_state_publisher` node publishing the correct static " + "TF transformations " + "or a modified URDF not correctly reproducing the ZED " + "TF chain '%s' -> '%s' -> '%s'", + mBaseFrameId.c_str(), + mCameraFrameId.c_str(), + mDepthFrameId.c_str()); + first_error = false; + } + + mCamera2BaseTransf.setIdentity(); + return false; + } - // <---- Static transforms - mCamera2BaseTransfValid = true; - return true; + // <---- Static transforms + mCamera2BaseTransfValid = true; + return true; } bool ZedCamera::getSens2CameraTransform() { - RCLCPP_DEBUG(get_logger(), "Getting static TF from '%s' to '%s'", mDepthFrameId.c_str(), mCameraFrameId.c_str()); - - mSensor2CameraTransfValid = false; - - static bool first_error = true; + RCLCPP_DEBUG(get_logger(), + "Getting static TF from '%s' to '%s'", + mDepthFrameId.c_str(), + mCameraFrameId.c_str()); + + mSensor2CameraTransfValid = false; + + static bool first_error = true; + + // ----> Static transforms + // Sensor to Camera Center + try { + // Save the transformation + geometry_msgs::msg::TransformStamped s2c = mTfBuffer->lookupTransform( + mDepthFrameId, mCameraFrameId, TIMEZERO_SYS, rclcpp::Duration(0.1)); + + // Get the TF2 transformation + // tf2::fromMsg(s2c.transform, mSensor2CameraTransf); + geometry_msgs::msg::Transform in = s2c.transform; + mSensor2CameraTransf.setOrigin( + tf2::Vector3(in.translation.x, in.translation.y, in.translation.z)); + // w at the end in the constructor + mSensor2CameraTransf.setRotation(tf2::Quaternion( + in.rotation.x, in.rotation.y, in.rotation.z, in.rotation.w)); - // ----> Static transforms - // Sensor to Camera Center - try - { - // Save the transformation - geometry_msgs::msg::TransformStamped s2c = - mTfBuffer->lookupTransform(mDepthFrameId, mCameraFrameId, TIMEZERO_SYS, rclcpp::Duration(0.1)); - - // Get the TF2 transformation - // tf2::fromMsg(s2c.transform, mSensor2CameraTransf); - geometry_msgs::msg::Transform in = s2c.transform; - mSensor2CameraTransf.setOrigin(tf2::Vector3(in.translation.x, in.translation.y, in.translation.z)); - // w at the end in the constructor - mSensor2CameraTransf.setRotation(tf2::Quaternion(in.rotation.x, in.rotation.y, in.rotation.z, in.rotation.w)); - - double roll, pitch, yaw; - tf2::Matrix3x3(mSensor2CameraTransf.getRotation()).getRPY(roll, pitch, yaw); - - RCLCPP_INFO(get_logger(), "Static transform Sensor to Camera Center [%s -> %s]", mDepthFrameId.c_str(), + double roll, pitch, yaw; + tf2::Matrix3x3(mSensor2CameraTransf.getRotation()).getRPY(roll, pitch, yaw); + + RCLCPP_INFO(get_logger(), + "Static transform Sensor to Camera Center [%s -> %s]", + mDepthFrameId.c_str(), + mCameraFrameId.c_str()); + RCLCPP_INFO(get_logger(), + " * Translation: {%.3f,%.3f,%.3f}", + mSensor2CameraTransf.getOrigin().x(), + mSensor2CameraTransf.getOrigin().y(), + mSensor2CameraTransf.getOrigin().z()); + RCLCPP_INFO(get_logger(), + " * Rotation: {%.3f,%.3f,%.3f}", + roll * RAD2DEG, + pitch * RAD2DEG, + yaw * RAD2DEG); + } catch (tf2::TransformException& ex) { + if (!first_error) { + rclcpp::Clock steady_clock(RCL_STEADY_TIME); + RCLCPP_DEBUG_THROTTLE( + get_logger(), steady_clock, 1.0, "Transform error: %s", ex.what()); + RCLCPP_WARN_THROTTLE(get_logger(), + steady_clock, + 1.0, + "The tf from '%s' to '%s' is not available.", + mDepthFrameId.c_str(), mCameraFrameId.c_str()); - RCLCPP_INFO(get_logger(), " * Translation: {%.3f,%.3f,%.3f}", mSensor2CameraTransf.getOrigin().x(), - mSensor2CameraTransf.getOrigin().y(), mSensor2CameraTransf.getOrigin().z()); - RCLCPP_INFO(get_logger(), " * Rotation: {%.3f,%.3f,%.3f}", roll * RAD2DEG, pitch * RAD2DEG, yaw * RAD2DEG); - } - catch (tf2::TransformException& ex) - { - if (!first_error) - { - rclcpp::Clock steady_clock(RCL_STEADY_TIME); - RCLCPP_DEBUG_THROTTLE(get_logger(), steady_clock, 1.0, "Transform error: %s", ex.what()); - RCLCPP_WARN_THROTTLE(get_logger(), steady_clock, 1.0, "The tf from '%s' to '%s' is not available.", - mDepthFrameId.c_str(), mCameraFrameId.c_str()); - RCLCPP_WARN_THROTTLE(get_logger(), steady_clock, 1.0, - "Note: one of the possible cause of the problem is the absense of an instance " - "of the `robot_state_publisher` node publishing the correct static TF transformations " - "or a modified URDF not correctly reproducing the ZED " - "TF chain '%s' -> '%s' -> '%s'", - mBaseFrameId.c_str(), mCameraFrameId.c_str(), mDepthFrameId.c_str()); - first_error = false; - } - - mSensor2CameraTransf.setIdentity(); - return false; - } + RCLCPP_WARN_THROTTLE( + get_logger(), + steady_clock, + 1.0, + "Note: one of the possible cause of the problem is the absense of an " + "instance " + "of the `robot_state_publisher` node publishing the correct static " + "TF transformations " + "or a modified URDF not correctly reproducing the ZED " + "TF chain '%s' -> '%s' -> '%s'", + mBaseFrameId.c_str(), + mCameraFrameId.c_str(), + mDepthFrameId.c_str()); + first_error = false; + } - // <---- Static transforms + mSensor2CameraTransf.setIdentity(); + return false; + } + + // <---- Static transforms - mSensor2CameraTransfValid = true; - return true; + mSensor2CameraTransfValid = true; + return true; } bool ZedCamera::getSens2BaseTransform() { - RCLCPP_DEBUG(get_logger(), "Getting static TF from '%s' to '%s'", mDepthFrameId.c_str(), mBaseFrameId.c_str()); - - mSensor2BaseTransfValid = false; - static bool first_error = true; + RCLCPP_DEBUG(get_logger(), + "Getting static TF from '%s' to '%s'", + mDepthFrameId.c_str(), + mBaseFrameId.c_str()); + + mSensor2BaseTransfValid = false; + static bool first_error = true; + + // ----> Static transforms + // Sensor to Base link + try { + // Save the transformation + geometry_msgs::msg::TransformStamped s2b = mTfBuffer->lookupTransform( + mDepthFrameId, mBaseFrameId, TIMEZERO_SYS, rclcpp::Duration(0.1)); + + // Get the TF2 transformation + // tf2::fromMsg(s2b.transform, mSensor2BaseTransf); + geometry_msgs::msg::Transform in = s2b.transform; + mSensor2BaseTransf.setOrigin( + tf2::Vector3(in.translation.x, in.translation.y, in.translation.z)); + // w at the end in the constructor + mSensor2BaseTransf.setRotation(tf2::Quaternion( + in.rotation.x, in.rotation.y, in.rotation.z, in.rotation.w)); - // ----> Static transforms - // Sensor to Base link - try - { - // Save the transformation - geometry_msgs::msg::TransformStamped s2b = - mTfBuffer->lookupTransform(mDepthFrameId, mBaseFrameId, TIMEZERO_SYS, rclcpp::Duration(0.1)); - - // Get the TF2 transformation - // tf2::fromMsg(s2b.transform, mSensor2BaseTransf); - geometry_msgs::msg::Transform in = s2b.transform; - mSensor2BaseTransf.setOrigin(tf2::Vector3(in.translation.x, in.translation.y, in.translation.z)); - // w at the end in the constructor - mSensor2BaseTransf.setRotation(tf2::Quaternion(in.rotation.x, in.rotation.y, in.rotation.z, in.rotation.w)); - - double roll, pitch, yaw; - tf2::Matrix3x3(mSensor2BaseTransf.getRotation()).getRPY(roll, pitch, yaw); - - RCLCPP_INFO(get_logger(), "Static transform Sensor to Base [%s -> %s]", mDepthFrameId.c_str(), + double roll, pitch, yaw; + tf2::Matrix3x3(mSensor2BaseTransf.getRotation()).getRPY(roll, pitch, yaw); + + RCLCPP_INFO(get_logger(), + "Static transform Sensor to Base [%s -> %s]", + mDepthFrameId.c_str(), + mBaseFrameId.c_str()); + RCLCPP_INFO(get_logger(), + " * Translation: {%.3f,%.3f,%.3f}", + mSensor2BaseTransf.getOrigin().x(), + mSensor2BaseTransf.getOrigin().y(), + mSensor2BaseTransf.getOrigin().z()); + RCLCPP_INFO(get_logger(), + " * Rotation: {%.3f,%.3f,%.3f}", + roll * RAD2DEG, + pitch * RAD2DEG, + yaw * RAD2DEG); + } catch (tf2::TransformException& ex) { + if (!first_error) { + rclcpp::Clock steady_clock(RCL_STEADY_TIME); + RCLCPP_DEBUG_THROTTLE( + get_logger(), steady_clock, 1.0, "Transform error: %s", ex.what()); + RCLCPP_WARN_THROTTLE(get_logger(), + steady_clock, + 1.0, + "The tf from '%s' to '%s' is not available.", + mDepthFrameId.c_str(), mBaseFrameId.c_str()); - RCLCPP_INFO(get_logger(), " * Translation: {%.3f,%.3f,%.3f}", mSensor2BaseTransf.getOrigin().x(), - mSensor2BaseTransf.getOrigin().y(), mSensor2BaseTransf.getOrigin().z()); - RCLCPP_INFO(get_logger(), " * Rotation: {%.3f,%.3f,%.3f}", roll * RAD2DEG, pitch * RAD2DEG, yaw * RAD2DEG); - } - catch (tf2::TransformException& ex) - { - if (!first_error) - { - rclcpp::Clock steady_clock(RCL_STEADY_TIME); - RCLCPP_DEBUG_THROTTLE(get_logger(), steady_clock, 1.0, "Transform error: %s", ex.what()); - RCLCPP_WARN_THROTTLE(get_logger(), steady_clock, 1.0, "The tf from '%s' to '%s' is not available.", - mDepthFrameId.c_str(), mBaseFrameId.c_str()); - RCLCPP_WARN_THROTTLE(get_logger(), steady_clock, 1.0, - "Note: one of the possible cause of the problem is the absense of an instance " - "of the `robot_state_publisher` node publishing the correct static TF transformations " - "or a modified URDF not correctly reproducing the ZED " - "TF chain '%s' -> '%s' -> '%s'", - mBaseFrameId.c_str(), mCameraFrameId.c_str(), mDepthFrameId.c_str()); - first_error = false; - } - - mSensor2BaseTransf.setIdentity(); - return false; - } - - // <---- Static transforms - - mSensor2BaseTransfValid = true; - return true; -} - -bool ZedCamera::setPose(float xt, float yt, float zt, float rr, float pr, float yr) -{ - initTransforms(); - - if (!mSensor2BaseTransfValid) - { - getSens2BaseTransform(); - } + RCLCPP_WARN_THROTTLE( + get_logger(), + steady_clock, + 1.0, + "Note: one of the possible cause of the problem is the absense of an " + "instance " + "of the `robot_state_publisher` node publishing the correct static " + "TF transformations " + "or a modified URDF not correctly reproducing the ZED " + "TF chain '%s' -> '%s' -> '%s'", + mBaseFrameId.c_str(), + mCameraFrameId.c_str(), + mDepthFrameId.c_str()); + first_error = false; + } - if (!mSensor2CameraTransfValid) - { - getSens2CameraTransform(); - } + mSensor2BaseTransf.setIdentity(); + return false; + } - if (!mCamera2BaseTransfValid) - { - getCamera2BaseTransform(); - } + // <---- Static transforms - // Apply Base to sensor transform - tf2::Transform initPose; - tf2::Vector3 origin(xt, yt, zt); - initPose.setOrigin(origin); - tf2::Quaternion quat; - quat.setRPY(rr, pr, yr); - initPose.setRotation(quat); - - initPose = initPose * mSensor2BaseTransf.inverse(); // TODO Check this transformation. Rotation seems wrong - - // SL pose - sl::float3 t_vec; - t_vec[0] = initPose.getOrigin().x(); - t_vec[1] = initPose.getOrigin().y(); - t_vec[2] = initPose.getOrigin().z(); - - sl::float4 q_vec; - q_vec[0] = initPose.getRotation().x(); - q_vec[1] = initPose.getRotation().y(); - q_vec[2] = initPose.getRotation().z(); - q_vec[3] = initPose.getRotation().w(); - - sl::Translation trasl(t_vec); - sl::Orientation orient(q_vec); - mInitialPoseSl.setTranslation(trasl); - mInitialPoseSl.setOrientation(orient); - - return (mSensor2BaseTransfValid & mSensor2CameraTransfValid & mCamera2BaseTransfValid); + mSensor2BaseTransfValid = true; + return true; } -void ZedCamera::publishStaticImuFrameAndTopic() +bool ZedCamera::setPose(float xt, float yt, float zt, float rr, float pr, float yr) { - sl::Orientation sl_rot = mSlCamImuTransf.getOrientation(); - sl::Translation sl_tr = mSlCamImuTransf.getTranslation(); - - mCameraImuTransfMgs = std::make_shared(); - - mCameraImuTransfMgs->header.stamp = get_clock()->now(); - - mCameraImuTransfMgs->header.frame_id = mLeftCamFrameId; - mCameraImuTransfMgs->child_frame_id = mImuFrameId; + initTransforms(); - mCameraImuTransfMgs->transform.rotation.x = sl_rot.ox; - mCameraImuTransfMgs->transform.rotation.y = sl_rot.oy; - mCameraImuTransfMgs->transform.rotation.z = sl_rot.oz; - mCameraImuTransfMgs->transform.rotation.w = sl_rot.ow; - - mCameraImuTransfMgs->transform.translation.x = sl_tr.x; - mCameraImuTransfMgs->transform.translation.y = sl_tr.y; - mCameraImuTransfMgs->transform.translation.z = sl_tr.z; - - if (!mStaticImuTopicPublished) - { - rclcpp::QoS transf_qos = mSensQos; - transf_qos.durability(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL); // Latched topic - transf_qos.keep_last(1); - std::string cam_imu_tr_topic = mTopicRoot + "left_cam_imu_transform"; - mPubCamImuTransf = create_publisher(cam_imu_tr_topic, transf_qos); + if (!mSensor2BaseTransfValid) { + getSens2BaseTransform(); + } - mPubCamImuTransf->publish(*(mCameraImuTransfMgs.get())); + if (!mSensor2CameraTransfValid) { + getSens2CameraTransform(); + } - RCLCPP_INFO_STREAM(get_logger(), "Advertised on topic: " << mPubCamImuTransf->get_topic_name() << " [LATCHED]"); - RCLCPP_INFO(get_logger(), "Camera-IMU Translation: \n %g %g %g", sl_tr.x, sl_tr.y, sl_tr.z); - RCLCPP_INFO(get_logger(), "Camera-IMU Rotation: \n %s", sl_rot.getRotationMatrix().getInfos().c_str()); + if (!mCamera2BaseTransfValid) { + getCamera2BaseTransform(); + } - mStaticImuTopicPublished = true; - } + // Apply Base to sensor transform + tf2::Transform initPose; + tf2::Vector3 origin(xt, yt, zt); + initPose.setOrigin(origin); + tf2::Quaternion quat; + quat.setRPY(rr, pr, yr); + initPose.setRotation(quat); - // Publish IMU TF as static TF - if (!mPublishImuTF) - { - return; - } + initPose = initPose * mSensor2BaseTransf.inverse(); // TODO Check this transformation. Rotation seems wrong - if (mStaticImuFramePublished) - { - return; - } + // SL pose + sl::float3 t_vec; + t_vec[0] = initPose.getOrigin().x(); + t_vec[1] = initPose.getOrigin().y(); + t_vec[2] = initPose.getOrigin().z(); - // Publish transformation - mStaticTfBroadcaster->sendTransform(*(mCameraImuTransfMgs.get())); + sl::float4 q_vec; + q_vec[0] = initPose.getRotation().x(); + q_vec[1] = initPose.getRotation().y(); + q_vec[2] = initPose.getRotation().z(); + q_vec[3] = initPose.getRotation().w(); - RCLCPP_INFO_STREAM(get_logger(), "Published static TF: '" << mImuFrameId << "' -> '" << mLeftCamFrameId << "'"); + sl::Translation trasl(t_vec); + sl::Orientation orient(q_vec); + mInitialPoseSl.setTranslation(trasl); + mInitialPoseSl.setOrientation(orient); - mStaticImuFramePublished = true; + return (mSensor2BaseTransfValid & mSensor2CameraTransfValid & mCamera2BaseTransfValid); } -void ZedCamera::threadFunc_zedGrab() +void ZedCamera::publishStaticImuFrameAndTopic() { - RCLCPP_DEBUG(get_logger(), "Grab thread started"); + sl::Orientation sl_rot = mSlCamImuTransf.getOrientation(); + sl::Translation sl_tr = mSlCamImuTransf.getTranslation(); - mFrameCount = 0; - mRgbDepthDataRetrieved = true; // Force first grab + mCameraImuTransfMgs = std::make_shared(); - // ----> Grab Runtime parameters - mRunParams.sensing_mode = static_cast(mDepthSensingMode); - mRunParams.enable_depth = false; - mRunParams.measure3D_reference_frame = sl::REFERENCE_FRAME::CAMERA; - // <---- Grab Runtime parameters + mCameraImuTransfMgs->header.stamp = get_clock()->now(); - // Infinite grab thread - while (1) - { - // ----> Interruption check - if (!rclcpp::ok()) - { - RCLCPP_DEBUG(get_logger(), "Ctrl+C received: stopping grab thread"); - break; - } + mCameraImuTransfMgs->header.frame_id = mLeftCamFrameId; + mCameraImuTransfMgs->child_frame_id = mImuFrameId; - if (mThreadStop) - { - RCLCPP_DEBUG(get_logger(), "Grab thread stopped"); - break; - } - // <---- Interruption check + mCameraImuTransfMgs->transform.rotation.x = sl_rot.ox; + mCameraImuTransfMgs->transform.rotation.y = sl_rot.oy; + mCameraImuTransfMgs->transform.rotation.z = sl_rot.oz; + mCameraImuTransfMgs->transform.rotation.w = sl_rot.ow; - // Wait for operations on Positional Tracking - std::lock_guard lock(mPosTrkMutex); + mCameraImuTransfMgs->transform.translation.x = sl_tr.x; + mCameraImuTransfMgs->transform.translation.y = sl_tr.y; + mCameraImuTransfMgs->transform.translation.z = sl_tr.z; - // ----> Apply depth settings - applyDepthSettings(); - // <---- Apply depth settings + if (!mStaticImuTopicPublished) { + rclcpp::QoS transf_qos = mSensQos; + transf_qos.durability( + RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL); // Latched topic + transf_qos.keep_last(1); + std::string cam_imu_tr_topic = mTopicRoot + "left_cam_imu_transform"; + mPubCamImuTransf = create_publisher( + cam_imu_tr_topic, transf_qos); - // ----> Apply video dynamic parameters - applyVideoSettings(); - // <---- Apply video dynamic parameters + mPubCamImuTransf->publish(*(mCameraImuTransfMgs.get())); - // ----> Check for Positional Tracking requirement - if (isPosTrackingRequired() && !mPosTrackingStarted) - { - startPosTracking(); - } - // ----> Check for Positional Tracking requirement + RCLCPP_INFO_STREAM(get_logger(), + "Advertised on topic: " + << mPubCamImuTransf->get_topic_name() << " [LATCHED]"); + RCLCPP_INFO(get_logger(), + "Camera-IMU Translation: \n %g %g %g", + sl_tr.x, + sl_tr.y, + sl_tr.z); + RCLCPP_INFO(get_logger(), + "Camera-IMU Rotation: \n %s", + sl_rot.getRotationMatrix().getInfos().c_str()); - // ----> Check for Spatial Mapping requirement - if (!mDepthDisabled) - { - mMappingMutex.lock(); - if (mMappingEnabled && !mMappingRunning) - { - start3dMapping(); - } - mMappingMutex.unlock(); + mStaticImuTopicPublished = true; } - // <---- Check for Spatial Mapping requirement - // ----> Check for Object Detection requirement - if (!mDepthDisabled) - { - mObjDetMutex.lock(); - if (mObjDetEnabled && !mObjDetRunning) - { - startObjDetect(); - if (!sl_tools::isZED2OrZED2i(mCamRealModel)) - { - mObjDetEnabled = false; - } - } - mObjDetMutex.unlock(); + // Publish IMU TF as static TF + if (!mPublishImuTF) { + return; } - // ----> Check for Object Detection requirement - // ----> Wait for RGB/Depth synchronization before grabbing - std::unique_lock datalock(mCamDataMutex); - while (!mRgbDepthDataRetrieved) - { // loop to avoid spurious wakeups - if (mRgbDepthDataRetrievedCondVar.wait_for(datalock, std::chrono::milliseconds(500)) == std::cv_status::timeout) - { - // Check thread stopping - if (mThreadStop) - { - return; - } - else - { - continue; - } - } + if (mStaticImuFramePublished) { + return; } - mRgbDepthDataRetrieved = false; - // <---- Wait for RGB/Depth synchronization before grabbing - // ----> Grab freq calculation - static sl_tools::StopWatch freqTimer; + // Publish transformation + mStaticTfBroadcaster->sendTransform(*(mCameraImuTransfMgs.get())); - double elapsed_sec = freqTimer.toc(); - mGrabPeriodMean_sec->addValue(elapsed_sec); - freqTimer.tic(); + RCLCPP_INFO_STREAM(get_logger(), + "Published static TF: '" << mImuFrameId << "' -> '" + << mLeftCamFrameId << "'"); - // RCLCPP_INFO_STREAM( get_logger(), "Grab period: " << mGrabPeriodMean_sec->getMean()/1e6 << - // " Freq: " << 1e6/mGrabPeriodMean_usec->getMean() ); - // <---- Grab freq calculation + mStaticImuFramePublished = true; +} - if (mSvoMode && !mSvoRealtime) - { - static bool first = true; +void ZedCamera::threadFunc_zedGrab() +{ + RCLCPP_DEBUG(get_logger(), "Grab thread started"); - double grab_period_usec = (1. / mPubFrameRate) * 1e6; - int64_t residual_period_usec = grab_period_usec - (elapsed_sec * 1e6); + mFrameCount = 0; + mRgbDepthDataRetrieved = true; // Force first grab - if (residual_period_usec > 0) - { - if (first) - { - first = false; - } - else - { - // RCLCPP_DEBUG_STREAM(get_logger(), "Sleeping for " << residual_period_usec << "usec" ); - std::this_thread::sleep_for(std::chrono::microseconds(residual_period_usec)); + // ----> Grab Runtime parameters + mRunParams.sensing_mode = static_cast(mDepthSensingMode); + mRunParams.enable_depth = false; + mRunParams.measure3D_reference_frame = sl::REFERENCE_FRAME::CAMERA; + // <---- Grab Runtime parameters + + // Infinite grab thread + while (1) { + // ----> Interruption check + if (!rclcpp::ok()) { + RCLCPP_DEBUG(get_logger(), "Ctrl+C received: stopping grab thread"); + break; } - } - } - - sl_tools::StopWatch elabTimer; - if (!mSvoPause) - { - // ZED grab - mGrabStatus = mZed.grab(mRunParams); - - // ----> Check SVO status - if (mSvoMode && mGrabStatus == sl::ERROR_CODE::END_OF_SVOFILE_REACHED) - { - if (mSvoLoop) - { - mZed.setSVOPosition(0); - RCLCPP_WARN(get_logger(), "SVO reached the end and has been restarted."); - std::this_thread::sleep_for(std::chrono::milliseconds(static_cast(mGrabPeriodMean_sec->getMean()*1000.f))); - continue; - } - else - { - RCLCPP_WARN(get_logger(), "SVO reached the end. The node has been stopped."); - break; + if (mThreadStop) { + RCLCPP_DEBUG(get_logger(), "Grab thread stopped"); + break; } - } - // <---- Check SVO status + // <---- Interruption check - // ----> Grab errors? - // Note: disconnection are automatically handled by the ZED SDK - if (mGrabStatus != sl::ERROR_CODE::SUCCESS) - { - RCLCPP_ERROR_STREAM(get_logger(), "Camera error: " << sl::toString(mGrabStatus).c_str()); - break; // TODO verify what to do in case of grab errors - } - // <---- Grab errors? - - mFrameCount++; + // Wait for operations on Positional Tracking + std::lock_guard lock(mPosTrkMutex); - // ----> Timestamp - if (mSvoMode) - { - mFrameTimestamp = sl_tools::slTime2Ros(mZed.getTimestamp(sl::TIME_REFERENCE::CURRENT)); - } - else - { - mFrameTimestamp = sl_tools::slTime2Ros(mZed.getTimestamp(sl::TIME_REFERENCE::IMAGE)); - } - // <---- Timestamp + // ----> Apply depth settings + applyDepthSettings(); + // <---- Apply depth settings - // ----> Check recording status - mRecMutex.lock(); - if (mRecording) - { - mRecStatus = mZed.getRecordingStatus(); + // ----> Apply video dynamic parameters + applyVideoSettings(); + // <---- Apply video dynamic parameters - if (!mRecStatus.status) - { - rclcpp::Clock steady_clock(RCL_STEADY_TIME); - RCLCPP_ERROR_THROTTLE(get_logger(), steady_clock, 1.0, "Error saving frame to SVO"); + // ----> Check for Positional Tracking requirement + if (isPosTrackingRequired() && !mPosTrackingStarted) { + startPosTracking(); + } + // ----> Check for Positional Tracking requirement + + // ----> Check for Spatial Mapping requirement + if (!mDepthDisabled) { + mMappingMutex.lock(); + if (mMappingEnabled && !mMappingRunning) { + start3dMapping(); + } + mMappingMutex.unlock(); + } + // <---- Check for Spatial Mapping requirement + + // ----> Check for Object Detection requirement + if (!mDepthDisabled) { + mObjDetMutex.lock(); + if (mObjDetEnabled && !mObjDetRunning) { + startObjDetect(); + if (!sl_tools::isZED2OrZED2i(mCamRealModel)) { + mObjDetEnabled = false; + } + } + mObjDetMutex.unlock(); + } + // ----> Check for Object Detection requirement + + // ----> Wait for RGB/Depth synchronization before grabbing + std::unique_lock datalock(mCamDataMutex); + while (!mRgbDepthDataRetrieved) { // loop to avoid spurious wakeups + if (mRgbDepthDataRetrievedCondVar.wait_for( + datalock, std::chrono::milliseconds(500)) + == std::cv_status::timeout) { + // Check thread stopping + if (mThreadStop) { + return; + } else { + continue; + } + } + } + mRgbDepthDataRetrieved = false; + // <---- Wait for RGB/Depth synchronization before grabbing + + // ----> Grab freq calculation + static sl_tools::StopWatch freqTimer; + + double elapsed_sec = freqTimer.toc(); + mGrabPeriodMean_sec->addValue(elapsed_sec); + freqTimer.tic(); + + // RCLCPP_INFO_STREAM( get_logger(), "Grab period: " << + // mGrabPeriodMean_sec->getMean()/1e6 << + // " Freq: " << + // 1e6/mGrabPeriodMean_usec->getMean() ); + // <---- Grab freq calculation + + if (mSvoMode && !mSvoRealtime) { + static bool first = true; + + double grab_period_usec = (1. / mPubFrameRate) * 1e6; + int64_t residual_period_usec = grab_period_usec - (elapsed_sec * 1e6); + + if (residual_period_usec > 0) { + if (first) { + first = false; + } else { + // RCLCPP_DEBUG_STREAM(get_logger(), "Sleeping for " << + // residual_period_usec << "usec" ); + std::this_thread::sleep_for( + std::chrono::microseconds(residual_period_usec)); + } + } } - // TODO update disgnostic - // mDiagUpdater.force_update(); - } - mRecMutex.unlock(); - // <---- Check recording status - } - - if (!mDepthDisabled) - { - if (mPosTrackingStarted) - { - if (!mSvoPause) - { - processOdometry(); - processPose(); + sl_tools::StopWatch elabTimer; + + if (!mSvoPause) { + // ZED grab + mGrabStatus = mZed.grab(mRunParams); + + // ----> Check SVO status + if (mSvoMode && mGrabStatus == sl::ERROR_CODE::END_OF_SVOFILE_REACHED) { + if (mSvoLoop) { + mZed.setSVOPosition(0); + RCLCPP_WARN(get_logger(), + "SVO reached the end and has been restarted."); + std::this_thread::sleep_for(std::chrono::milliseconds( + static_cast(mGrabPeriodMean_sec->getMean() * 1000.f))); + continue; + } else { + RCLCPP_WARN(get_logger(), + "SVO reached the end. The node has been stopped."); + break; + } + } + // <---- Check SVO status + + // ----> Grab errors? + // Note: disconnection are automatically handled by the ZED SDK + if (mGrabStatus != sl::ERROR_CODE::SUCCESS) { + RCLCPP_ERROR_STREAM( + get_logger(), "Camera error: " << sl::toString(mGrabStatus).c_str()); + break; // TODO verify what to do in case of grab errors + } + // <---- Grab errors? + + mFrameCount++; + + // ----> Timestamp + if (mSvoMode) { + mFrameTimestamp = sl_tools::slTime2Ros(mZed.getTimestamp(sl::TIME_REFERENCE::CURRENT)); + } else { + mFrameTimestamp = sl_tools::slTime2Ros(mZed.getTimestamp(sl::TIME_REFERENCE::IMAGE)); + } + // <---- Timestamp + + // ----> Check recording status + mRecMutex.lock(); + if (mRecording) { + mRecStatus = mZed.getRecordingStatus(); + + if (!mRecStatus.status) { + rclcpp::Clock steady_clock(RCL_STEADY_TIME); + RCLCPP_ERROR_THROTTLE( + get_logger(), steady_clock, 1.0, "Error saving frame to SVO"); + } + + // TODO update disgnostic + // mDiagUpdater.force_update(); + } + mRecMutex.unlock(); + // <---- Check recording status } - if (mCamRealModel == sl::MODEL::ZED || !mPublishImuTF || mSvoMode) - { - publishTFs(mFrameTimestamp); + if (!mDepthDisabled) { + if (mPosTrackingStarted) { + if (!mSvoPause) { + processOdometry(); + processPose(); + } + + if (mCamRealModel == sl::MODEL::ZED || !mPublishImuTF || mSvoMode) { + publishTFs(mFrameTimestamp); + } + } } - } - } - // Publish the point cloud if someone has subscribed to - if (!mDepthDisabled) - { - size_t cloudSubnumber = 0; - try - { - cloudSubnumber = count_subscribers(mPubCloud->get_topic_name()); - } - catch (...) - { - rcutils_reset_error(); - RCLCPP_DEBUG(get_logger(), "threadFunc_zedGrab: Exception while counting point cloud subscribers"); - return; - } + // Publish the point cloud if someone has subscribed to + if (!mDepthDisabled) { + size_t cloudSubnumber = 0; + try { + cloudSubnumber = count_subscribers(mPubCloud->get_topic_name()); + } catch (...) { + rcutils_reset_error(); + RCLCPP_DEBUG(get_logger(), + "threadFunc_zedGrab: Exception while " + "counting point cloud subscribers"); + return; + } + + if (cloudSubnumber > 0) { + // Run the point cloud conversion asynchronously to avoid slowing down + // all the program + // Retrieve raw pointCloud data if latest Pointcloud is ready + std::unique_lock lock(mPcMutex, std::defer_lock); + + if (lock.try_lock()) { + mZed.retrieveMeasure( + mMatCloud, sl::MEASURE::XYZBGRA, sl::MEM::CPU, mMatResolDepth); + + // Signal Pointcloud thread that a new pointcloud is ready + mPcDataReadyCondVar.notify_one(); + mPcDataReady = true; + mPcPublishing = true; + } + } else { + mPcPublishing = false; + } + } - if (cloudSubnumber > 0) - { - // Run the point cloud conversion asynchronously to avoid slowing down - // all the program - // Retrieve raw pointCloud data if latest Pointcloud is ready - std::unique_lock lock(mPcMutex, std::defer_lock); - - if (lock.try_lock()) - { - mZed.retrieveMeasure(mMatCloud, sl::MEASURE::XYZBGRA, sl::MEM::CPU, mMatResolDepth); - - // Signal Pointcloud thread that a new pointcloud is ready - mPcDataReadyCondVar.notify_one(); - mPcDataReady = true; - mPcPublishing = true; + if (!mDepthDisabled) { + mObjDetMutex.lock(); + if (mObjDetRunning) { + processDetectedObjects(mFrameTimestamp); + } + mObjDetMutex.unlock(); } - } - else - { - mPcPublishing = false; - } - } - if (!mDepthDisabled) - { - mObjDetMutex.lock(); - if (mObjDetRunning) - { - processDetectedObjects(mFrameTimestamp); - } - mObjDetMutex.unlock(); + // Diagnostic statistics update + double mean_elab_sec = mElabPeriodMean_sec->addValue(elabTimer.toc()); } - // Diagnostic statistics update - double mean_elab_sec = mElabPeriodMean_sec->addValue(elabTimer.toc()); - } - - RCLCPP_DEBUG(get_logger(), "Grab thread finished"); + RCLCPP_DEBUG(get_logger(), "Grab thread finished"); } -rclcpp::Time ZedCamera::publishSensorsData(rclcpp::Time t) +rclcpp::Time +ZedCamera::publishSensorsData(rclcpp::Time t) { - // ----> Subscribers count - RCLCPP_DEBUG_ONCE(get_logger(), "Sensors callback: counting subscribers"); - - size_t imu_SubNumber = 0; - size_t imu_RawSubNumber = 0; - size_t imu_TempSubNumber = 0; - size_t imu_MagSubNumber = 0; - size_t pressSubNumber = 0; - size_t tempLeftSubNumber = 0; - size_t tempRightSubNumber = 0; - - try - { - imu_SubNumber = count_subscribers(mPubImu->get_topic_name()); - imu_RawSubNumber = count_subscribers(mPubImuRaw->get_topic_name()); - imu_TempSubNumber = 0; - imu_MagSubNumber = 0; - pressSubNumber = 0; - tempLeftSubNumber = 0; - tempRightSubNumber = 0; - - if (sl_tools::isZED2OrZED2i(mCamRealModel)) - { - imu_TempSubNumber = count_subscribers(mPubImuTemp->get_topic_name()); - imu_MagSubNumber = count_subscribers(mPubImuMag->get_topic_name()); - pressSubNumber = count_subscribers(mPubPressure->get_topic_name()); - tempLeftSubNumber = count_subscribers(mPubTempL->get_topic_name()); - tempRightSubNumber = count_subscribers(mPubTempR->get_topic_name()); + // ----> Subscribers count + RCLCPP_DEBUG_ONCE(get_logger(), "Sensors callback: counting subscribers"); + + size_t imu_SubNumber = 0; + size_t imu_RawSubNumber = 0; + size_t imu_TempSubNumber = 0; + size_t imu_MagSubNumber = 0; + size_t pressSubNumber = 0; + size_t tempLeftSubNumber = 0; + size_t tempRightSubNumber = 0; + + try { + imu_SubNumber = count_subscribers(mPubImu->get_topic_name()); + imu_RawSubNumber = count_subscribers(mPubImuRaw->get_topic_name()); + imu_TempSubNumber = 0; + imu_MagSubNumber = 0; + pressSubNumber = 0; + tempLeftSubNumber = 0; + tempRightSubNumber = 0; + + if (sl_tools::isZED2OrZED2i(mCamRealModel)) { + imu_TempSubNumber = count_subscribers(mPubImuTemp->get_topic_name()); + imu_MagSubNumber = count_subscribers(mPubImuMag->get_topic_name()); + pressSubNumber = count_subscribers(mPubPressure->get_topic_name()); + tempLeftSubNumber = count_subscribers(mPubTempL->get_topic_name()); + tempRightSubNumber = count_subscribers(mPubTempR->get_topic_name()); + } + } catch (...) { + rcutils_reset_error(); + RCLCPP_DEBUG(get_logger(), + "pubSensorsData: Exception while counting subscribers"); + return TIMEZERO_ROS; } - } - catch (...) - { - rcutils_reset_error(); - RCLCPP_DEBUG(get_logger(), "pubSensorsData: Exception while counting subscribers"); - return TIMEZERO_ROS; - } - // <---- Subscribers count + // <---- Subscribers count - int totSub = imu_SubNumber + imu_RawSubNumber + imu_TempSubNumber + imu_MagSubNumber + pressSubNumber + - tempLeftSubNumber + tempRightSubNumber; + int totSub = imu_SubNumber + imu_RawSubNumber + imu_TempSubNumber + imu_MagSubNumber + pressSubNumber + tempLeftSubNumber + tempRightSubNumber; - // ----> Grab data and setup timestamps - RCLCPP_DEBUG_ONCE(get_logger(), "Sensors callback: Grab data and setup timestamps"); - rclcpp::Time ts_imu; - rclcpp::Time ts_baro; - rclcpp::Time ts_mag; + // ----> Grab data and setup timestamps + RCLCPP_DEBUG_ONCE(get_logger(), + "Sensors callback: Grab data and setup timestamps"); + rclcpp::Time ts_imu; + rclcpp::Time ts_baro; + rclcpp::Time ts_mag; - rclcpp::Time now = get_clock()->now(); + rclcpp::Time now = get_clock()->now(); - static rclcpp::Time lastTs_imu = now; - static rclcpp::Time lastTs_baro = now; - static rclcpp::Time lastTs_mag = now; + static rclcpp::Time lastTs_imu = now; + static rclcpp::Time lastTs_baro = now; + static rclcpp::Time lastTs_mag = now; - sl::SensorsData sens_data; + sl::SensorsData sens_data; - if (mSvoMode || mSensCameraSync) - { - if (mZed.getSensorsData(sens_data, sl::TIME_REFERENCE::IMAGE) != sl::ERROR_CODE::SUCCESS) - { - return TIMEZERO_ROS; - } - } - else - { - if (mZed.getSensorsData(sens_data, sl::TIME_REFERENCE::CURRENT) != sl::ERROR_CODE::SUCCESS) - { - return TIMEZERO_ROS; + if (mSvoMode || mSensCameraSync) { + if (mZed.getSensorsData(sens_data, sl::TIME_REFERENCE::IMAGE) != sl::ERROR_CODE::SUCCESS) { + return TIMEZERO_ROS; + } + } else { + if (mZed.getSensorsData(sens_data, sl::TIME_REFERENCE::CURRENT) != sl::ERROR_CODE::SUCCESS) { + return TIMEZERO_ROS; + } } - } - if (mSensCameraSync) - { - ts_imu = t; - ts_baro = t; - ts_mag = t; - } - else - { - ts_imu = sl_tools::slTime2Ros(sens_data.imu.timestamp); - ts_baro = sl_tools::slTime2Ros(sens_data.barometer.timestamp); - ts_mag = sl_tools::slTime2Ros(sens_data.magnetometer.timestamp); - } - // <---- Grab data and setup timestamps + if (mSensCameraSync) { + ts_imu = t; + ts_baro = t; + ts_mag = t; + } else { + ts_imu = sl_tools::slTime2Ros(sens_data.imu.timestamp); + ts_baro = sl_tools::slTime2Ros(sens_data.barometer.timestamp); + ts_mag = sl_tools::slTime2Ros(sens_data.magnetometer.timestamp); + } + // <---- Grab data and setup timestamps - // ----> Check for duplicated data - bool new_imu_data = ts_imu != lastTs_imu; - double sens_period = 1. / mSensPubRate; - double dT = ts_imu.seconds() - lastTs_imu.seconds(); + // ----> Check for duplicated data + bool new_imu_data = ts_imu != lastTs_imu; + double sens_period = 1. / mSensPubRate; + double dT = ts_imu.seconds() - lastTs_imu.seconds(); - /*if(dT Sensors freq for diagnostic - if (new_imu_data) - { - RCLCPP_DEBUG_ONCE(get_logger(), "Sensors callback: IMU FREQ"); - static sl_tools::StopWatch freqTimer; + // ----> Sensors freq for diagnostic + if (new_imu_data) { + RCLCPP_DEBUG_ONCE(get_logger(), "Sensors callback: IMU FREQ"); + static sl_tools::StopWatch freqTimer; - double mean = mImuPeriodMean_sec->addValue(freqTimer.toc()); - freqTimer.tic(); + double mean = mImuPeriodMean_sec->addValue(freqTimer.toc()); + freqTimer.tic(); - // RCLCPP_DEBUG_STREAM(get_logger(), "IMU MEAN freq: " << 1e6 / mean); - } + // RCLCPP_DEBUG_STREAM(get_logger(), "IMU MEAN freq: " << 1e6 / mean); + } - if (new_baro_data) - { - RCLCPP_DEBUG_ONCE(get_logger(), "Sensors callback: BARO FREQ"); - static sl_tools::StopWatch freqTimer; + if (new_baro_data) { + RCLCPP_DEBUG_ONCE(get_logger(), "Sensors callback: BARO FREQ"); + static sl_tools::StopWatch freqTimer; - double mean = mBaroPeriodMean_sec->addValue(freqTimer.toc()); - freqTimer.tic(); - // RCLCPP_DEBUG_STREAM(get_logger(), "Barometer freq: " << 1e6/mean); - } + double mean = mBaroPeriodMean_sec->addValue(freqTimer.toc()); + freqTimer.tic(); + // RCLCPP_DEBUG_STREAM(get_logger(), "Barometer freq: " << 1e6/mean); + } - if (new_mag_data) - { - RCLCPP_DEBUG_ONCE(get_logger(), "Sensors callback: MAG FREQ"); - static sl_tools::StopWatch freqTimer; + if (new_mag_data) { + RCLCPP_DEBUG_ONCE(get_logger(), "Sensors callback: MAG FREQ"); + static sl_tools::StopWatch freqTimer; - double mean = mMagPeriodMean_sec->addValue(freqTimer.toc()); - freqTimer.tic(); + double mean = mMagPeriodMean_sec->addValue(freqTimer.toc()); + freqTimer.tic(); - // RCLCPP_DEBUG_STREAM(get_logger(), "Magnetometer freq: " << 1e6/mean); - } - // <---- Sensors freq for diagnostic + // RCLCPP_DEBUG_STREAM(get_logger(), "Magnetometer freq: " << 1e6/mean); + } + // <---- Sensors freq for diagnostic - // if (!totSub < 0) - // { - // return TIMEZERO_ROS; // Nothing to publish - // } + // if (!totSub < 0) + // { + // return TIMEZERO_ROS; // Nothing to publish + // } - // ----> Sensors data publishing - if (new_imu_data) - { - if (imu_SubNumber > 0) - { - mImuPublishing = true; + // ----> Sensors data publishing + if (new_imu_data) { + if (imu_SubNumber > 0) { + mImuPublishing = true; - imuMsgPtr imuMsg = std::make_unique(); + imuMsgPtr imuMsg = std::make_unique(); - imuMsg->header.stamp = ts_imu; - imuMsg->header.frame_id = mImuFrameId; + imuMsg->header.stamp = ts_imu; + imuMsg->header.frame_id = mImuFrameId; - imuMsg->orientation.x = sens_data.imu.pose.getOrientation()[0]; - imuMsg->orientation.y = sens_data.imu.pose.getOrientation()[1]; - imuMsg->orientation.z = sens_data.imu.pose.getOrientation()[2]; - imuMsg->orientation.w = sens_data.imu.pose.getOrientation()[3]; + imuMsg->orientation.x = sens_data.imu.pose.getOrientation()[0]; + imuMsg->orientation.y = sens_data.imu.pose.getOrientation()[1]; + imuMsg->orientation.z = sens_data.imu.pose.getOrientation()[2]; + imuMsg->orientation.w = sens_data.imu.pose.getOrientation()[3]; - imuMsg->angular_velocity.x = sens_data.imu.angular_velocity[0] * DEG2RAD; - imuMsg->angular_velocity.y = sens_data.imu.angular_velocity[1] * DEG2RAD; - imuMsg->angular_velocity.z = sens_data.imu.angular_velocity[2] * DEG2RAD; + imuMsg->angular_velocity.x = sens_data.imu.angular_velocity[0] * DEG2RAD; + imuMsg->angular_velocity.y = sens_data.imu.angular_velocity[1] * DEG2RAD; + imuMsg->angular_velocity.z = sens_data.imu.angular_velocity[2] * DEG2RAD; - imuMsg->linear_acceleration.x = sens_data.imu.linear_acceleration[0]; - imuMsg->linear_acceleration.y = sens_data.imu.linear_acceleration[1]; - imuMsg->linear_acceleration.z = sens_data.imu.linear_acceleration[2]; + imuMsg->linear_acceleration.x = sens_data.imu.linear_acceleration[0]; + imuMsg->linear_acceleration.y = sens_data.imu.linear_acceleration[1]; + imuMsg->linear_acceleration.z = sens_data.imu.linear_acceleration[2]; - // ----> Covariances copy - // Note: memcpy not allowed because ROS2 uses double and ZED SDK uses float - for (int i = 0; i < 3; ++i) - { - int r = 0; + // ----> Covariances copy + // Note: memcpy not allowed because ROS2 uses double and ZED SDK uses + // float + for (int i = 0; i < 3; ++i) { + int r = 0; - if (i == 0) - { - r = 0; - } - else if (i == 1) - { - r = 1; - } - else - { - r = 2; - } + if (i == 0) { + r = 0; + } else if (i == 1) { + r = 1; + } else { + r = 2; + } - imuMsg->orientation_covariance[i * 3 + 0] = sens_data.imu.pose_covariance.r[r * 3 + 0] * DEG2RAD * DEG2RAD; - imuMsg->orientation_covariance[i * 3 + 1] = sens_data.imu.pose_covariance.r[r * 3 + 1] * DEG2RAD * DEG2RAD; - imuMsg->orientation_covariance[i * 3 + 2] = sens_data.imu.pose_covariance.r[r * 3 + 2] * DEG2RAD * DEG2RAD; + imuMsg->orientation_covariance[i * 3 + 0] = sens_data.imu.pose_covariance.r[r * 3 + 0] * DEG2RAD * DEG2RAD; + imuMsg->orientation_covariance[i * 3 + 1] = sens_data.imu.pose_covariance.r[r * 3 + 1] * DEG2RAD * DEG2RAD; + imuMsg->orientation_covariance[i * 3 + 2] = sens_data.imu.pose_covariance.r[r * 3 + 2] * DEG2RAD * DEG2RAD; - imuMsg->linear_acceleration_covariance[i * 3 + 0] = sens_data.imu.linear_acceleration_covariance.r[r * 3 + 0]; - imuMsg->linear_acceleration_covariance[i * 3 + 1] = sens_data.imu.linear_acceleration_covariance.r[r * 3 + 1]; - imuMsg->linear_acceleration_covariance[i * 3 + 2] = sens_data.imu.linear_acceleration_covariance.r[r * 3 + 2]; + imuMsg->linear_acceleration_covariance[i * 3 + 0] = sens_data.imu.linear_acceleration_covariance.r[r * 3 + 0]; + imuMsg->linear_acceleration_covariance[i * 3 + 1] = sens_data.imu.linear_acceleration_covariance.r[r * 3 + 1]; + imuMsg->linear_acceleration_covariance[i * 3 + 2] = sens_data.imu.linear_acceleration_covariance.r[r * 3 + 2]; - imuMsg->angular_velocity_covariance[i * 3 + 0] = - sens_data.imu.angular_velocity_covariance.r[r * 3 + 0] * DEG2RAD * DEG2RAD; - imuMsg->angular_velocity_covariance[i * 3 + 1] = - sens_data.imu.angular_velocity_covariance.r[r * 3 + 1] * DEG2RAD * DEG2RAD; - imuMsg->angular_velocity_covariance[i * 3 + 2] = - sens_data.imu.angular_velocity_covariance.r[r * 3 + 2] * DEG2RAD * DEG2RAD; - } - // <---- Covariances copy + imuMsg->angular_velocity_covariance[i * 3 + 0] = sens_data.imu.angular_velocity_covariance.r[r * 3 + 0] * DEG2RAD * DEG2RAD; + imuMsg->angular_velocity_covariance[i * 3 + 1] = sens_data.imu.angular_velocity_covariance.r[r * 3 + 1] * DEG2RAD * DEG2RAD; + imuMsg->angular_velocity_covariance[i * 3 + 2] = sens_data.imu.angular_velocity_covariance.r[r * 3 + 2] * DEG2RAD * DEG2RAD; + } + // <---- Covariances copy - mPubImu->publish(std::move(imuMsg)); - } - else - { - mImuPublishing = false; - } + mPubImu->publish(std::move(imuMsg)); + } else { + mImuPublishing = false; + } - if (imu_RawSubNumber > 0) - { - mImuPublishing = true; + if (imu_RawSubNumber > 0) { + mImuPublishing = true; - imuMsgPtr imuRawMsg = std::make_unique(); + imuMsgPtr imuRawMsg = std::make_unique(); - imuRawMsg->header.stamp = ts_imu; - imuRawMsg->header.frame_id = mImuFrameId; + imuRawMsg->header.stamp = ts_imu; + imuRawMsg->header.frame_id = mImuFrameId; - imuRawMsg->angular_velocity.x = sens_data.imu.angular_velocity[0] * DEG2RAD; - imuRawMsg->angular_velocity.y = sens_data.imu.angular_velocity[1] * DEG2RAD; - imuRawMsg->angular_velocity.z = sens_data.imu.angular_velocity[2] * DEG2RAD; + imuRawMsg->angular_velocity.x = sens_data.imu.angular_velocity[0] * DEG2RAD; + imuRawMsg->angular_velocity.y = sens_data.imu.angular_velocity[1] * DEG2RAD; + imuRawMsg->angular_velocity.z = sens_data.imu.angular_velocity[2] * DEG2RAD; - imuRawMsg->linear_acceleration.x = sens_data.imu.linear_acceleration[0]; - imuRawMsg->linear_acceleration.y = sens_data.imu.linear_acceleration[1]; - imuRawMsg->linear_acceleration.z = sens_data.imu.linear_acceleration[2]; + imuRawMsg->linear_acceleration.x = sens_data.imu.linear_acceleration[0]; + imuRawMsg->linear_acceleration.y = sens_data.imu.linear_acceleration[1]; + imuRawMsg->linear_acceleration.z = sens_data.imu.linear_acceleration[2]; - // ----> Covariances copy - // Note: memcpy not allowed because ROS2 uses double and ZED SDK uses float - for (int i = 0; i < 3; ++i) - { - int r = 0; + // ----> Covariances copy + // Note: memcpy not allowed because ROS2 uses double and ZED SDK uses + // float + for (int i = 0; i < 3; ++i) { + int r = 0; - if (i == 0) - { - r = 0; - } - else if (i == 1) - { - r = 1; - } - else - { - r = 2; - } + if (i == 0) { + r = 0; + } else if (i == 1) { + r = 1; + } else { + r = 2; + } - imuRawMsg->linear_acceleration_covariance[i * 3 + 0] = - sens_data.imu.linear_acceleration_covariance.r[r * 3 + 0]; - imuRawMsg->linear_acceleration_covariance[i * 3 + 1] = - sens_data.imu.linear_acceleration_covariance.r[r * 3 + 1]; - imuRawMsg->linear_acceleration_covariance[i * 3 + 2] = - sens_data.imu.linear_acceleration_covariance.r[r * 3 + 2]; + imuRawMsg->linear_acceleration_covariance[i * 3 + 0] = sens_data.imu.linear_acceleration_covariance.r[r * 3 + 0]; + imuRawMsg->linear_acceleration_covariance[i * 3 + 1] = sens_data.imu.linear_acceleration_covariance.r[r * 3 + 1]; + imuRawMsg->linear_acceleration_covariance[i * 3 + 2] = sens_data.imu.linear_acceleration_covariance.r[r * 3 + 2]; - imuRawMsg->angular_velocity_covariance[i * 3 + 0] = - sens_data.imu.angular_velocity_covariance.r[r * 3 + 0] * DEG2RAD * DEG2RAD; - imuRawMsg->angular_velocity_covariance[i * 3 + 1] = - sens_data.imu.angular_velocity_covariance.r[r * 3 + 1] * DEG2RAD * DEG2RAD; - imuRawMsg->angular_velocity_covariance[i * 3 + 2] = - sens_data.imu.angular_velocity_covariance.r[r * 3 + 2] * DEG2RAD * DEG2RAD; - } - // <---- Covariances copy + imuRawMsg->angular_velocity_covariance[i * 3 + 0] = sens_data.imu.angular_velocity_covariance.r[r * 3 + 0] * DEG2RAD * DEG2RAD; + imuRawMsg->angular_velocity_covariance[i * 3 + 1] = sens_data.imu.angular_velocity_covariance.r[r * 3 + 1] * DEG2RAD * DEG2RAD; + imuRawMsg->angular_velocity_covariance[i * 3 + 2] = sens_data.imu.angular_velocity_covariance.r[r * 3 + 2] * DEG2RAD * DEG2RAD; + } + // <---- Covariances copy - mPubImuRaw->publish(std::move(imuRawMsg)); - } + mPubImuRaw->publish(std::move(imuRawMsg)); + } - if (imu_TempSubNumber > 0) - { - mImuPublishing = true; + if (imu_TempSubNumber > 0) { + mImuPublishing = true; - tempMsgPtr imuTempMsg = std::make_unique(); + tempMsgPtr imuTempMsg = std::make_unique(); - imuTempMsg->header.stamp = ts_imu; + imuTempMsg->header.stamp = ts_imu; - imuTempMsg->header.frame_id = mImuFrameId; - float imu_temp; - sens_data.temperature.get(sl::SensorsData::TemperatureData::SENSOR_LOCATION::IMU, imu_temp); - imuTempMsg->temperature = static_cast(imu_temp); - imuTempMsg->variance = 0.0; + imuTempMsg->header.frame_id = mImuFrameId; + float imu_temp; + sens_data.temperature.get( + sl::SensorsData::TemperatureData::SENSOR_LOCATION::IMU, imu_temp); + imuTempMsg->temperature = static_cast(imu_temp); + imuTempMsg->variance = 0.0; - mPubImuTemp->publish(std::move(imuTempMsg)); + mPubImuTemp->publish(std::move(imuTempMsg)); + } } - } - if (sens_data.barometer.is_available && new_baro_data) - { - if (pressSubNumber > 0) - { - mBaroPublishing = true; + if (sens_data.barometer.is_available && new_baro_data) { + if (pressSubNumber > 0) { + mBaroPublishing = true; - pressMsgPtr pressMsg = std::make_unique(); + pressMsgPtr pressMsg = std::make_unique(); - pressMsg->header.stamp = ts_baro; - pressMsg->header.frame_id = mBaroFrameId; - pressMsg->fluid_pressure = sens_data.barometer.pressure * 1e2; // Pascal - pressMsg->variance = 1.0585e-2; + pressMsg->header.stamp = ts_baro; + pressMsg->header.frame_id = mBaroFrameId; + pressMsg->fluid_pressure = sens_data.barometer.pressure * 1e2; // Pascal + pressMsg->variance = 1.0585e-2; - mPubPressure->publish(std::move(pressMsg)); - } - else - { - mBaroPublishing = false; - } + mPubPressure->publish(std::move(pressMsg)); + } else { + mBaroPublishing = false; + } - if (tempLeftSubNumber > 0) - { - mBaroPublishing = true; + if (tempLeftSubNumber > 0) { + mBaroPublishing = true; - tempMsgPtr leftTempMsg = std::make_unique(); + tempMsgPtr leftTempMsg = std::make_unique(); - leftTempMsg->header.stamp = ts_baro; + leftTempMsg->header.stamp = ts_baro; - leftTempMsg->header.frame_id = mTempLeftFrameId; - leftTempMsg->temperature = static_cast(mTempLeft); - leftTempMsg->variance = 0.0; + leftTempMsg->header.frame_id = mTempLeftFrameId; + leftTempMsg->temperature = static_cast(mTempLeft); + leftTempMsg->variance = 0.0; - mPubTempL->publish(std::move(leftTempMsg)); - } + mPubTempL->publish(std::move(leftTempMsg)); + } - if (tempRightSubNumber > 0) - { - mBaroPublishing = true; + if (tempRightSubNumber > 0) { + mBaroPublishing = true; - tempMsgPtr rightTempMsg = std::make_unique(); + tempMsgPtr rightTempMsg = std::make_unique(); - rightTempMsg->header.stamp = ts_baro; + rightTempMsg->header.stamp = ts_baro; - rightTempMsg->header.frame_id = mTempRightFrameId; - rightTempMsg->temperature = static_cast(mTempRight); - rightTempMsg->variance = 0.0; + rightTempMsg->header.frame_id = mTempRightFrameId; + rightTempMsg->temperature = static_cast(mTempRight); + rightTempMsg->variance = 0.0; - mPubTempR->publish(std::move(rightTempMsg)); + mPubTempR->publish(std::move(rightTempMsg)); + } } - } - if (sens_data.magnetometer.is_available && new_mag_data) - { - if (imu_MagSubNumber > 0) - { - mMagPublishing = true; - - magMsgPtr magMsg = std::make_unique(); - - magMsg->header.stamp = ts_mag; - magMsg->header.frame_id = mMagFrameId; - magMsg->magnetic_field.x = sens_data.magnetometer.magnetic_field_calibrated.x * 1e-6; // Tesla - magMsg->magnetic_field.y = sens_data.magnetometer.magnetic_field_calibrated.y * 1e-6; // Tesla - magMsg->magnetic_field.z = sens_data.magnetometer.magnetic_field_calibrated.z * 1e-6; // Tesla - magMsg->magnetic_field_covariance[0] = 0.039e-6; - magMsg->magnetic_field_covariance[1] = 0.0f; - magMsg->magnetic_field_covariance[2] = 0.0f; - magMsg->magnetic_field_covariance[3] = 0.0f; - magMsg->magnetic_field_covariance[4] = 0.037e-6; - magMsg->magnetic_field_covariance[5] = 0.0f; - magMsg->magnetic_field_covariance[6] = 0.0f; - magMsg->magnetic_field_covariance[7] = 0.0f; - magMsg->magnetic_field_covariance[8] = 0.047e-6; - - mPubImuMag->publish(std::move(magMsg)); - } - else - { - mMagPublishing = false; + if (sens_data.magnetometer.is_available && new_mag_data) { + if (imu_MagSubNumber > 0) { + mMagPublishing = true; + + magMsgPtr magMsg = std::make_unique(); + + magMsg->header.stamp = ts_mag; + magMsg->header.frame_id = mMagFrameId; + magMsg->magnetic_field.x = sens_data.magnetometer.magnetic_field_calibrated.x * 1e-6; // Tesla + magMsg->magnetic_field.y = sens_data.magnetometer.magnetic_field_calibrated.y * 1e-6; // Tesla + magMsg->magnetic_field.z = sens_data.magnetometer.magnetic_field_calibrated.z * 1e-6; // Tesla + magMsg->magnetic_field_covariance[0] = 0.039e-6; + magMsg->magnetic_field_covariance[1] = 0.0f; + magMsg->magnetic_field_covariance[2] = 0.0f; + magMsg->magnetic_field_covariance[3] = 0.0f; + magMsg->magnetic_field_covariance[4] = 0.037e-6; + magMsg->magnetic_field_covariance[5] = 0.0f; + magMsg->magnetic_field_covariance[6] = 0.0f; + magMsg->magnetic_field_covariance[7] = 0.0f; + magMsg->magnetic_field_covariance[8] = 0.047e-6; + + mPubImuMag->publish(std::move(magMsg)); + } else { + mMagPublishing = false; + } } - } - // <---- Sensors data publishing + // <---- Sensors data publishing - return ts_imu; + return ts_imu; } void ZedCamera::publishTFs(rclcpp::Time t) { - // RCLCPP_DEBUG(get_logger(), "publishTFs"); + // RCLCPP_DEBUG(get_logger(), "publishTFs"); - // RCLCPP_INFO_STREAM(get_logger(), "publishTFs - t type:" << t.get_clock_type()); + // RCLCPP_INFO_STREAM(get_logger(), "publishTFs - t type:" << + // t.get_clock_type()); - if (t == TIMEZERO_ROS) - { - RCLCPP_DEBUG(get_logger(), "Time zero: not publishing TFs"); - return; - } + if (t == TIMEZERO_ROS) { + RCLCPP_DEBUG(get_logger(), "Time zero: not publishing TFs"); + return; + } - // Publish pose tf only if enabled - if (mDepthQuality != sl::DEPTH_MODE::NONE && mPublishTF) - { - publishOdomTF(t); // publish the base Frame in odometry frame + // Publish pose tf only if enabled + if (mDepthQuality != sl::DEPTH_MODE::NONE && mPublishTF) { + publishOdomTF(t); // publish the base Frame in odometry frame - if (mPublishMapTF) - { - publishPoseTF(t); // publish the odometry Frame in map frame - } + if (mPublishMapTF) { + publishPoseTF(t); // publish the odometry Frame in map frame + } - if (mCamRealModel != sl::MODEL::ZED) - { - if (mPublishImuTF && !mStaticImuFramePublished) - { - publishStaticImuFrameAndTopic(); - } + if (mCamRealModel != sl::MODEL::ZED) { + if (mPublishImuTF && !mStaticImuFramePublished) { + publishStaticImuFrameAndTopic(); + } + } } - } } void ZedCamera::publishOdomTF(rclcpp::Time t) { - // RCLCPP_DEBUG(get_logger(), "publishOdomTF"); - - // ----> Avoid duplicated TF publishing - static rclcpp::Time last_stamp = TIMEZERO_ROS; - - if (t == last_stamp) - { - return; - } - last_stamp = t; - // <---- Avoid duplicated TF publishing - - if (!mSensor2BaseTransfValid) - { - getSens2BaseTransform(); - } + // RCLCPP_DEBUG(get_logger(), "publishOdomTF"); - if (!mSensor2CameraTransfValid) - { - getSens2CameraTransform(); - } - - if (!mCamera2BaseTransfValid) - { - getCamera2BaseTransform(); - } - - transfMsgPtr transformStamped = std::make_shared(); - - transformStamped->header.stamp = t; - transformStamped->header.frame_id = mOdomFrameId; - transformStamped->child_frame_id = mBaseFrameId; - // conversion from Tranform to message - tf2::Vector3 translation = mOdom2BaseTransf.getOrigin(); - tf2::Quaternion quat = mOdom2BaseTransf.getRotation(); - transformStamped->transform.translation.x = translation.x(); - transformStamped->transform.translation.y = translation.y(); - transformStamped->transform.translation.z = translation.z(); - transformStamped->transform.rotation.x = quat.x(); - transformStamped->transform.rotation.y = quat.y(); - transformStamped->transform.rotation.z = quat.z(); - transformStamped->transform.rotation.w = quat.w(); - - // Publish transformation - mTfBroadcaster->sendTransform(*(transformStamped.get())); -} + // ----> Avoid duplicated TF publishing + static rclcpp::Time last_stamp = TIMEZERO_ROS; -void ZedCamera::publishPoseTF(rclcpp::Time t) -{ - // RCLCPP_DEBUG(get_logger(), "publishPoseTF"); + if (t == last_stamp) { + return; + } + last_stamp = t; + // <---- Avoid duplicated TF publishing - // ----> Avoid duplicated TF publishing - static rclcpp::Time last_stamp = TIMEZERO_ROS; + if (!mSensor2BaseTransfValid) { + getSens2BaseTransform(); + } - if (t == last_stamp) - { - return; - } - last_stamp = t; - // <---- Avoid duplicated TF publishing + if (!mSensor2CameraTransfValid) { + getSens2CameraTransform(); + } - if (!mSensor2BaseTransfValid) - { - getSens2BaseTransform(); - } + if (!mCamera2BaseTransfValid) { + getCamera2BaseTransform(); + } - if (!mSensor2CameraTransfValid) - { - getSens2CameraTransform(); - } + transfMsgPtr transformStamped = std::make_shared(); - if (!mCamera2BaseTransfValid) - { - getCamera2BaseTransform(); - } + transformStamped->header.stamp = t; + transformStamped->header.frame_id = mOdomFrameId; + transformStamped->child_frame_id = mBaseFrameId; + // conversion from Tranform to message + tf2::Vector3 translation = mOdom2BaseTransf.getOrigin(); + tf2::Quaternion quat = mOdom2BaseTransf.getRotation(); + transformStamped->transform.translation.x = translation.x(); + transformStamped->transform.translation.y = translation.y(); + transformStamped->transform.translation.z = translation.z(); + transformStamped->transform.rotation.x = quat.x(); + transformStamped->transform.rotation.y = quat.y(); + transformStamped->transform.rotation.z = quat.z(); + transformStamped->transform.rotation.w = quat.w(); - transfMsgPtr transformStamped = std::make_shared(); - - transformStamped->header.stamp = t; - transformStamped->header.frame_id = mMapFrameId; - transformStamped->child_frame_id = mOdomFrameId; - // conversion from Tranform to message - tf2::Vector3 translation = mMap2OdomTransf.getOrigin(); - tf2::Quaternion quat = mMap2OdomTransf.getRotation(); - transformStamped->transform.translation.x = translation.x(); - transformStamped->transform.translation.y = translation.y(); - transformStamped->transform.translation.z = translation.z(); - transformStamped->transform.rotation.x = quat.x(); - transformStamped->transform.rotation.y = quat.y(); - transformStamped->transform.rotation.z = quat.z(); - transformStamped->transform.rotation.w = quat.w(); - - // Publish transformation - mTfBroadcaster->sendTransform(*(transformStamped.get())); + // Publish transformation + mTfBroadcaster->sendTransform(*(transformStamped.get())); } -void ZedCamera::threadFunc_pointcloudElab() +void ZedCamera::publishPoseTF(rclcpp::Time t) { - std::unique_lock lock(mPcMutex); + // RCLCPP_DEBUG(get_logger(), "publishPoseTF"); - RCLCPP_DEBUG(get_logger(), "Point Cloud thread started"); + // ----> Avoid duplicated TF publishing + static rclcpp::Time last_stamp = TIMEZERO_ROS; - while (1) - { - if (!rclcpp::ok()) - { - RCLCPP_DEBUG(get_logger(), "Ctrl+C received: stopping point cloud thread"); - break; + if (t == last_stamp) { + return; } + last_stamp = t; + // <---- Avoid duplicated TF publishing - // RCLCPP_DEBUG(get_logger(), "pointcloudThreadFunc -> mPcDataReady value: %s", mPcDataReady ? "TRUE" : "FALSE"); - - while (!mPcDataReady) - { // loop to avoid spurious wakeups - if (mPcDataReadyCondVar.wait_for(lock, std::chrono::milliseconds(500)) == std::cv_status::timeout) - { - // Check thread stopping - if (!rclcpp::ok()) - { - RCLCPP_DEBUG(get_logger(), "Ctrl+C received: stopping point cloud thread"); - mThreadStop = true; - break; - } - if (mThreadStop) - { - RCLCPP_DEBUG(get_logger(), "threadFunc_pointcloudElab (2): Point Cloud thread stopped"); - break; - } - else - { - // RCLCPP_DEBUG(get_logger(), "pointcloudThreadFunc -> WAIT FOR CLOUD DATA"); - continue; - } - } + if (!mSensor2BaseTransfValid) { + getSens2BaseTransform(); } - if (mThreadStop) - { - RCLCPP_DEBUG(get_logger(), "threadFunc_pointcloudElab (1): Point Cloud thread stopped"); - break; + if (!mSensor2CameraTransfValid) { + getSens2CameraTransform(); } - publishPointCloud(); - - // ----> Check publishing frequency - double pc_period_msec = 1000.0 / mPcPubRate; - - static sl_tools::StopWatch freqTimer; - double elapsed_msec = freqTimer.toc()*1e3; - - if (elapsed_msec < pc_period_msec) - { - std::this_thread::sleep_for(std::chrono::milliseconds(static_cast(pc_period_msec - elapsed_msec))); + if (!mCamera2BaseTransfValid) { + getCamera2BaseTransform(); } - freqTimer.tic(); - // <---- Check publishing frequency + transfMsgPtr transformStamped = std::make_shared(); - mPcDataReady = false; - // RCLCPP_DEBUG(get_logger(), "pointcloudThreadFunc -> mPcDataReady FALSE") - } + transformStamped->header.stamp = t; + transformStamped->header.frame_id = mMapFrameId; + transformStamped->child_frame_id = mOdomFrameId; + // conversion from Tranform to message + tf2::Vector3 translation = mMap2OdomTransf.getOrigin(); + tf2::Quaternion quat = mMap2OdomTransf.getRotation(); + transformStamped->transform.translation.x = translation.x(); + transformStamped->transform.translation.y = translation.y(); + transformStamped->transform.translation.z = translation.z(); + transformStamped->transform.rotation.x = quat.x(); + transformStamped->transform.rotation.y = quat.y(); + transformStamped->transform.rotation.z = quat.z(); + transformStamped->transform.rotation.w = quat.w(); - RCLCPP_DEBUG(get_logger(), "Pointcloud thread finished"); + // Publish transformation + mTfBroadcaster->sendTransform(*(transformStamped.get())); } -void ZedCamera::callback_pubVideoDepth() +void ZedCamera::threadFunc_pointcloudElab() { - RCLCPP_DEBUG_ONCE(get_logger(), "Video Depth callback called"); + std::unique_lock lock(mPcMutex); - static rclcpp::Time prev_ts = TIMEZERO_ROS; + RCLCPP_DEBUG(get_logger(), "Point Cloud thread started"); - std::lock_guard lock(mCloseZedMutex); - mPublishingData = false; + while (1) { + if (!rclcpp::ok()) { + RCLCPP_DEBUG(get_logger(), + "Ctrl+C received: stopping point cloud thread"); + break; + } - rclcpp::Time pub_ts; - mPublishingData = publishVideoDepth(pub_ts); + // RCLCPP_DEBUG(get_logger(), "pointcloudThreadFunc -> mPcDataReady value: + // %s", mPcDataReady ? "TRUE" : "FALSE"); + + while (!mPcDataReady) { // loop to avoid spurious wakeups + if (mPcDataReadyCondVar.wait_for(lock, std::chrono::milliseconds(500)) == std::cv_status::timeout) { + // Check thread stopping + if (!rclcpp::ok()) { + RCLCPP_DEBUG(get_logger(), + "Ctrl+C received: stopping point cloud thread"); + mThreadStop = true; + break; + } + if (mThreadStop) { + RCLCPP_DEBUG( + get_logger(), + "threadFunc_pointcloudElab (2): Point Cloud thread stopped"); + break; + } else { + // RCLCPP_DEBUG(get_logger(), "pointcloudThreadFunc -> WAIT FOR CLOUD + // DATA"); + continue; + } + } + } - // RCLCPP_INFO_STREAM(get_logger(), "callback_pubVideoDepth - pub_ts type:" << pub_ts.get_clock_type()); + if (mThreadStop) { + RCLCPP_DEBUG(get_logger(), + "threadFunc_pointcloudElab (1): Point Cloud thread stopped"); + break; + } - if (mCamRealModel != sl::MODEL::ZED && mPublishingData && pub_ts != TIMEZERO_ROS) - { - if (mSensCameraSync || mSvoMode) - { - publishSensorsData(pub_ts); - } - } -} + publishPointCloud(); -void ZedCamera::callback_pubSensorsData() -{ - RCLCPP_DEBUG_ONCE(get_logger(), "Sensors callback called"); + // ----> Check publishing frequency + double pc_period_msec = 1000.0 / mPcPubRate; - std::lock_guard lock(mCloseZedMutex); - if (!mZed.isOpened()) - { - return; - } + static sl_tools::StopWatch freqTimer; + double elapsed_msec = freqTimer.toc() * 1e3; - rclcpp::Time sens_ts = publishSensorsData(); + if (elapsed_msec < pc_period_msec) { + std::this_thread::sleep_for(std::chrono::milliseconds( + static_cast(pc_period_msec - elapsed_msec))); + } - // RCLCPP_INFO_STREAM(get_logger(), "callback_pubSensorsData - sens_ts type:" << sens_ts.get_clock_type()); + freqTimer.tic(); + // <---- Check publishing frequency - // Publish TF at the same frequency of IMU data, so they are always synchronized - if (sens_ts != TIMEZERO_ROS) - { - publishTFs(sens_ts); - } + mPcDataReady = false; + // RCLCPP_DEBUG(get_logger(), "pointcloudThreadFunc -> mPcDataReady FALSE") + } + + RCLCPP_DEBUG(get_logger(), "Pointcloud thread finished"); } -bool ZedCamera::publishVideoDepth(rclcpp::Time& out_pub_ts) +void ZedCamera::callback_pubVideoDepth() { - sl_tools::StopWatch elabTimer; - - static sl::Timestamp lastZedTs = 0; // Used to calculate stable publish frequency - - static size_t rgbSubnumber = 0; - static size_t rgbRawSubnumber = 0; - static size_t rgbGraySubnumber = 0; - static size_t rgbGrayRawSubnumber = 0; - static size_t leftSubnumber = 0; - static size_t leftRawSubnumber = 0; - static size_t leftGraySubnumber = 0; - static size_t leftGrayRawSubnumber = 0; - static size_t rightSubnumber = 0; - static size_t rightRawSubnumber = 0; - static size_t rightGraySubnumber = 0; - static size_t rightGrayRawSubnumber = 0; - static size_t stereoSubnumber = 0; - static size_t stereoRawSubnumber = 0; - static size_t depthSubnumber = 0; - static size_t confMapSubnumber = 0; - static size_t disparitySubnumber = 0; - - try - { - rgbSubnumber = mPubRgb.getNumSubscribers(); - rgbRawSubnumber = mPubRawRgb.getNumSubscribers(); - rgbGraySubnumber = mPubRgbGray.getNumSubscribers(); - rgbGrayRawSubnumber = mPubRawRgbGray.getNumSubscribers(); - leftSubnumber = mPubLeft.getNumSubscribers(); - leftRawSubnumber = mPubRawLeft.getNumSubscribers(); - leftGraySubnumber = mPubLeftGray.getNumSubscribers(); - leftGrayRawSubnumber = mPubRawLeftGray.getNumSubscribers(); - rightSubnumber = mPubRight.getNumSubscribers(); - rightRawSubnumber = mPubRawRight.getNumSubscribers(); - rightGraySubnumber = mPubRightGray.getNumSubscribers(); - rightGrayRawSubnumber = mPubRawRightGray.getNumSubscribers(); - stereoSubnumber = mPubStereo.getNumSubscribers(); - stereoRawSubnumber = mPubRawStereo.getNumSubscribers(); - depthSubnumber = mPubDepth.getNumSubscribers(); - confMapSubnumber = count_subscribers(mPubConfMap->get_topic_name()); - disparitySubnumber = count_subscribers(mPubDisparity->get_topic_name()); - } - catch (...) - { - rcutils_reset_error(); - RCLCPP_DEBUG(get_logger(), "publishImages: Exception while counting subscribers"); - return false; - } + RCLCPP_DEBUG_ONCE(get_logger(), "Video Depth callback called"); - bool retrieved = false; + static rclcpp::Time prev_ts = TIMEZERO_ROS; - static sl::Mat mat_left,mat_left_raw; - static sl::Mat mat_right,mat_right_raw; - static sl::Mat mat_left_gray,mat_left_raw_gray; - static sl::Mat mat_right_gray,mat_right_raw_gray; - static sl::Mat mat_depth,mat_disp,mat_conf; + std::lock_guard lock(mCloseZedMutex); + mPublishingData = false; - static sl::Timestamp ts_rgb=0; // used to check RGB/Depth sync - static sl::Timestamp ts_depth=0; // used to check RGB/Depth sync - static sl::Timestamp grab_ts=0; + rclcpp::Time pub_ts; + mPublishingData = publishVideoDepth(pub_ts); - // ----> Retrieve all required data - std::unique_lock lock(mCamDataMutex, std::defer_lock); + // RCLCPP_INFO_STREAM(get_logger(), "callback_pubVideoDepth - pub_ts type:" << + // pub_ts.get_clock_type()); - if (lock.try_lock_for(std::chrono::milliseconds(1000/mCamGrabFrameRate))) - { - if (rgbSubnumber + leftSubnumber + stereoSubnumber > 0) - { - retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_left, sl::VIEW::LEFT, sl::MEM::CPU, mMatResolVideo); - ts_rgb = mat_left.timestamp; - grab_ts = mat_left.timestamp; - } - if (rgbRawSubnumber + leftRawSubnumber + stereoRawSubnumber > 0) - { - retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_left_raw, sl::VIEW::LEFT_UNRECTIFIED, sl::MEM::CPU, mMatResolVideo); - grab_ts = mat_left_raw.timestamp; - } - if (rightSubnumber + stereoSubnumber > 0) - { - retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_right, sl::VIEW::RIGHT, sl::MEM::CPU, mMatResolVideo); - grab_ts = mat_right.timestamp; - } - if (rightRawSubnumber + stereoRawSubnumber > 0) - { - retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_right_raw, sl::VIEW::RIGHT_UNRECTIFIED, sl::MEM::CPU, mMatResolVideo); - grab_ts = mat_right_raw.timestamp; - } - if (rgbGraySubnumber + leftGraySubnumber > 0) - { - retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_left_gray, sl::VIEW::LEFT_GRAY, sl::MEM::CPU, mMatResolVideo); - grab_ts = mat_left_gray.timestamp; - } - if (rgbGrayRawSubnumber + leftGrayRawSubnumber > 0) - { - retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_left_raw_gray, sl::VIEW::LEFT_UNRECTIFIED_GRAY, sl::MEM::CPU, mMatResolVideo); - grab_ts = mat_left_raw_gray.timestamp; - } - if (rightGraySubnumber > 0) - { - retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_right_gray, sl::VIEW::RIGHT_GRAY, sl::MEM::CPU, mMatResolVideo); - grab_ts = mat_right_gray.timestamp; - } - if (rightGrayRawSubnumber > 0) - { - retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_right_raw_gray, sl::VIEW::RIGHT_UNRECTIFIED_GRAY, sl::MEM::CPU, mMatResolVideo); - grab_ts = mat_right_raw_gray.timestamp; + if (mCamRealModel != sl::MODEL::ZED && mPublishingData && pub_ts != TIMEZERO_ROS) { + if (mSensCameraSync || mSvoMode) { + publishSensorsData(pub_ts); + } } - if (depthSubnumber > 0) - { - retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveMeasure(mat_depth, sl::MEASURE::DEPTH, sl::MEM::CPU, mMatResolDepth); - grab_ts = mat_depth.timestamp; +} - ts_depth = mat_depth.timestamp; +void ZedCamera::callback_pubSensorsData() +{ + RCLCPP_DEBUG_ONCE(get_logger(), "Sensors callback called"); - if (ts_rgb.data_ns != 0 && (ts_depth.data_ns != ts_rgb.data_ns)) - { - RCLCPP_WARN_STREAM(get_logger(), "!!!!! DEPTH/RGB ASYNC !!!!! - Delta: " - << 1e-9 * static_cast(ts_depth - ts_rgb) << " sec"); - } - } - if (disparitySubnumber > 0) - { - retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveMeasure(mat_disp, sl::MEASURE::DISPARITY, sl::MEM::CPU, mMatResolDepth); - grab_ts = mat_disp.timestamp; - } - if (confMapSubnumber > 0) - { - retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveMeasure(mat_conf, sl::MEASURE::CONFIDENCE, sl::MEM::CPU, mMatResolDepth); - grab_ts = mat_conf.timestamp; + std::lock_guard lock(mCloseZedMutex); + if (!mZed.isOpened()) { + return; } - } else { - RCLCPP_INFO(get_logger(), "Lock timeout"); - } - // <---- Retrieve all required data - - // ----> Notify grab thread that all data are synchronized and a new grab can be done - mRgbDepthDataRetrievedCondVar.notify_one(); - mRgbDepthDataRetrieved = true; - // <---- Notify grab thread that all data are synchronized and a new grab can be done - - if (!retrieved) - { - lastZedTs = 0; - out_pub_ts = TIMEZERO_ROS; - return false; - } - - // ----> Check if a grab has been done before publishing the same images - if (grab_ts.data_ns == lastZedTs.data_ns) - { - out_pub_ts = TIMEZERO_ROS; - // Data not updated by a grab calling in the grab thread - return true; - } - - if (lastZedTs.data_ns != 0) - { - double period_sec = static_cast(grab_ts.data_ns - lastZedTs.data_ns) / 1e9; - RCLCPP_DEBUG_STREAM(get_logger(), - "VIDEO/DEPTH PUB LAST PERIOD: " << period_sec << " sec @" << 1. / period_sec << " Hz"); - - mVideoDepthPeriodMean_sec->addValue(period_sec); - RCLCPP_DEBUG_STREAM(get_logger(), - "VIDEO/DEPTH PUB MEAN PERIOD: " << mVideoDepthPeriodMean_sec->getMean() << " sec @" - << 1. / mVideoDepthPeriodMean_sec->getMean() << " Hz"); - } - lastZedTs = grab_ts; - // <---- Check if a grab has been done before publishing the same images - - static rclcpp::Time timeStamp; - if (!mSvoMode) - { - timeStamp = sl_tools::slTime2Ros(grab_ts, get_clock()->get_clock_type()); - } - else - { - timeStamp = sl_tools::slTime2Ros(mZed.getTimestamp(sl::TIME_REFERENCE::CURRENT), get_clock()->get_clock_type()); - } - - out_pub_ts = timeStamp; - - // ----> Publish the left=rgb image if someone has subscribed to - if (leftSubnumber > 0) - { - publishImageWithInfo(mat_left, mPubLeft, mLeftCamInfoMsg, mLeftCamOptFrameId, timeStamp); - } - if (rgbSubnumber > 0) - { - publishImageWithInfo(mat_left, mPubRgb, mRgbCamInfoMsg, mDepthOptFrameId, timeStamp); - } - // <---- Publish the left=rgb image if someone has subscribed to - // ----> Publish the left_raw=rgb_raw image if someone has subscribed to - if (leftRawSubnumber > 0) - { - publishImageWithInfo(mat_left_raw, mPubRawLeft, mLeftCamInfoRawMsg, mLeftCamOptFrameId, timeStamp); - } - if (rgbRawSubnumber > 0) - { - publishImageWithInfo(mat_left_raw, mPubRawRgb, mRgbCamInfoRawMsg, mDepthOptFrameId, timeStamp); - } - // <---- Publish the left_raw=rgb_raw image if someone has subscribed to - - // ----> Publish the left_gray=rgb_gray image if someone has subscribed to - if (leftGraySubnumber > 0) - { - publishImageWithInfo(mat_left_gray, mPubLeftGray, mLeftCamInfoMsg, mLeftCamOptFrameId, timeStamp); - } - if (rgbGraySubnumber > 0) - { - publishImageWithInfo(mat_left_gray, mPubRgbGray, mRgbCamInfoMsg, mDepthOptFrameId, timeStamp); - } - // <---- Publish the left_raw=rgb_raw image if someone has subscribed to + rclcpp::Time sens_ts = publishSensorsData(); - // ----> Publish the left_raw_gray=rgb_raw_gray image if someone has subscribed to - if (leftGrayRawSubnumber > 0) - { - publishImageWithInfo(mat_left_raw_gray, mPubRawLeftGray, mLeftCamInfoRawMsg, mLeftCamOptFrameId, timeStamp); - } - if (rgbGrayRawSubnumber > 0) - { - publishImageWithInfo(mat_left_raw_gray, mPubRawRgbGray, mRgbCamInfoRawMsg, mDepthOptFrameId, timeStamp); - } - // ----> Publish the left_raw_gray=rgb_raw_gray image if someone has subscribed to + // RCLCPP_INFO_STREAM(get_logger(), "callback_pubSensorsData - sens_ts type:" + // << sens_ts.get_clock_type()); - // ----> Publish the right image if someone has subscribed to - if (rightSubnumber > 0) - { - publishImageWithInfo(mat_right, mPubRight, mRightCamInfoMsg, mRightCamOptFrameId, timeStamp); - } - // <---- Publish the right image if someone has subscribed to + // Publish TF at the same frequency of IMU data, so they are always + // synchronized + if (sens_ts != TIMEZERO_ROS) { + publishTFs(sens_ts); + } +} - // ----> Publish the right raw image if someone has subscribed to - if (rightRawSubnumber > 0) - { - publishImageWithInfo(mat_right_raw, mPubRawRight, mRightCamInfoRawMsg, mRightCamOptFrameId, timeStamp); - } - // <---- Publish the right raw image if someone has subscribed to +bool ZedCamera::publishVideoDepth(rclcpp::Time& out_pub_ts) +{ + sl_tools::StopWatch elabTimer; - // ----> Publish the right gray image if someone has subscribed to - if (rightGraySubnumber > 0) - { - publishImageWithInfo(mat_right_gray, mPubRightGray, mRightCamInfoMsg, mRightCamOptFrameId, timeStamp); - } - // <---- Publish the right gray image if someone has subscribed to + static sl::Timestamp lastZedTs = 0; // Used to calculate stable publish frequency + + static size_t rgbSubnumber = 0; + static size_t rgbRawSubnumber = 0; + static size_t rgbGraySubnumber = 0; + static size_t rgbGrayRawSubnumber = 0; + static size_t leftSubnumber = 0; + static size_t leftRawSubnumber = 0; + static size_t leftGraySubnumber = 0; + static size_t leftGrayRawSubnumber = 0; + static size_t rightSubnumber = 0; + static size_t rightRawSubnumber = 0; + static size_t rightGraySubnumber = 0; + static size_t rightGrayRawSubnumber = 0; + static size_t stereoSubnumber = 0; + static size_t stereoRawSubnumber = 0; + static size_t depthSubnumber = 0; + static size_t confMapSubnumber = 0; + static size_t disparitySubnumber = 0; + + try { + rgbSubnumber = mPubRgb.getNumSubscribers(); + rgbRawSubnumber = mPubRawRgb.getNumSubscribers(); + rgbGraySubnumber = mPubRgbGray.getNumSubscribers(); + rgbGrayRawSubnumber = mPubRawRgbGray.getNumSubscribers(); + leftSubnumber = mPubLeft.getNumSubscribers(); + leftRawSubnumber = mPubRawLeft.getNumSubscribers(); + leftGraySubnumber = mPubLeftGray.getNumSubscribers(); + leftGrayRawSubnumber = mPubRawLeftGray.getNumSubscribers(); + rightSubnumber = mPubRight.getNumSubscribers(); + rightRawSubnumber = mPubRawRight.getNumSubscribers(); + rightGraySubnumber = mPubRightGray.getNumSubscribers(); + rightGrayRawSubnumber = mPubRawRightGray.getNumSubscribers(); + stereoSubnumber = mPubStereo.getNumSubscribers(); + stereoRawSubnumber = mPubRawStereo.getNumSubscribers(); + depthSubnumber = mPubDepth.getNumSubscribers(); + confMapSubnumber = count_subscribers(mPubConfMap->get_topic_name()); + disparitySubnumber = count_subscribers(mPubDisparity->get_topic_name()); + } catch (...) { + rcutils_reset_error(); + RCLCPP_DEBUG(get_logger(), + "publishImages: Exception while counting subscribers"); + return false; + } - // ----> Publish the right raw gray image if someone has subscribed to - if (rightGrayRawSubnumber > 0) - { - publishImageWithInfo(mat_right_raw_gray, mPubRawRightGray, mRightCamInfoRawMsg, mRightCamOptFrameId, timeStamp); - } - // <---- Publish the right raw gray image if someone has subscribed to + bool retrieved = false; - // ----> Publish the side-by-side image if someone has subscribed to - if (stereoSubnumber > 0) - { - auto combined = sl_tools::imagesToROSmsg(mat_left, mat_right, mCameraFrameId, timeStamp); - mPubStereo.publish(combined); - } - // <---- Publish the side-by-side image if someone has subscribed to + static sl::Mat mat_left, mat_left_raw; + static sl::Mat mat_right, mat_right_raw; + static sl::Mat mat_left_gray, mat_left_raw_gray; + static sl::Mat mat_right_gray, mat_right_raw_gray; + static sl::Mat mat_depth, mat_disp, mat_conf; - // ----> Publish the side-by-side image if someone has subscribed to - if (stereoRawSubnumber > 0) - { - auto combined = sl_tools::imagesToROSmsg(mat_left_raw, mat_right_raw, mCameraFrameId, timeStamp); - mPubRawStereo.publish(combined); - } - // <---- Publish the side-by-side image if someone has subscribed to + static sl::Timestamp ts_rgb = 0; // used to check RGB/Depth sync + static sl::Timestamp ts_depth = 0; // used to check RGB/Depth sync + static sl::Timestamp grab_ts = 0; - // ----> Publish the depth image if someone has subscribed to - if (depthSubnumber > 0) - { - publishDepthMapWithInfo(mat_depth, timeStamp); - } - // <---- Publish the depth image if someone has subscribed to + // ----> Retrieve all required data + std::unique_lock lock(mCamDataMutex, std::defer_lock); - // ----> Publish the confidence image and map if someone has subscribed to - if (confMapSubnumber > 0) - { - mPubConfMap->publish(*sl_tools::imageToROSmsg(mat_conf, mDepthOptFrameId, timeStamp)); - } - // <---- Publish the confidence image and map if someone has subscribed to + if (lock.try_lock_for(std::chrono::milliseconds(1000 / mCamGrabFrameRate))) { + if (rgbSubnumber + leftSubnumber + stereoSubnumber > 0) { + retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_left, sl::VIEW::LEFT, sl::MEM::CPU, mMatResolVideo); + ts_rgb = mat_left.timestamp; + grab_ts = mat_left.timestamp; + } + if (rgbRawSubnumber + leftRawSubnumber + stereoRawSubnumber > 0) { + retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_left_raw, sl::VIEW::LEFT_UNRECTIFIED, sl::MEM::CPU, mMatResolVideo); + grab_ts = mat_left_raw.timestamp; + } + if (rightSubnumber + stereoSubnumber > 0) { + retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_right, sl::VIEW::RIGHT, sl::MEM::CPU, mMatResolVideo); + grab_ts = mat_right.timestamp; + } + if (rightRawSubnumber + stereoRawSubnumber > 0) { + retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_right_raw, sl::VIEW::RIGHT_UNRECTIFIED, sl::MEM::CPU, mMatResolVideo); + grab_ts = mat_right_raw.timestamp; + } + if (rgbGraySubnumber + leftGraySubnumber > 0) { + retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_left_gray, sl::VIEW::LEFT_GRAY, sl::MEM::CPU, mMatResolVideo); + grab_ts = mat_left_gray.timestamp; + } + if (rgbGrayRawSubnumber + leftGrayRawSubnumber > 0) { + retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_left_raw_gray, sl::VIEW::LEFT_UNRECTIFIED_GRAY, sl::MEM::CPU, mMatResolVideo); + grab_ts = mat_left_raw_gray.timestamp; + } + if (rightGraySubnumber > 0) { + retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_right_gray, sl::VIEW::RIGHT_GRAY, sl::MEM::CPU, mMatResolVideo); + grab_ts = mat_right_gray.timestamp; + } + if (rightGrayRawSubnumber > 0) { + retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveImage(mat_right_raw_gray, sl::VIEW::RIGHT_UNRECTIFIED_GRAY, sl::MEM::CPU, mMatResolVideo); + grab_ts = mat_right_raw_gray.timestamp; + } + if (depthSubnumber > 0) { + retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveMeasure(mat_depth, sl::MEASURE::DEPTH, sl::MEM::CPU, mMatResolDepth); + grab_ts = mat_depth.timestamp; + + ts_depth = mat_depth.timestamp; + + if (ts_rgb.data_ns != 0 && (ts_depth.data_ns != ts_rgb.data_ns)) { + RCLCPP_WARN_STREAM(get_logger(), + "!!!!! DEPTH/RGB ASYNC !!!!! - Delta: " + << 1e-9 * static_cast(ts_depth - ts_rgb) + << " sec"); + } + } + if (disparitySubnumber > 0) { + retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveMeasure(mat_disp, sl::MEASURE::DISPARITY, sl::MEM::CPU, mMatResolDepth); + grab_ts = mat_disp.timestamp; + } + if (confMapSubnumber > 0) { + retrieved = sl::ERROR_CODE::SUCCESS == mZed.retrieveMeasure(mat_conf, sl::MEASURE::CONFIDENCE, sl::MEM::CPU, mMatResolDepth); + grab_ts = mat_conf.timestamp; + } + } else { + RCLCPP_INFO(get_logger(), "Lock timeout"); + } + // <---- Retrieve all required data + + // ----> Notify grab thread that all data are synchronized and a new grab can + // be done + mRgbDepthDataRetrievedCondVar.notify_one(); + mRgbDepthDataRetrieved = true; + // <---- Notify grab thread that all data are synchronized and a new grab can + // be done + + if (!retrieved) { + lastZedTs = 0; + out_pub_ts = TIMEZERO_ROS; + return false; + } + + // ----> Check if a grab has been done before publishing the same images + if (grab_ts.data_ns == lastZedTs.data_ns) { + out_pub_ts = TIMEZERO_ROS; + // Data not updated by a grab calling in the grab thread + return true; + } + + if (lastZedTs.data_ns != 0) { + double period_sec = static_cast(grab_ts.data_ns - lastZedTs.data_ns) / 1e9; + RCLCPP_DEBUG_STREAM(get_logger(), + "VIDEO/DEPTH PUB LAST PERIOD: " + << period_sec << " sec @" << 1. / period_sec + << " Hz"); + + mVideoDepthPeriodMean_sec->addValue(period_sec); + RCLCPP_DEBUG_STREAM(get_logger(), + "VIDEO/DEPTH PUB MEAN PERIOD: " + << mVideoDepthPeriodMean_sec->getMean() << " sec @" + << 1. / mVideoDepthPeriodMean_sec->getMean() + << " Hz"); + } + lastZedTs = grab_ts; + // <---- Check if a grab has been done before publishing the same images + + static rclcpp::Time timeStamp; + if (!mSvoMode) { + timeStamp = sl_tools::slTime2Ros(grab_ts, get_clock()->get_clock_type()); + } else { + timeStamp = sl_tools::slTime2Ros(mZed.getTimestamp(sl::TIME_REFERENCE::CURRENT), + get_clock()->get_clock_type()); + } + + out_pub_ts = timeStamp; + + // ----> Publish the left=rgb image if someone has subscribed to + if (leftSubnumber > 0) { + publishImageWithInfo( + mat_left, mPubLeft, mLeftCamInfoMsg, mLeftCamOptFrameId, timeStamp); + } + if (rgbSubnumber > 0) { + publishImageWithInfo( + mat_left, mPubRgb, mRgbCamInfoMsg, mDepthOptFrameId, timeStamp); + } + // <---- Publish the left=rgb image if someone has subscribed to + + // ----> Publish the left_raw=rgb_raw image if someone has subscribed to + if (leftRawSubnumber > 0) { + publishImageWithInfo(mat_left_raw, + mPubRawLeft, + mLeftCamInfoRawMsg, + mLeftCamOptFrameId, + timeStamp); + } + if (rgbRawSubnumber > 0) { + publishImageWithInfo( + mat_left_raw, mPubRawRgb, mRgbCamInfoRawMsg, mDepthOptFrameId, timeStamp); + } + // <---- Publish the left_raw=rgb_raw image if someone has subscribed to + + // ----> Publish the left_gray=rgb_gray image if someone has subscribed to + if (leftGraySubnumber > 0) { + publishImageWithInfo(mat_left_gray, + mPubLeftGray, + mLeftCamInfoMsg, + mLeftCamOptFrameId, + timeStamp); + } + if (rgbGraySubnumber > 0) { + publishImageWithInfo( + mat_left_gray, mPubRgbGray, mRgbCamInfoMsg, mDepthOptFrameId, timeStamp); + } + // <---- Publish the left_raw=rgb_raw image if someone has subscribed to + + // ----> Publish the left_raw_gray=rgb_raw_gray image if someone has + // subscribed to + if (leftGrayRawSubnumber > 0) { + publishImageWithInfo(mat_left_raw_gray, + mPubRawLeftGray, + mLeftCamInfoRawMsg, + mLeftCamOptFrameId, + timeStamp); + } + if (rgbGrayRawSubnumber > 0) { + publishImageWithInfo(mat_left_raw_gray, + mPubRawRgbGray, + mRgbCamInfoRawMsg, + mDepthOptFrameId, + timeStamp); + } + // ----> Publish the left_raw_gray=rgb_raw_gray image if someone has + // subscribed to + + // ----> Publish the right image if someone has subscribed to + if (rightSubnumber > 0) { + publishImageWithInfo( + mat_right, mPubRight, mRightCamInfoMsg, mRightCamOptFrameId, timeStamp); + } + // <---- Publish the right image if someone has subscribed to + + // ----> Publish the right raw image if someone has subscribed to + if (rightRawSubnumber > 0) { + publishImageWithInfo(mat_right_raw, + mPubRawRight, + mRightCamInfoRawMsg, + mRightCamOptFrameId, + timeStamp); + } + // <---- Publish the right raw image if someone has subscribed to + + // ----> Publish the right gray image if someone has subscribed to + if (rightGraySubnumber > 0) { + publishImageWithInfo(mat_right_gray, + mPubRightGray, + mRightCamInfoMsg, + mRightCamOptFrameId, + timeStamp); + } + // <---- Publish the right gray image if someone has subscribed to + + // ----> Publish the right raw gray image if someone has subscribed to + if (rightGrayRawSubnumber > 0) { + publishImageWithInfo(mat_right_raw_gray, + mPubRawRightGray, + mRightCamInfoRawMsg, + mRightCamOptFrameId, + timeStamp); + } + // <---- Publish the right raw gray image if someone has subscribed to - // ----> Publish the disparity image if someone has subscribed to - if (disparitySubnumber > 0) - { - publishDisparity(mat_disp, timeStamp); - } - // <---- Publish the disparity image if someone has subscribed to + // ----> Publish the side-by-side image if someone has subscribed to + if (stereoSubnumber > 0) { + auto combined = sl_tools::imagesToROSmsg(mat_left, mat_right, mCameraFrameId, timeStamp); + mPubStereo.publish(combined); + } + // <---- Publish the side-by-side image if someone has subscribed to + + // ----> Publish the side-by-side image if someone has subscribed to + if (stereoRawSubnumber > 0) { + auto combined = sl_tools::imagesToROSmsg( + mat_left_raw, mat_right_raw, mCameraFrameId, timeStamp); + mPubRawStereo.publish(combined); + } + // <---- Publish the side-by-side image if someone has subscribed to + + // ----> Publish the depth image if someone has subscribed to + if (depthSubnumber > 0) { + publishDepthMapWithInfo(mat_depth, timeStamp); + } + // <---- Publish the depth image if someone has subscribed to + + // ----> Publish the confidence image and map if someone has subscribed to + if (confMapSubnumber > 0) { + mPubConfMap->publish( + *sl_tools::imageToROSmsg(mat_conf, mDepthOptFrameId, timeStamp)); + } + // <---- Publish the confidence image and map if someone has subscribed to - // Diagnostic statistic - mVideoDepthElabMean_sec->addValue(elabTimer.toc()); + // ----> Publish the disparity image if someone has subscribed to + if (disparitySubnumber > 0) { + publishDisparity(mat_disp, timeStamp); + } + // <---- Publish the disparity image if someone has subscribed to + + // Diagnostic statistic + mVideoDepthElabMean_sec->addValue(elabTimer.toc()); - return true; + return true; } -void ZedCamera::publishImageWithInfo(sl::Mat& img, image_transport::CameraPublisher& pubImg, camInfoMsgPtr& camInfoMsg, - std::string imgFrameId, rclcpp::Time t) +void ZedCamera::publishImageWithInfo(sl::Mat& img, + image_transport::CameraPublisher& pubImg, + camInfoMsgPtr& camInfoMsg, + std::string imgFrameId, + rclcpp::Time t) { - auto image = sl_tools::imageToROSmsg(img, imgFrameId, t); - camInfoMsg->header.stamp = t; - pubImg.publish(image, camInfoMsg); // TODO CHECK FOR ZERO-COPY + auto image = sl_tools::imageToROSmsg(img, imgFrameId, t); + camInfoMsg->header.stamp = t; + pubImg.publish(image, camInfoMsg); // TODO CHECK FOR ZERO-COPY } void ZedCamera::processOdometry() { - rclcpp::Clock steady_clock(RCL_STEADY_TIME); + rclcpp::Clock steady_clock(RCL_STEADY_TIME); - if (!mSensor2BaseTransfValid) - { - getSens2BaseTransform(); - } + if (!mSensor2BaseTransfValid) { + getSens2BaseTransform(); + } - if (!mSensor2CameraTransfValid) - { - getSens2CameraTransform(); - } + if (!mSensor2CameraTransfValid) { + getSens2CameraTransform(); + } - if (!mCamera2BaseTransfValid) - { - getCamera2BaseTransform(); - } + if (!mCamera2BaseTransfValid) { + getCamera2BaseTransform(); + } - if (!mInitOdomWithPose) - { - sl::Pose deltaOdom; + if (!mInitOdomWithPose) { + sl::Pose deltaOdom; - mPosTrackingStatus = mZed.getPosition(deltaOdom, sl::REFERENCE_FRAME::CAMERA); + mPosTrackingStatus = mZed.getPosition(deltaOdom, sl::REFERENCE_FRAME::CAMERA); - sl::Translation translation = deltaOdom.getTranslation(); - sl::Orientation quat = deltaOdom.getOrientation(); + sl::Translation translation = deltaOdom.getTranslation(); + sl::Orientation quat = deltaOdom.getOrientation(); #if 0 RCLCPP_DEBUG(get_logger(), "delta ODOM [%s] - %.2f,%.2f,%.2f %.2f,%.2f,%.2f,%.2f", @@ -4331,36 +4520,34 @@ void ZedCamera::processOdometry() quat(0), quat(1), quat(2), quat(3)); #endif - if (mPosTrackingStatus == sl::POSITIONAL_TRACKING_STATE::OK || - mPosTrackingStatus == sl::POSITIONAL_TRACKING_STATE::SEARCHING || - mPosTrackingStatus == sl::POSITIONAL_TRACKING_STATE::FPS_TOO_LOW) - { - // Transform ZED delta odom pose in TF2 Transformation - tf2::Transform deltaOdomTf; - deltaOdomTf.setOrigin(tf2::Vector3(translation(0), translation(1), translation(2))); - // w at the end in the constructor - deltaOdomTf.setRotation(tf2::Quaternion(quat(0), quat(1), quat(2), quat(3))); + if (mPosTrackingStatus == sl::POSITIONAL_TRACKING_STATE::OK || mPosTrackingStatus == sl::POSITIONAL_TRACKING_STATE::SEARCHING || mPosTrackingStatus == sl::POSITIONAL_TRACKING_STATE::FPS_TOO_LOW) { + // Transform ZED delta odom pose in TF2 Transformation + tf2::Transform deltaOdomTf; + deltaOdomTf.setOrigin( + tf2::Vector3(translation(0), translation(1), translation(2))); + // w at the end in the constructor + deltaOdomTf.setRotation( + tf2::Quaternion(quat(0), quat(1), quat(2), quat(3))); - // delta odom from sensor to base frame - tf2::Transform deltaOdomTf_base = mSensor2BaseTransf.inverse() * deltaOdomTf * mSensor2BaseTransf; + // delta odom from sensor to base frame + tf2::Transform deltaOdomTf_base = mSensor2BaseTransf.inverse() * deltaOdomTf * mSensor2BaseTransf; - // Propagate Odom transform in time - mOdom2BaseTransf = mOdom2BaseTransf * deltaOdomTf_base; + // Propagate Odom transform in time + mOdom2BaseTransf = mOdom2BaseTransf * deltaOdomTf_base; - if (mTwoDMode) - { - tf2::Vector3 tr_2d = mOdom2BaseTransf.getOrigin(); - tr_2d.setZ(mFixedZValue); - mOdom2BaseTransf.setOrigin(tr_2d); + if (mTwoDMode) { + tf2::Vector3 tr_2d = mOdom2BaseTransf.getOrigin(); + tr_2d.setZ(mFixedZValue); + mOdom2BaseTransf.setOrigin(tr_2d); - double roll, pitch, yaw; - tf2::Matrix3x3(mOdom2BaseTransf.getRotation()).getRPY(roll, pitch, yaw); + double roll, pitch, yaw; + tf2::Matrix3x3(mOdom2BaseTransf.getRotation()).getRPY(roll, pitch, yaw); - tf2::Quaternion quat_2d; - quat_2d.setRPY(0.0, 0.0, yaw); + tf2::Quaternion quat_2d; + quat_2d.setRPY(0.0, 0.0, yaw); - mOdom2BaseTransf.setRotation(quat_2d); - } + mOdom2BaseTransf.setRotation(quat_2d); + } #if 0 double roll, pitch, yaw; @@ -4374,99 +4561,90 @@ void ZedCamera::processOdometry() roll * RAD2DEG, pitch * RAD2DEG, yaw * RAD2DEG); #endif - // Publish odometry message - publishOdom(mOdom2BaseTransf, deltaOdom, mFrameTimestamp); - mPosTrackingReady = true; + // Publish odometry message + publishOdom(mOdom2BaseTransf, deltaOdom, mFrameTimestamp); + mPosTrackingReady = true; + } + } else if (mFloorAlignment) { + RCLCPP_DEBUG_THROTTLE(get_logger(), + steady_clock, + 5.0, + "Odometry will be published as soon as the floor as " + "been detected for the first time"); } - } - else if (mFloorAlignment) - { - RCLCPP_DEBUG_THROTTLE(get_logger(), steady_clock, 5.0, - "Odometry will be published as soon as the floor as been detected for the first time"); - } } -void ZedCamera::publishOdom(tf2::Transform& odom2baseTransf, sl::Pose& slPose, rclcpp::Time t) +void ZedCamera::publishOdom(tf2::Transform& odom2baseTransf, + sl::Pose& slPose, + rclcpp::Time t) { - odomMsgPtr odomMsg = std::make_unique(); - - odomMsg->header.stamp = t; - odomMsg->header.frame_id = mOdomFrameId; // frame - odomMsg->child_frame_id = mBaseFrameId; // camera_frame - - // Add all value in odometry message - odomMsg->pose.pose.position.x = odom2baseTransf.getOrigin().x(); - odomMsg->pose.pose.position.y = odom2baseTransf.getOrigin().y(); - odomMsg->pose.pose.position.z = odom2baseTransf.getOrigin().z(); - odomMsg->pose.pose.orientation.x = odom2baseTransf.getRotation().x(); - odomMsg->pose.pose.orientation.y = odom2baseTransf.getRotation().y(); - odomMsg->pose.pose.orientation.z = odom2baseTransf.getRotation().z(); - odomMsg->pose.pose.orientation.w = odom2baseTransf.getRotation().w(); - - // Odometry pose covariance - for (size_t i = 0; i < odomMsg->pose.covariance.size(); i++) - { - odomMsg->pose.covariance[i] = static_cast(slPose.pose_covariance[i]); - - if (mTwoDMode) - { - if (i == 14 || i == 21 || i == 28) - { - odomMsg->pose.covariance[i] = 1e-9; // Very low covariance if 2D mode - } - else if ((i >= 2 && i <= 4) || (i >= 8 && i <= 10) || (i >= 12 && i <= 13) || (i >= 15 && i <= 16) || - (i >= 18 && i <= 20) || (i == 22) || (i >= 24 && i <= 27)) - { - odomMsg->pose.covariance[i] = 0.0; - } + odomMsgPtr odomMsg = std::make_unique(); + + odomMsg->header.stamp = t; + odomMsg->header.frame_id = mOdomFrameId; // frame + odomMsg->child_frame_id = mBaseFrameId; // camera_frame + + // Add all value in odometry message + odomMsg->pose.pose.position.x = odom2baseTransf.getOrigin().x(); + odomMsg->pose.pose.position.y = odom2baseTransf.getOrigin().y(); + odomMsg->pose.pose.position.z = odom2baseTransf.getOrigin().z(); + odomMsg->pose.pose.orientation.x = odom2baseTransf.getRotation().x(); + odomMsg->pose.pose.orientation.y = odom2baseTransf.getRotation().y(); + odomMsg->pose.pose.orientation.z = odom2baseTransf.getRotation().z(); + odomMsg->pose.pose.orientation.w = odom2baseTransf.getRotation().w(); + + // Odometry pose covariance + for (size_t i = 0; i < odomMsg->pose.covariance.size(); i++) { + odomMsg->pose.covariance[i] = static_cast(slPose.pose_covariance[i]); + + if (mTwoDMode) { + if (i == 14 || i == 21 || i == 28) { + odomMsg->pose.covariance[i] = 1e-9; // Very low covariance if 2D mode + } else if ((i >= 2 && i <= 4) || (i >= 8 && i <= 10) || (i >= 12 && i <= 13) || (i >= 15 && i <= 16) || (i >= 18 && i <= 20) || (i == 22) || (i >= 24 && i <= 27)) { + odomMsg->pose.covariance[i] = 0.0; + } + } } - } - // Publish odometry message - mPubOdom->publish(std::move(odomMsg)); + // Publish odometry message + mPubOdom->publish(std::move(odomMsg)); } void ZedCamera::processPose() { - if (!mSensor2BaseTransfValid) - { - getSens2BaseTransform(); - } + if (!mSensor2BaseTransfValid) { + getSens2BaseTransform(); + } - if (!mSensor2CameraTransfValid) - { - getSens2CameraTransform(); - } + if (!mSensor2CameraTransfValid) { + getSens2CameraTransform(); + } - if (!mCamera2BaseTransfValid) - { - getCamera2BaseTransform(); - } + if (!mCamera2BaseTransfValid) { + getCamera2BaseTransform(); + } - size_t odomSub = 0; - try - { - odomSub = count_subscribers(mOdomTopic); // mPubOdom subscribers - } - catch (...) - { - rcutils_reset_error(); - RCLCPP_DEBUG(get_logger(), "processPose: Exception while counting subscribers"); - return; - } + size_t odomSub = 0; + try { + odomSub = count_subscribers(mOdomTopic); // mPubOdom subscribers + } catch (...) { + rcutils_reset_error(); + RCLCPP_DEBUG(get_logger(), + "processPose: Exception while counting subscribers"); + return; + } - static sl::POSITIONAL_TRACKING_STATE oldStatus; - mPosTrackingStatus = mZed.getPosition(mLastZedPose, sl::REFERENCE_FRAME::WORLD); + static sl::POSITIONAL_TRACKING_STATE oldStatus; + mPosTrackingStatus = mZed.getPosition(mLastZedPose, sl::REFERENCE_FRAME::WORLD); - sl::Translation translation = mLastZedPose.getTranslation(); - sl::Orientation quat = mLastZedPose.getOrientation(); + sl::Translation translation = mLastZedPose.getTranslation(); + sl::Orientation quat = mLastZedPose.getOrientation(); - if (quat.sum() == 0) - { - return; - } + if (quat.sum() == 0) { + return; + } -#if 0 // Enable for TF checking +#if 0 // Enable for TF checking double roll, pitch, yaw; tf2::Matrix3x3(tf2::Quaternion(quat.ox, quat.oy, quat.oz, quat.ow)).getRPY(roll, pitch, yaw); @@ -4478,31 +4656,30 @@ void ZedCamera::processPose() RCLCPP_DEBUG(get_logger(), "MAP -> Tracking Status: %s", sl::toString(mTrackingStatus).c_str()); #endif - if (mPosTrackingStatus == sl::POSITIONAL_TRACKING_STATE::OK || - mPosTrackingStatus == sl::POSITIONAL_TRACKING_STATE::SEARCHING) - { - tf2::Transform map_to_sens_transf; - map_to_sens_transf.setOrigin(tf2::Vector3(translation(0), translation(1), translation(2))); - map_to_sens_transf.setRotation(tf2::Quaternion(quat(0), quat(1), quat(2), quat(3))); + if (mPosTrackingStatus == sl::POSITIONAL_TRACKING_STATE::OK || mPosTrackingStatus == sl::POSITIONAL_TRACKING_STATE::SEARCHING) { + tf2::Transform map_to_sens_transf; + map_to_sens_transf.setOrigin( + tf2::Vector3(translation(0), translation(1), translation(2))); + map_to_sens_transf.setRotation( + tf2::Quaternion(quat(0), quat(1), quat(2), quat(3))); - mMap2BaseTransf = map_to_sens_transf * mSensor2BaseTransf; // Base position in map frame + mMap2BaseTransf = map_to_sens_transf * mSensor2BaseTransf; // Base position in map frame - if (mTwoDMode) - { - tf2::Vector3 tr_2d = mMap2BaseTransf.getOrigin(); - tr_2d.setZ(mFixedZValue); - mMap2BaseTransf.setOrigin(tr_2d); + if (mTwoDMode) { + tf2::Vector3 tr_2d = mMap2BaseTransf.getOrigin(); + tr_2d.setZ(mFixedZValue); + mMap2BaseTransf.setOrigin(tr_2d); - double roll, pitch, yaw; - tf2::Matrix3x3(mMap2BaseTransf.getRotation()).getRPY(roll, pitch, yaw); + double roll, pitch, yaw; + tf2::Matrix3x3(mMap2BaseTransf.getRotation()).getRPY(roll, pitch, yaw); - tf2::Quaternion quat_2d; - quat_2d.setRPY(0.0, 0.0, yaw); + tf2::Quaternion quat_2d; + quat_2d.setRPY(0.0, 0.0, yaw); - mMap2BaseTransf.setRotation(quat_2d); - } + mMap2BaseTransf.setRotation(quat_2d); + } -#if 0 // Enable for TF checking +#if 0 // Enable for TF checking double roll, pitch, yaw; tf2::Matrix3x3(mMap2BaseTransf.getRotation()).getRPY(roll, pitch, yaw); @@ -4512,40 +4689,33 @@ void ZedCamera::processPose() roll * RAD2DEG, pitch * RAD2DEG, yaw * RAD2DEG); #endif - bool initOdom = false; + bool initOdom = false; - if (!(mFloorAlignment)) - { - initOdom = mInitOdomWithPose; - } - else - { - initOdom = mInitOdomWithPose & (mPosTrackingStatus == sl::POSITIONAL_TRACKING_STATE::OK); - } + if (!(mFloorAlignment)) { + initOdom = mInitOdomWithPose; + } else { + initOdom = mInitOdomWithPose & (mPosTrackingStatus == sl::POSITIONAL_TRACKING_STATE::OK); + } - if (initOdom || mResetOdom) - { - RCLCPP_INFO(get_logger(), "Odometry aligned to last tracking pose"); + if (initOdom || mResetOdom) { + RCLCPP_INFO(get_logger(), "Odometry aligned to last tracking pose"); - // Propagate Odom transform in time - mOdom2BaseTransf = mMap2BaseTransf; - mMap2BaseTransf.setIdentity(); + // Propagate Odom transform in time + mOdom2BaseTransf = mMap2BaseTransf; + mMap2BaseTransf.setIdentity(); - if (odomSub > 0) - { - // Publish odometry message - publishOdom(mOdom2BaseTransf, mLastZedPose, mFrameTimestamp); - } + if (odomSub > 0) { + // Publish odometry message + publishOdom(mOdom2BaseTransf, mLastZedPose, mFrameTimestamp); + } - mInitOdomWithPose = false; - mResetOdom = false; - } - else - { - // Transformation from map to odometry frame - mMap2OdomTransf = mMap2BaseTransf * mOdom2BaseTransf.inverse(); + mInitOdomWithPose = false; + mResetOdom = false; + } else { + // Transformation from map to odometry frame + mMap2OdomTransf = mMap2BaseTransf * mOdom2BaseTransf.inverse(); -#if 0 // Enable for TF checking +#if 0 // Enable for TF checking double roll, pitch, yaw; tf2::Matrix3x3(mMap2OdomTransf.getRotation()).getRPY(roll, pitch, yaw); @@ -4554,1295 +4724,1231 @@ void ZedCamera::processPose() mMap2OdomTransf.getOrigin().x(), mMap2OdomTransf.getOrigin().y(), mMap2OdomTransf.getOrigin().z(), roll * RAD2DEG, pitch * RAD2DEG, yaw * RAD2DEG); #endif - } + } - // Publish Pose message - publishPose(); - mPosTrackingReady = true; - } + // Publish Pose message + publishPose(); + mPosTrackingReady = true; + } - oldStatus = mPosTrackingStatus; + oldStatus = mPosTrackingStatus; } void ZedCamera::publishPose() { - size_t poseSub = 0; - size_t poseCovSub = 0; + size_t poseSub = 0; + size_t poseCovSub = 0; - try - { - poseSub = count_subscribers(mPoseTopic); // mPubPose subscribers - poseCovSub = count_subscribers(mPoseCovTopic); // mPubPoseCov subscribers - } - catch (...) - { - rcutils_reset_error(); - RCLCPP_DEBUG(get_logger(), "publishPose: Exception while counting subscribers"); - return; - } + try { + poseSub = count_subscribers(mPoseTopic); // mPubPose subscribers + poseCovSub = count_subscribers(mPoseCovTopic); // mPubPoseCov subscribers + } catch (...) { + rcutils_reset_error(); + RCLCPP_DEBUG(get_logger(), + "publishPose: Exception while counting subscribers"); + return; + } - tf2::Transform base_pose; - base_pose.setIdentity(); + tf2::Transform base_pose; + base_pose.setIdentity(); - base_pose = mMap2BaseTransf; + base_pose = mMap2BaseTransf; - std_msgs::msg::Header header; - header.stamp = mFrameTimestamp; - header.frame_id = mMapFrameId; // frame + std_msgs::msg::Header header; + header.stamp = mFrameTimestamp; + header.frame_id = mMapFrameId; // frame - geometry_msgs::msg::Pose pose; + geometry_msgs::msg::Pose pose; - // Add all value in Pose message - pose.position.x = mMap2BaseTransf.getOrigin().x(); - pose.position.y = mMap2BaseTransf.getOrigin().y(); - pose.position.z = mMap2BaseTransf.getOrigin().z(); - pose.orientation.x = mMap2BaseTransf.getRotation().x(); - pose.orientation.y = mMap2BaseTransf.getRotation().y(); - pose.orientation.z = mMap2BaseTransf.getRotation().z(); - pose.orientation.w = mMap2BaseTransf.getRotation().w(); + // Add all value in Pose message + pose.position.x = mMap2BaseTransf.getOrigin().x(); + pose.position.y = mMap2BaseTransf.getOrigin().y(); + pose.position.z = mMap2BaseTransf.getOrigin().z(); + pose.orientation.x = mMap2BaseTransf.getRotation().x(); + pose.orientation.y = mMap2BaseTransf.getRotation().y(); + pose.orientation.z = mMap2BaseTransf.getRotation().z(); + pose.orientation.w = mMap2BaseTransf.getRotation().w(); - if (poseSub > 0) - { - poseMsgPtr poseNoCov = std::make_unique(); + if (poseSub > 0) { + poseMsgPtr poseNoCov = std::make_unique(); - poseNoCov->header = header; - poseNoCov->pose = pose; + poseNoCov->header = header; + poseNoCov->pose = pose; - // Publish pose stamped message - mPubPose->publish(std::move(poseNoCov)); - } + // Publish pose stamped message + mPubPose->publish(std::move(poseNoCov)); + } - if (mPublishPoseCov) - { - if (poseCovSub > 0) - { - poseCovMsgPtr poseCov = std::make_unique(); + if (mPublishPoseCov) { + if (poseCovSub > 0) { + poseCovMsgPtr poseCov = std::make_unique(); - poseCov->header = header; - poseCov->pose.pose = pose; + poseCov->header = header; + poseCov->pose.pose = pose; - // Odometry pose covariance if available + // Odometry pose covariance if available - for (size_t i = 0; i < poseCov->pose.covariance.size(); i++) - { - poseCov->pose.covariance[i] = static_cast(mLastZedPose.pose_covariance[i]); - - if (mTwoDMode) - { - if ((i >= 2 && i <= 4) || (i >= 8 && i <= 10) || (i >= 12 && i <= 29) || (i >= 32 && i <= 34)) - { - poseCov->pose.covariance[i] = 1e-9; // Very low covariance if 2D mode - } - } - } + for (size_t i = 0; i < poseCov->pose.covariance.size(); i++) { + poseCov->pose.covariance[i] = static_cast(mLastZedPose.pose_covariance[i]); - // Publish pose with covariance stamped message - mPubPoseCov->publish(std::move(poseCov)); + if (mTwoDMode) { + if ((i >= 2 && i <= 4) || (i >= 8 && i <= 10) || (i >= 12 && i <= 29) || (i >= 32 && i <= 34)) { + poseCov->pose.covariance[i] = 1e-9; // Very low covariance if 2D mode + } + } + } + + // Publish pose with covariance stamped message + mPubPoseCov->publish(std::move(poseCov)); + } } - } } void ZedCamera::processDetectedObjects(rclcpp::Time t) { - size_t objdet_sub_count = 0; - - try - { - objdet_sub_count = count_subscribers(mPubObjDet->get_topic_name()); - } - catch (...) - { - rcutils_reset_error(); - RCLCPP_DEBUG(get_logger(), "processDetectedObjects: Exception while counting subscribers"); - return; - } - - if (objdet_sub_count < 1) - { - mObjDetSubscribed = false; - return; - } - - sl_tools::StopWatch elabTimer; - static sl_tools::StopWatch freqTimer; + size_t objdet_sub_count = 0; - mObjDetSubscribed = true; - - sl::ObjectDetectionRuntimeParameters objectTracker_parameters_rt; + try { + objdet_sub_count = count_subscribers(mPubObjDet->get_topic_name()); + } catch (...) { + rcutils_reset_error(); + RCLCPP_DEBUG( + get_logger(), + "processDetectedObjects: Exception while counting subscribers"); + return; + } - // ----> Process realtime dynamic parameters - objectTracker_parameters_rt.detection_confidence_threshold = mObjDetConfidence; - mObjDetFilter.clear(); - if (mObjDetPeopleEnable) - { - mObjDetFilter.push_back(sl::OBJECT_CLASS::PERSON); - } - if (mObjDetVehiclesEnable) - { - mObjDetFilter.push_back(sl::OBJECT_CLASS::VEHICLE); - } - if (mObjDetBagsEnable) - { - mObjDetFilter.push_back(sl::OBJECT_CLASS::BAG); - } - if (mObjDetAnimalsEnable) - { - mObjDetFilter.push_back(sl::OBJECT_CLASS::ANIMAL); - } - if (mObjDetElectronicsEnable) - { - mObjDetFilter.push_back(sl::OBJECT_CLASS::ELECTRONICS); - } - if (mObjDetFruitsEnable) - { - mObjDetFilter.push_back(sl::OBJECT_CLASS::FRUIT_VEGETABLE); - } - objectTracker_parameters_rt.object_class_filter = mObjDetFilter; - // <---- Process realtime dynamic parameters + if (objdet_sub_count < 1) { + mObjDetSubscribed = false; + return; + } - sl::Objects objects; + sl_tools::StopWatch elabTimer; + static sl_tools::StopWatch freqTimer; - sl::ERROR_CODE objDetRes = mZed.retrieveObjects(objects, objectTracker_parameters_rt); + mObjDetSubscribed = true; - if (objDetRes != sl::ERROR_CODE::SUCCESS) - { - RCLCPP_WARN_STREAM(get_logger(), "Object Detection error: " << sl::toString(objDetRes)); - return; - } + sl::ObjectDetectionRuntimeParameters objectTracker_parameters_rt; - if (!objects.is_new) // Async object detection. Update data only if new detection is available - { - return; - } + // ----> Process realtime dynamic parameters + objectTracker_parameters_rt.detection_confidence_threshold = mObjDetConfidence; + mObjDetFilter.clear(); + if (mObjDetPeopleEnable) { + mObjDetFilter.push_back(sl::OBJECT_CLASS::PERSON); + } + if (mObjDetVehiclesEnable) { + mObjDetFilter.push_back(sl::OBJECT_CLASS::VEHICLE); + } + if (mObjDetBagsEnable) { + mObjDetFilter.push_back(sl::OBJECT_CLASS::BAG); + } + if (mObjDetAnimalsEnable) { + mObjDetFilter.push_back(sl::OBJECT_CLASS::ANIMAL); + } + if (mObjDetElectronicsEnable) { + mObjDetFilter.push_back(sl::OBJECT_CLASS::ELECTRONICS); + } + if (mObjDetFruitsEnable) { + mObjDetFilter.push_back(sl::OBJECT_CLASS::FRUIT_VEGETABLE); + } + objectTracker_parameters_rt.object_class_filter = mObjDetFilter; + // <---- Process realtime dynamic parameters - // RCLCPP_DEBUG_STREAM(get_logger(), "Detected " << objects.object_list.size() << " objects"); + sl::Objects objects; - size_t objCount = objects.object_list.size(); + sl::ERROR_CODE objDetRes = mZed.retrieveObjects(objects, objectTracker_parameters_rt); - objDetMsgPtr objMsg = std::make_unique(); + if (objDetRes != sl::ERROR_CODE::SUCCESS) { + RCLCPP_WARN_STREAM(get_logger(), + "Object Detection error: " << sl::toString(objDetRes)); + return; + } - objMsg->header.stamp = t; - objMsg->header.frame_id = mLeftCamFrameId; + if (!objects.is_new) // Async object detection. Update data only if new + // detection is available + { + return; + } - objMsg->objects.resize(objCount); + // RCLCPP_DEBUG_STREAM(get_logger(), "Detected " << objects.object_list.size() + // << " objects"); - size_t idx = 0; - for (auto data : objects.object_list) - { - objMsg->objects[idx].label = sl::toString(data.label).c_str(); - objMsg->objects[idx].sublabel = sl::toString(data.sublabel).c_str(); - objMsg->objects[idx].label_id = data.id; - objMsg->objects[idx].confidence = data.confidence; + size_t objCount = objects.object_list.size(); - memcpy(&(objMsg->objects[idx].position[0]), &(data.position[0]), 3 * sizeof(float)); - memcpy(&(objMsg->objects[idx].position_covariance[0]), &(data.position_covariance[0]), 6 * sizeof(float)); - memcpy(&(objMsg->objects[idx].velocity[0]), &(data.velocity[0]), 3 * sizeof(float)); + objDetMsgPtr objMsg = std::make_unique(); - objMsg->objects[idx].tracking_state = static_cast(data.tracking_state); - objMsg->objects[idx].action_state = static_cast(data.action_state); + objMsg->header.stamp = t; + objMsg->header.frame_id = mLeftCamFrameId; - if (data.bounding_box_2d.size() == 4) - { - memcpy(&(objMsg->objects[idx].bounding_box_2d.corners[0]), &(data.bounding_box_2d[0]), 8 * sizeof(unsigned int)); - } - if (data.bounding_box.size() == 8) - { - memcpy(&(objMsg->objects[idx].bounding_box_3d.corners[0]), &(data.bounding_box[0]), 24 * sizeof(float)); - } + objMsg->objects.resize(objCount); + + size_t idx = 0; + for (auto data : objects.object_list) { + objMsg->objects[idx].label = sl::toString(data.label).c_str(); + objMsg->objects[idx].sublabel = sl::toString(data.sublabel).c_str(); + objMsg->objects[idx].label_id = data.id; + objMsg->objects[idx].confidence = data.confidence; + + memcpy(&(objMsg->objects[idx].position[0]), + &(data.position[0]), + 3 * sizeof(float)); + memcpy(&(objMsg->objects[idx].position_covariance[0]), + &(data.position_covariance[0]), + 6 * sizeof(float)); + memcpy(&(objMsg->objects[idx].velocity[0]), + &(data.velocity[0]), + 3 * sizeof(float)); + + objMsg->objects[idx].tracking_available = mObjDetTracking; + objMsg->objects[idx].tracking_state = static_cast(data.tracking_state); + objMsg->objects[idx].action_state = static_cast(data.action_state); + + if (data.bounding_box_2d.size() == 4) { + memcpy(&(objMsg->objects[idx].bounding_box_2d.corners[0]), + &(data.bounding_box_2d[0]), + 8 * sizeof(unsigned int)); + } + if (data.bounding_box.size() == 8) { + memcpy(&(objMsg->objects[idx].bounding_box_3d.corners[0]), + &(data.bounding_box[0]), + 24 * sizeof(float)); + } - memcpy(&(objMsg->objects[idx].dimensions_3d[0]), &(data.dimensions[0]), 3 * sizeof(float)); + memcpy(&(objMsg->objects[idx].dimensions_3d[0]), + &(data.dimensions[0]), + 3 * sizeof(float)); - if (mObjDetModel == sl::DETECTION_MODEL::HUMAN_BODY_ACCURATE || + if (mObjDetModel == sl::DETECTION_MODEL::HUMAN_BODY_ACCURATE || #if ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION >= 5 - mObjDetModel == sl::DETECTION_MODEL::HUMAN_BODY_MEDIUM || + mObjDetModel == sl::DETECTION_MODEL::HUMAN_BODY_MEDIUM || #endif - mObjDetModel == sl::DETECTION_MODEL::HUMAN_BODY_FAST) - { - objMsg->objects[idx].skeleton_available = true; - - if (data.head_bounding_box_2d.size() == 4) - { - memcpy(&(objMsg->objects[idx].head_bounding_box_2d.corners[0]), &(data.head_bounding_box_2d[0]), - 8 * sizeof(unsigned int)); - } - if (data.head_bounding_box.size() == 8) - { - memcpy(&(objMsg->objects[idx].head_bounding_box_3d.corners[0]), &(data.head_bounding_box[0]), - 24 * sizeof(float)); - } - memcpy(&(objMsg->objects[idx].head_position[0]), &(data.head_position[0]), 3 * sizeof(float)); + mObjDetModel == sl::DETECTION_MODEL::HUMAN_BODY_FAST) { + objMsg->objects[idx].skeleton_available = true; + + if (data.head_bounding_box_2d.size() == 4) { + memcpy(&(objMsg->objects[idx].head_bounding_box_2d.corners[0]), + &(data.head_bounding_box_2d[0]), + 8 * sizeof(unsigned int)); + } + if (data.head_bounding_box.size() == 8) { + memcpy(&(objMsg->objects[idx].head_bounding_box_3d.corners[0]), + &(data.head_bounding_box[0]), + 24 * sizeof(float)); + } + memcpy(&(objMsg->objects[idx].head_position[0]), + &(data.head_position[0]), + 3 * sizeof(float)); + + if (data.keypoint_2d.size() == 18) { + memcpy(&(objMsg->objects[idx].skeleton_2d.keypoints[0]), + &(data.keypoint_2d[0]), + 36 * sizeof(float)); + } + if (data.keypoint_2d.size() == 18) { + memcpy(&(objMsg->objects[idx].skeleton_3d.keypoints[0]), + &(data.keypoint[0]), + 54 * sizeof(float)); + } + } else { + objMsg->objects[idx].skeleton_available = false; + } - if (data.keypoint_2d.size() == 18) - { - memcpy(&(objMsg->objects[idx].skeleton_2d.keypoints[0]), &(data.keypoint_2d[0]), 36 * sizeof(float)); - } - if (data.keypoint_2d.size() == 18) - { - memcpy(&(objMsg->objects[idx].skeleton_3d.keypoints[0]), &(data.keypoint[0]), 54 * sizeof(float)); - } - } - else - { - objMsg->objects[idx].skeleton_available = false; + // at the end of the loop + idx++; } - // at the end of the loop - idx++; - } - - mPubObjDet->publish(std::move(objMsg)); + mPubObjDet->publish(std::move(objMsg)); - // ----> Diagnostic information update - mObjDetElabMean_sec->addValue(elabTimer.toc()); - mObjDetPeriodMean_sec->addValue(freqTimer.toc()); - freqTimer.tic(); - // <---- Diagnostic information update + // ----> Diagnostic information update + mObjDetElabMean_sec->addValue(elabTimer.toc()); + mObjDetPeriodMean_sec->addValue(freqTimer.toc()); + freqTimer.tic(); + // <---- Diagnostic information update } bool ZedCamera::isDepthRequired() { - if (mDepthDisabled) - { - return false; - } + if (mDepthDisabled) { + return false; + } - size_t topics_sub = 0; - try - { - topics_sub = count_subscribers(mPubDepth.getTopic()) + count_subscribers(mPubConfMap->get_topic_name()) + - count_subscribers(mPubDisparity->get_topic_name()) + count_subscribers(mPubCloud->get_topic_name()); - } - catch (...) - { - rcutils_reset_error(); - RCLCPP_DEBUG(get_logger(), "isDepthRequired: Exception while counting subscribers"); - return false; - } + size_t topics_sub = 0; + try { + topics_sub = count_subscribers(mPubDepth.getTopic()) + count_subscribers(mPubConfMap->get_topic_name()) + count_subscribers(mPubDisparity->get_topic_name()) + count_subscribers(mPubCloud->get_topic_name()); + } catch (...) { + rcutils_reset_error(); + RCLCPP_DEBUG(get_logger(), + "isDepthRequired: Exception while counting subscribers"); + return false; + } - return topics_sub > 0 || isPosTrackingRequired(); + return topics_sub > 0 || isPosTrackingRequired(); } void ZedCamera::applyDepthSettings() { - if (isDepthRequired()) - { - mDynParMutex.lock(); - mRunParams.confidence_threshold = mDepthConf; // Update depth confidence if changed - mRunParams.texture_confidence_threshold = mDepthTextConf; // Update depth texture confidence if changed - mDynParMutex.unlock(); + if (isDepthRequired()) { + mDynParMutex.lock(); + mRunParams.confidence_threshold = mDepthConf; // Update depth confidence if changed + mRunParams.texture_confidence_threshold = mDepthTextConf; // Update depth texture confidence if changed + mDynParMutex.unlock(); - mRunParams.enable_depth = true; - } - else - { - mRunParams.enable_depth = false; - } + mRunParams.enable_depth = true; + } else { + mRunParams.enable_depth = false; + } } void ZedCamera::applyVideoSettings() { - if (!mSvoMode && mFrameCount % 5 == 0) - { - mDynParMutex.lock(); - if (mCamAutoExpGain) - { - if (mTriggerAutoExpGain) - { - mZed.setCameraSettings(sl::VIDEO_SETTINGS::AEC_AGC, 1); - mTriggerAutoExpGain = false; - } - } - else - { - int exposure = mZed.getCameraSettings(sl::VIDEO_SETTINGS::EXPOSURE); - if (exposure != mCamExposure) - { - mZed.setCameraSettings(sl::VIDEO_SETTINGS::EXPOSURE, mCamExposure); - } + if (!mSvoMode && mFrameCount % 5 == 0) { + mDynParMutex.lock(); + if (mCamAutoExpGain) { + if (mTriggerAutoExpGain) { + mZed.setCameraSettings(sl::VIDEO_SETTINGS::AEC_AGC, 1); + mTriggerAutoExpGain = false; + } + } else { + int exposure = mZed.getCameraSettings(sl::VIDEO_SETTINGS::EXPOSURE); + if (exposure != mCamExposure) { + mZed.setCameraSettings(sl::VIDEO_SETTINGS::EXPOSURE, mCamExposure); + } + + int gain = mZed.getCameraSettings(sl::VIDEO_SETTINGS::GAIN); + if (gain != mCamGain) { + mZed.setCameraSettings(sl::VIDEO_SETTINGS::GAIN, mCamGain); + } + } + if (mCamAutoWB) { + if (mTriggerAutoWB) { + mZed.setCameraSettings(sl::VIDEO_SETTINGS::WHITEBALANCE_AUTO, 1); + mTriggerAutoWB = false; + } + } else { + int wb = mZed.getCameraSettings(sl::VIDEO_SETTINGS::WHITEBALANCE_TEMPERATURE); + if (wb != mCamWBTemp) { + mZed.setCameraSettings(sl::VIDEO_SETTINGS::WHITEBALANCE_TEMPERATURE, + mCamWBTemp); + } + } + int brgt = mZed.getCameraSettings(sl::VIDEO_SETTINGS::BRIGHTNESS); + if (brgt != mCamBrightness) { + mZed.setCameraSettings(sl::VIDEO_SETTINGS::BRIGHTNESS, mCamBrightness); + } + int contr = mZed.getCameraSettings(sl::VIDEO_SETTINGS::CONTRAST); + if (contr != mCamContrast) { + mZed.setCameraSettings(sl::VIDEO_SETTINGS::CONTRAST, mCamContrast); + } + int hue = mZed.getCameraSettings(sl::VIDEO_SETTINGS::HUE); + if (hue != mCamHue) { + mZed.setCameraSettings(sl::VIDEO_SETTINGS::HUE, mCamHue); + } + int sat = mZed.getCameraSettings(sl::VIDEO_SETTINGS::SATURATION); + if (sat != mCamSaturation) { + mZed.setCameraSettings(sl::VIDEO_SETTINGS::HUE, mCamSaturation); + } + int sharp = mZed.getCameraSettings(sl::VIDEO_SETTINGS::SHARPNESS); + if (sharp != mCamSharpness) { + mZed.setCameraSettings(sl::VIDEO_SETTINGS::SHARPNESS, mCamSharpness); + } + int gamma = mZed.getCameraSettings(sl::VIDEO_SETTINGS::GAMMA); + if (gamma != mCamGamma) { + mZed.setCameraSettings(sl::VIDEO_SETTINGS::GAMMA, mCamGamma); + } - int gain = mZed.getCameraSettings(sl::VIDEO_SETTINGS::GAIN); - if (gain != mCamGain) - { - mZed.setCameraSettings(sl::VIDEO_SETTINGS::GAIN, mCamGain); - } - } - if (mCamAutoWB) - { - if (mTriggerAutoWB) - { - mZed.setCameraSettings(sl::VIDEO_SETTINGS::WHITEBALANCE_AUTO, 1); - mTriggerAutoWB = false; - } - } - else - { - int wb = mZed.getCameraSettings(sl::VIDEO_SETTINGS::WHITEBALANCE_TEMPERATURE); - if (wb != mCamWBTemp) - { - mZed.setCameraSettings(sl::VIDEO_SETTINGS::WHITEBALANCE_TEMPERATURE, mCamWBTemp); - } - } - int brgt = mZed.getCameraSettings(sl::VIDEO_SETTINGS::BRIGHTNESS); - if (brgt != mCamBrightness) - { - mZed.setCameraSettings(sl::VIDEO_SETTINGS::BRIGHTNESS, mCamBrightness); - } - int contr = mZed.getCameraSettings(sl::VIDEO_SETTINGS::CONTRAST); - if (contr != mCamContrast) - { - mZed.setCameraSettings(sl::VIDEO_SETTINGS::CONTRAST, mCamContrast); - } - int hue = mZed.getCameraSettings(sl::VIDEO_SETTINGS::HUE); - if (hue != mCamHue) - { - mZed.setCameraSettings(sl::VIDEO_SETTINGS::HUE, mCamHue); - } - int sat = mZed.getCameraSettings(sl::VIDEO_SETTINGS::SATURATION); - if (sat != mCamSaturation) - { - mZed.setCameraSettings(sl::VIDEO_SETTINGS::HUE, mCamSaturation); - } - int sharp = mZed.getCameraSettings(sl::VIDEO_SETTINGS::SHARPNESS); - if (sharp != mCamSharpness) - { - mZed.setCameraSettings(sl::VIDEO_SETTINGS::SHARPNESS, mCamSharpness); - } - int gamma = mZed.getCameraSettings(sl::VIDEO_SETTINGS::GAMMA); - if (gamma != mCamGamma) - { - mZed.setCameraSettings(sl::VIDEO_SETTINGS::GAMMA, mCamGamma); + mDynParMutex.unlock(); } - - mDynParMutex.unlock(); - } } bool ZedCamera::isPosTrackingRequired() { - if (mDepthDisabled) - { - return false; - } - - if (mPosTrackingEnabled) - { - return true; - } + if (mDepthDisabled) { + return false; + } - if (mPublishTF) - { - return true; - } + if (mPosTrackingEnabled) { + return true; + } - if (mDepthStabilization) - { - return true; - } + if (mPublishTF) { + return true; + } - if (mMappingEnabled || mObjDetEnabled) - { - return true; - } + if (mDepthStabilization) { + return true; + } - size_t topics_sub = 0; - try - { - topics_sub = count_subscribers(mPubPose->get_topic_name()) + +count_subscribers(mPubPoseCov->get_topic_name()) + - count_subscribers(mPubPosePath->get_topic_name()) + count_subscribers(mPubOdom->get_topic_name()) + - count_subscribers(mPubOdomPath->get_topic_name()); - } - catch (...) - { - rcutils_reset_error(); - RCLCPP_DEBUG(get_logger(), "isPosTrackingRequired: Exception while counting subscribers"); - return false; - } + if (mMappingEnabled || mObjDetEnabled) { + return true; + } - if (topics_sub > 0) - { - return true; - } + size_t topics_sub = 0; + try { + topics_sub = count_subscribers(mPubPose->get_topic_name()) + +count_subscribers(mPubPoseCov->get_topic_name()) + count_subscribers(mPubPosePath->get_topic_name()) + count_subscribers(mPubOdom->get_topic_name()) + count_subscribers(mPubOdomPath->get_topic_name()); + } catch (...) { + rcutils_reset_error(); + RCLCPP_DEBUG(get_logger(), + "isPosTrackingRequired: Exception while counting subscribers"); + return false; + } + + if (topics_sub > 0) { + return true; + } - return false; + return false; } void ZedCamera::publishDepthMapWithInfo(sl::Mat& depth, rclcpp::Time t) { - mDepthCamInfoMsg->header.stamp = t; + mDepthCamInfoMsg->header.stamp = t; - if (!mOpenniDepthMode) - { - auto depth_img = sl_tools::imageToROSmsg(depth, mDepthOptFrameId, t); - mPubDepth.publish(depth_img, mDepthCamInfoMsg); // TODO CHECK FOR ZERO-COPY - return; - } + if (!mOpenniDepthMode) { + auto depth_img = sl_tools::imageToROSmsg(depth, mDepthOptFrameId, t); + mPubDepth.publish(depth_img, mDepthCamInfoMsg); // TODO CHECK FOR ZERO-COPY + return; + } - // OPENNI CONVERSION (meter -> millimeters - float32 -> uint16) - std::shared_ptr openniDepthMsg = std::make_shared(); + // OPENNI CONVERSION (meter -> millimeters - float32 -> uint16) + std::shared_ptr openniDepthMsg = std::make_shared(); - openniDepthMsg->header.stamp = t; - openniDepthMsg->header.frame_id = mDepthOptFrameId; - openniDepthMsg->height = depth.getHeight(); - openniDepthMsg->width = depth.getWidth(); + openniDepthMsg->header.stamp = t; + openniDepthMsg->header.frame_id = mDepthOptFrameId; + openniDepthMsg->height = depth.getHeight(); + openniDepthMsg->width = depth.getWidth(); - int num = 1; // for endianness detection - openniDepthMsg->is_bigendian = !(*(char*)&num == 1); + int num = 1; // for endianness detection + openniDepthMsg->is_bigendian = !(*(char*)&num == 1); - openniDepthMsg->step = openniDepthMsg->width * sizeof(uint16_t); - openniDepthMsg->encoding = sensor_msgs::image_encodings::MONO16; + openniDepthMsg->step = openniDepthMsg->width * sizeof(uint16_t); + openniDepthMsg->encoding = sensor_msgs::image_encodings::MONO16; - size_t size = openniDepthMsg->step * openniDepthMsg->height; - openniDepthMsg->data.resize(size); + size_t size = openniDepthMsg->step * openniDepthMsg->height; + openniDepthMsg->data.resize(size); - uint16_t* data = (uint16_t*)(&openniDepthMsg->data[0]); + uint16_t* data = (uint16_t*)(&openniDepthMsg->data[0]); - int dataSize = openniDepthMsg->width * openniDepthMsg->height; - sl::float1* depthDataPtr = depth.getPtr(); + int dataSize = openniDepthMsg->width * openniDepthMsg->height; + sl::float1* depthDataPtr = depth.getPtr(); - for (int i = 0; i < dataSize; i++) - { - *(data++) = static_cast(std::round(*(depthDataPtr++) * 1000)); // in mm, rounded - } + for (int i = 0; i < dataSize; i++) { + *(data++) = static_cast( + std::round(*(depthDataPtr++) * 1000)); // in mm, rounded + } - mPubDepth.publish(openniDepthMsg, mDepthCamInfoMsg); + mPubDepth.publish(openniDepthMsg, mDepthCamInfoMsg); } void ZedCamera::publishDisparity(sl::Mat disparity, rclcpp::Time t) { - sl::CameraInformation zedParam = mZed.getCameraInformation(mMatResolDepth); + sl::CameraInformation zedParam = mZed.getCameraInformation(mMatResolDepth); - std::shared_ptr disparity_image = sl_tools::imageToROSmsg(disparity, mDepthOptFrameId, t); + std::shared_ptr disparity_image = sl_tools::imageToROSmsg(disparity, mDepthOptFrameId, t); - dispMsgPtr disparityMsg = std::make_unique(); - disparityMsg->image = *disparity_image.get(); - disparityMsg->header = disparityMsg->image.header; + dispMsgPtr disparityMsg = std::make_unique(); + disparityMsg->image = *disparity_image.get(); + disparityMsg->header = disparityMsg->image.header; #if ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION < 1 - disparityMsg->f = zedParam.calibration_parameters.left_cam.fx; - disparityMsg->t = zedParam.calibration_parameters.T.x; + disparityMsg->f = zedParam.calibration_parameters.left_cam.fx; + disparityMsg->t = zedParam.calibration_parameters.T.x; #else - disparityMsg->f = zedParam.camera_configuration.calibration_parameters.left_cam.fx; - disparityMsg->t = zedParam.camera_configuration.calibration_parameters.getCameraBaseline(); + disparityMsg->f = zedParam.camera_configuration.calibration_parameters.left_cam.fx; + disparityMsg->t = zedParam.camera_configuration.calibration_parameters.getCameraBaseline(); #endif - disparityMsg->min_disparity = disparityMsg->f * disparityMsg->t / mZed.getInitParameters().depth_minimum_distance; - disparityMsg->max_disparity = disparityMsg->f * disparityMsg->t / mZed.getInitParameters().depth_maximum_distance; + disparityMsg->min_disparity = disparityMsg->f * disparityMsg->t / mZed.getInitParameters().depth_minimum_distance; + disparityMsg->max_disparity = disparityMsg->f * disparityMsg->t / mZed.getInitParameters().depth_maximum_distance; - mPubDisparity->publish(std::move(disparityMsg)); + mPubDisparity->publish(std::move(disparityMsg)); } void ZedCamera::publishPointCloud() { - sl_tools::StopWatch elabTimer; + sl_tools::StopWatch elabTimer; - pointcloudMsgPtr pcMsg = std::make_unique(); + pointcloudMsgPtr pcMsg = std::make_unique(); - // Initialize Point Cloud message - // https://github.com/ros/common_msgs/blob/jade-devel/sensor_msgs/include/sensor_msgs/point_cloud2_iterator.h + // Initialize Point Cloud message + // https://github.com/ros/common_msgs/blob/jade-devel/sensor_msgs/include/sensor_msgs/point_cloud2_iterator.h - int width = mMatResolDepth.width; - int height = mMatResolDepth.height; + int width = mMatResolDepth.width; + int height = mMatResolDepth.height; - int ptsCount = width * height; + int ptsCount = width * height; - if (!mSvoMode) - { - pcMsg->header.stamp = sl_tools::slTime2Ros(mMatCloud.timestamp); - } - else - { - pcMsg->header.stamp = sl_tools::slTime2Ros(mZed.getTimestamp(sl::TIME_REFERENCE::CURRENT)); - ; - } + if (!mSvoMode) { + pcMsg->header.stamp = sl_tools::slTime2Ros(mMatCloud.timestamp); + } else { + pcMsg->header.stamp = sl_tools::slTime2Ros(mZed.getTimestamp(sl::TIME_REFERENCE::CURRENT)); + ; + } - if (pcMsg->width != width || pcMsg->height != height) - { - pcMsg->header.frame_id = mPointCloudFrameId; // Set the header values of the ROS message + if (pcMsg->width != width || pcMsg->height != height) { + pcMsg->header.frame_id = mPointCloudFrameId; // Set the header values of the ROS message - pcMsg->is_bigendian = false; - pcMsg->is_dense = false; + pcMsg->is_bigendian = false; + pcMsg->is_dense = false; - pcMsg->width = width; - pcMsg->height = height; + pcMsg->width = width; + pcMsg->height = height; - sensor_msgs::PointCloud2Modifier modifier(*(pcMsg.get())); - modifier.setPointCloud2Fields(4, "x", 1, sensor_msgs::msg::PointField::FLOAT32, "y", 1, - sensor_msgs::msg::PointField::FLOAT32, "z", 1, sensor_msgs::msg::PointField::FLOAT32, - "rgb", 1, sensor_msgs::msg::PointField::FLOAT32); - } + sensor_msgs::PointCloud2Modifier modifier(*(pcMsg.get())); + modifier.setPointCloud2Fields(4, + "x", + 1, + sensor_msgs::msg::PointField::FLOAT32, + "y", + 1, + sensor_msgs::msg::PointField::FLOAT32, + "z", + 1, + sensor_msgs::msg::PointField::FLOAT32, + "rgb", + 1, + sensor_msgs::msg::PointField::FLOAT32); + } - sl::Vector4* cpu_cloud = mMatCloud.getPtr(); + sl::Vector4* cpu_cloud = mMatCloud.getPtr(); - // Data copy - float* ptCloudPtr = (float*)(&pcMsg->data[0]); - memcpy(ptCloudPtr, (float*)cpu_cloud, ptsCount * 4 * sizeof(float)); + // Data copy + float* ptCloudPtr = (float*)(&pcMsg->data[0]); + memcpy(ptCloudPtr, (float*)cpu_cloud, ptsCount * 4 * sizeof(float)); - // Pointcloud publishing - mPubCloud->publish(std::move(pcMsg)); + // Pointcloud publishing + mPubCloud->publish(std::move(pcMsg)); - // Publish freq calculation - static sl_tools::StopWatch freqTimer; - double mean = mPcPeriodMean_sec->addValue(freqTimer.toc()); - freqTimer.tic(); + // Publish freq calculation + static sl_tools::StopWatch freqTimer; + double mean = mPcPeriodMean_sec->addValue(freqTimer.toc()); + freqTimer.tic(); - // Point cloud elaboration time - mPcProcMean_sec->addValue(elabTimer.toc()); - // RCLCPP_INFO_STREAM(get_logger(), "Point cloud freq: " << 1e6/mean); + // Point cloud elaboration time + mPcProcMean_sec->addValue(elabTimer.toc()); + // RCLCPP_INFO_STREAM(get_logger(), "Point cloud freq: " << 1e6/mean); } void ZedCamera::callback_pubFusedPc() { - RCLCPP_DEBUG_ONCE(get_logger(), "Mapping callback called"); - - static rclcpp::Time prev_ts = TIMEZERO_ROS; - - pointcloudMsgPtr pointcloudFusedMsg = std::make_unique(); - - uint32_t fusedCloudSubnumber = 0; - try - { - fusedCloudSubnumber = count_subscribers(mPubFusedCloud->get_topic_name()); - } - catch (...) - { - rcutils_reset_error(); - RCLCPP_DEBUG(get_logger(), "pubFusedPc: Exception while counting subscribers"); - return; - } - - if (fusedCloudSubnumber == 0) - { - return; - } - - std::lock_guard lock(mCloseZedMutex); + RCLCPP_DEBUG_ONCE(get_logger(), "Mapping callback called"); - if (!mZed.isOpened()) - { - return; - } + static rclcpp::Time prev_ts = TIMEZERO_ROS; - mZed.requestSpatialMapAsync(); + pointcloudMsgPtr pointcloudFusedMsg = std::make_unique(); - while (mZed.getSpatialMapRequestStatusAsync() == sl::ERROR_CODE::FAILURE) - { - // Mesh is still generating - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } + uint32_t fusedCloudSubnumber = 0; + try { + fusedCloudSubnumber = count_subscribers(mPubFusedCloud->get_topic_name()); + } catch (...) { + rcutils_reset_error(); + RCLCPP_DEBUG(get_logger(), + "pubFusedPc: Exception while counting subscribers"); + return; + } - sl::ERROR_CODE res = mZed.retrieveSpatialMapAsync(mFusedPC); + if (fusedCloudSubnumber == 0) { + return; + } - if (res != sl::ERROR_CODE::SUCCESS) - { - RCLCPP_WARN_STREAM(get_logger(), "Fused point cloud not extracted: " << sl::toString(res).c_str()); - return; - } + std::lock_guard lock(mCloseZedMutex); - size_t ptsCount = mFusedPC.getNumberOfPoints(); - bool resized = false; + if (!mZed.isOpened()) { + return; + } - if (pointcloudFusedMsg->width != ptsCount || pointcloudFusedMsg->height != 1) - { - // Initialize Point Cloud message - // https://github.com/ros/common_msgs/blob/jade-devel/sensor_msgs/include/sensor_msgs/point_cloud2_iterator.h - pointcloudFusedMsg->header.frame_id = mMapFrameId; // Set the header values of the ROS message - pointcloudFusedMsg->is_bigendian = false; - pointcloudFusedMsg->is_dense = false; - pointcloudFusedMsg->width = ptsCount; - pointcloudFusedMsg->height = 1; - - sensor_msgs::PointCloud2Modifier modifier(*pointcloudFusedMsg); - modifier.setPointCloud2Fields(4, "x", 1, sensor_msgs::msg::PointField::FLOAT32, "y", 1, - sensor_msgs::msg::PointField::FLOAT32, "z", 1, sensor_msgs::msg::PointField::FLOAT32, - "rgb", 1, sensor_msgs::msg::PointField::FLOAT32); - - resized = true; - } + mZed.requestSpatialMapAsync(); - int index = 0; - float* ptCloudPtr = (float*)(&pointcloudFusedMsg->data[0]); - int updated = 0; + while (mZed.getSpatialMapRequestStatusAsync() == sl::ERROR_CODE::FAILURE) { + // Mesh is still generating + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } - for (int c = 0; c < mFusedPC.chunks.size(); c++) - { - if (mFusedPC.chunks[c].has_been_updated || resized) - { - updated++; - size_t chunkSize = mFusedPC.chunks[c].vertices.size(); + sl::ERROR_CODE res = mZed.retrieveSpatialMapAsync(mFusedPC); - if (chunkSize > 0) - { - float* cloud_pts = (float*)(mFusedPC.chunks[c].vertices.data()); - memcpy(ptCloudPtr, cloud_pts, 4 * chunkSize * sizeof(float)); - ptCloudPtr += 4 * chunkSize; - pointcloudFusedMsg->header.stamp = sl_tools::slTime2Ros(mFusedPC.chunks[c].timestamp); - } + if (res != sl::ERROR_CODE::SUCCESS) { + RCLCPP_WARN_STREAM( + get_logger(), + "Fused point cloud not extracted: " << sl::toString(res).c_str()); + return; } - else - { - index += mFusedPC.chunks[c].vertices.size(); + + size_t ptsCount = mFusedPC.getNumberOfPoints(); + bool resized = false; + + if (pointcloudFusedMsg->width != ptsCount || pointcloudFusedMsg->height != 1) { + // Initialize Point Cloud message + // https://github.com/ros/common_msgs/blob/jade-devel/sensor_msgs/include/sensor_msgs/point_cloud2_iterator.h + pointcloudFusedMsg->header.frame_id = mMapFrameId; // Set the header values of the ROS message + pointcloudFusedMsg->is_bigendian = false; + pointcloudFusedMsg->is_dense = false; + pointcloudFusedMsg->width = ptsCount; + pointcloudFusedMsg->height = 1; + + sensor_msgs::PointCloud2Modifier modifier(*pointcloudFusedMsg); + modifier.setPointCloud2Fields(4, + "x", + 1, + sensor_msgs::msg::PointField::FLOAT32, + "y", + 1, + sensor_msgs::msg::PointField::FLOAT32, + "z", + 1, + sensor_msgs::msg::PointField::FLOAT32, + "rgb", + 1, + sensor_msgs::msg::PointField::FLOAT32); + + resized = true; + } + + int index = 0; + float* ptCloudPtr = (float*)(&pointcloudFusedMsg->data[0]); + int updated = 0; + + for (int c = 0; c < mFusedPC.chunks.size(); c++) { + if (mFusedPC.chunks[c].has_been_updated || resized) { + updated++; + size_t chunkSize = mFusedPC.chunks[c].vertices.size(); + + if (chunkSize > 0) { + float* cloud_pts = (float*)(mFusedPC.chunks[c].vertices.data()); + memcpy(ptCloudPtr, cloud_pts, 4 * chunkSize * sizeof(float)); + ptCloudPtr += 4 * chunkSize; + pointcloudFusedMsg->header.stamp = sl_tools::slTime2Ros(mFusedPC.chunks[c].timestamp); + } + } else { + index += mFusedPC.chunks[c].vertices.size(); + } } - } - rclcpp::Time ros_ts = get_clock()->now(); + rclcpp::Time ros_ts = get_clock()->now(); - // RCLCPP_INFO_STREAM(get_logger(), "callback_pubFusedPc - prev_ts type:" << prev_ts.get_clock_type()); + // RCLCPP_INFO_STREAM(get_logger(), "callback_pubFusedPc - prev_ts type:" << + // prev_ts.get_clock_type()); - if (prev_ts != TIMEZERO_ROS) - { - double mean = mPubFusedCloudPeriodMean_sec->addValue((ros_ts - prev_ts).seconds()); - // RCLCPP_INFO_STREAM( get_logger(),"Fused Cloud Pub freq: " << 1.0/mean ); - } - prev_ts = ros_ts; + if (prev_ts != TIMEZERO_ROS) { + double mean = mPubFusedCloudPeriodMean_sec->addValue((ros_ts - prev_ts).seconds()); + // RCLCPP_INFO_STREAM( get_logger(),"Fused Cloud Pub freq: " << 1.0/mean ); + } + prev_ts = ros_ts; - // Pointcloud publishing - mPubFusedCloud->publish(std::move(pointcloudFusedMsg)); + // Pointcloud publishing + mPubFusedCloud->publish(std::move(pointcloudFusedMsg)); } void ZedCamera::callback_pubPaths() { - uint32_t mapPathSub = 0; - uint32_t odomPathSub = 0; - - try - { - mapPathSub = count_subscribers(mMapPathTopic); - odomPathSub = count_subscribers(mOdomPathTopic); - } - catch (...) - { - rcutils_reset_error(); - RCLCPP_DEBUG(get_logger(), "pubPaths: Exception while counting subscribers"); - return; - } - - geometry_msgs::msg::PoseStamped odomPose; - geometry_msgs::msg::PoseStamped mapPose; - - odomPose.header.stamp = mFrameTimestamp; - odomPose.header.frame_id = mMapFrameId; // map_frame - odomPose.pose.position.x = mOdom2BaseTransf.getOrigin().x(); - odomPose.pose.position.y = mOdom2BaseTransf.getOrigin().y(); - odomPose.pose.position.z = mOdom2BaseTransf.getOrigin().z(); - odomPose.pose.orientation.x = mOdom2BaseTransf.getRotation().x(); - odomPose.pose.orientation.y = mOdom2BaseTransf.getRotation().y(); - odomPose.pose.orientation.z = mOdom2BaseTransf.getRotation().z(); - odomPose.pose.orientation.w = mOdom2BaseTransf.getRotation().w(); - - mapPose.header.stamp = mFrameTimestamp; - mapPose.header.frame_id = mMapFrameId; // map_frame - mapPose.pose.position.x = mMap2BaseTransf.getOrigin().x(); - mapPose.pose.position.y = mMap2BaseTransf.getOrigin().y(); - mapPose.pose.position.z = mMap2BaseTransf.getOrigin().z(); - mapPose.pose.orientation.x = mMap2BaseTransf.getRotation().x(); - mapPose.pose.orientation.y = mMap2BaseTransf.getRotation().y(); - mapPose.pose.orientation.z = mMap2BaseTransf.getRotation().z(); - mapPose.pose.orientation.w = mMap2BaseTransf.getRotation().w(); - - // Circular vector - if (mPathMaxCount != -1) - { - if (mOdomPath.size() == mPathMaxCount) - { - RCLCPP_DEBUG(get_logger(), "Path vectors full: rotating "); - std::rotate(mOdomPath.begin(), mOdomPath.begin() + 1, mOdomPath.end()); - std::rotate(mMapPath.begin(), mMapPath.begin() + 1, mMapPath.end()); + uint32_t mapPathSub = 0; + uint32_t odomPathSub = 0; - mMapPath[mPathMaxCount - 1] = mapPose; - mOdomPath[mPathMaxCount - 1] = odomPose; + try { + mapPathSub = count_subscribers(mMapPathTopic); + odomPathSub = count_subscribers(mOdomPathTopic); + } catch (...) { + rcutils_reset_error(); + RCLCPP_DEBUG(get_logger(), + "pubPaths: Exception while counting subscribers"); + return; } - else - { - // RCLCPP_DEBUG(get_logger(), "Path vectors adding last available poses"); - mMapPath.push_back(mapPose); - mOdomPath.push_back(odomPose); + + geometry_msgs::msg::PoseStamped odomPose; + geometry_msgs::msg::PoseStamped mapPose; + + odomPose.header.stamp = mFrameTimestamp; + odomPose.header.frame_id = mMapFrameId; // map_frame + odomPose.pose.position.x = mOdom2BaseTransf.getOrigin().x(); + odomPose.pose.position.y = mOdom2BaseTransf.getOrigin().y(); + odomPose.pose.position.z = mOdom2BaseTransf.getOrigin().z(); + odomPose.pose.orientation.x = mOdom2BaseTransf.getRotation().x(); + odomPose.pose.orientation.y = mOdom2BaseTransf.getRotation().y(); + odomPose.pose.orientation.z = mOdom2BaseTransf.getRotation().z(); + odomPose.pose.orientation.w = mOdom2BaseTransf.getRotation().w(); + + mapPose.header.stamp = mFrameTimestamp; + mapPose.header.frame_id = mMapFrameId; // map_frame + mapPose.pose.position.x = mMap2BaseTransf.getOrigin().x(); + mapPose.pose.position.y = mMap2BaseTransf.getOrigin().y(); + mapPose.pose.position.z = mMap2BaseTransf.getOrigin().z(); + mapPose.pose.orientation.x = mMap2BaseTransf.getRotation().x(); + mapPose.pose.orientation.y = mMap2BaseTransf.getRotation().y(); + mapPose.pose.orientation.z = mMap2BaseTransf.getRotation().z(); + mapPose.pose.orientation.w = mMap2BaseTransf.getRotation().w(); + + // Circular vector + if (mPathMaxCount != -1) { + if (mOdomPath.size() == mPathMaxCount) { + RCLCPP_DEBUG(get_logger(), "Path vectors full: rotating "); + std::rotate(mOdomPath.begin(), mOdomPath.begin() + 1, mOdomPath.end()); + std::rotate(mMapPath.begin(), mMapPath.begin() + 1, mMapPath.end()); + + mMapPath[mPathMaxCount - 1] = mapPose; + mOdomPath[mPathMaxCount - 1] = odomPose; + } else { + // RCLCPP_DEBUG(get_logger(), "Path vectors adding last available poses"); + mMapPath.push_back(mapPose); + mOdomPath.push_back(odomPose); + } + } else { + // RCLCPP_DEBUG(get_logger(), "No limit path vectors, adding last available + // poses"); + mMapPath.push_back(mapPose); + mOdomPath.push_back(odomPose); } - } - else - { - // RCLCPP_DEBUG(get_logger(), "No limit path vectors, adding last available poses"); - mMapPath.push_back(mapPose); - mOdomPath.push_back(odomPose); - } - if (mapPathSub > 0) - { - pathMsgPtr mapPath = std::make_unique(); - mapPath->header.frame_id = mMapFrameId; - mapPath->header.stamp = mFrameTimestamp; - mapPath->poses = mMapPath; + if (mapPathSub > 0) { + pathMsgPtr mapPath = std::make_unique(); + mapPath->header.frame_id = mMapFrameId; + mapPath->header.stamp = mFrameTimestamp; + mapPath->poses = mMapPath; - mPubPosePath->publish(std::move(mapPath)); - } + mPubPosePath->publish(std::move(mapPath)); + } - if (odomPathSub > 0) - { - pathMsgPtr odomPath = std::make_unique(); - odomPath->header.frame_id = mMapFrameId; - odomPath->header.stamp = mFrameTimestamp; - odomPath->poses = mOdomPath; + if (odomPathSub > 0) { + pathMsgPtr odomPath = std::make_unique(); + odomPath->header.frame_id = mMapFrameId; + odomPath->header.stamp = mFrameTimestamp; + odomPath->poses = mOdomPath; - mPubOdomPath->publish(std::move(odomPath)); - } + mPubOdomPath->publish(std::move(odomPath)); + } } -void ZedCamera::callback_resetOdometry(const std::shared_ptr request_header, - const std::shared_ptr req, - std::shared_ptr res) +void ZedCamera::callback_resetOdometry( + const std::shared_ptr request_header, + const std::shared_ptr req, + std::shared_ptr res) { - (void)request_header; - (void)req; + (void)request_header; + (void)req; - RCLCPP_INFO(get_logger(), "** Reset Odometry service called **"); - mResetOdom = true; - res->message = "Odometry reset OK"; - res->success = true; + RCLCPP_INFO(get_logger(), "** Reset Odometry service called **"); + mResetOdom = true; + res->message = "Odometry reset OK"; + res->success = true; } -void ZedCamera::callback_resetPosTracking(const std::shared_ptr request_header, - const std::shared_ptr req, - std::shared_ptr res) +void ZedCamera::callback_resetPosTracking( + const std::shared_ptr request_header, + const std::shared_ptr req, + std::shared_ptr res) { - (void)request_header; - (void)req; + (void)request_header; + (void)req; - RCLCPP_INFO(get_logger(), "** Reset Pos. Tracking service called **"); + RCLCPP_INFO(get_logger(), "** Reset Pos. Tracking service called **"); - if (!mPosTrackingStarted) - { - RCLCPP_WARN(get_logger(), "Pos. Tracking was not started"); - res->message = "Positional tracking not active"; - res->success = false; - return; - } + if (!mPosTrackingStarted) { + RCLCPP_WARN(get_logger(), "Pos. Tracking was not started"); + res->message = "Positional tracking not active"; + res->success = false; + return; + } - if (!setPose(mInitialBasePose[0], mInitialBasePose[1], mInitialBasePose[2], mInitialBasePose[3], mInitialBasePose[4], - mInitialBasePose[5])) - { - res->message = "Error setting initial pose"; - RCLCPP_WARN(get_logger(), "Error setting initial pose"); - res->success = false; - return; - } + if (!setPose(mInitialBasePose[0], + mInitialBasePose[1], + mInitialBasePose[2], + mInitialBasePose[3], + mInitialBasePose[4], + mInitialBasePose[5])) { + res->message = "Error setting initial pose"; + RCLCPP_WARN(get_logger(), "Error setting initial pose"); + res->success = false; + return; + } - std::lock_guard lock(mPosTrkMutex); + std::lock_guard lock(mPosTrkMutex); - mInitOdomWithPose = true; + mInitOdomWithPose = true; - // Disable tracking - mPosTrackingStarted = false; - mZed.disablePositionalTracking(); + // Disable tracking + mPosTrackingStarted = false; + mZed.disablePositionalTracking(); - // Restart tracking - startPosTracking(); + // Restart tracking + startPosTracking(); - res->message = "Positional tracking reset OK"; - res->success = true; + res->message = "Positional tracking reset OK"; + res->success = true; } -void ZedCamera::callback_setPose(const std::shared_ptr request_header, - const std::shared_ptr req, - std::shared_ptr res) +void ZedCamera::callback_setPose( + const std::shared_ptr request_header, + const std::shared_ptr req, + std::shared_ptr res) { - (void)request_header; - - RCLCPP_INFO(get_logger(), "** Set Pose service called **"); - RCLCPP_INFO_STREAM(get_logger(), "New pose: [" << req->pos[0] << "," << req->pos[1] << "," << req->pos[2] << ", " - << req->orient[0] << "," << req->orient[1] << "," << req->orient[2] - << "]"); + (void)request_header; - if (!mPosTrackingStarted) - { - RCLCPP_WARN(get_logger(), "Pos. Tracking was not active"); - res->message = "Positional tracking not active"; - res->success = false; - return; - } + RCLCPP_INFO(get_logger(), "** Set Pose service called **"); + RCLCPP_INFO_STREAM(get_logger(), + "New pose: [" << req->pos[0] << "," << req->pos[1] << "," + << req->pos[2] << ", " << req->orient[0] + << "," << req->orient[1] << "," + << req->orient[2] << "]"); + + if (!mPosTrackingStarted) { + RCLCPP_WARN(get_logger(), "Pos. Tracking was not active"); + res->message = "Positional tracking not active"; + res->success = false; + return; + } - mInitOdomWithPose = true; + mInitOdomWithPose = true; - mInitialBasePose[0] = req->pos[0]; - mInitialBasePose[1] = req->pos[1]; - mInitialBasePose[2] = req->pos[2]; + mInitialBasePose[0] = req->pos[0]; + mInitialBasePose[1] = req->pos[1]; + mInitialBasePose[2] = req->pos[2]; - mInitialBasePose[3] = req->orient[0]; - mInitialBasePose[4] = req->orient[1]; - mInitialBasePose[5] = req->orient[2]; + mInitialBasePose[3] = req->orient[0]; + mInitialBasePose[4] = req->orient[1]; + mInitialBasePose[5] = req->orient[2]; - if (!setPose(mInitialBasePose[0], mInitialBasePose[1], mInitialBasePose[2], mInitialBasePose[3], mInitialBasePose[4], - mInitialBasePose[5])) - { - res->message = "Error setting initial pose"; - RCLCPP_WARN(get_logger(), "Error setting initial pose"); - res->success = false; - return; - } + if (!setPose(mInitialBasePose[0], + mInitialBasePose[1], + mInitialBasePose[2], + mInitialBasePose[3], + mInitialBasePose[4], + mInitialBasePose[5])) { + res->message = "Error setting initial pose"; + RCLCPP_WARN(get_logger(), "Error setting initial pose"); + res->success = false; + return; + } - std::lock_guard lock(mPosTrkMutex); + std::lock_guard lock(mPosTrkMutex); - // Disable tracking - mPosTrackingStarted = false; - mZed.disablePositionalTracking(); + // Disable tracking + mPosTrackingStarted = false; + mZed.disablePositionalTracking(); - // Restart tracking - startPosTracking(); + // Restart tracking + startPosTracking(); - res->message = "Positional Tracking new pose OK"; - res->success = true; + res->message = "Positional Tracking new pose OK"; + res->success = true; } -void ZedCamera::callback_enableObjDet(const std::shared_ptr request_header, - const std::shared_ptr req, - std::shared_ptr res) +void ZedCamera::callback_enableObjDet( + const std::shared_ptr request_header, + const std::shared_ptr req, + std::shared_ptr res) { - (void)request_header; + (void)request_header; - RCLCPP_INFO(get_logger(), "** Enable Object Detection service called **"); + RCLCPP_INFO(get_logger(), "** Enable Object Detection service called **"); - std::lock_guard lock(mObjDetMutex); - - if (mCamRealModel == sl::MODEL::ZED || mCamRealModel == sl::MODEL::ZED_M) - { - RCLCPP_WARN(get_logger(), "Object Detection not available for ZED or ZED Mini"); - res->message = "Object Detection not available for ZED or ZED Mini"; - res->success = false; - return; - } + std::lock_guard lock(mObjDetMutex); - if (req->data) - { - RCLCPP_INFO(get_logger(), "Starting Object Detection"); - // Start - if (mObjDetEnabled && mObjDetRunning) - { - RCLCPP_WARN(get_logger(), "Object Detection is just running"); - res->message = "Object Detection is just running"; - res->success = false; - return; + if (mCamRealModel == sl::MODEL::ZED || mCamRealModel == sl::MODEL::ZED_M) { + RCLCPP_WARN(get_logger(), + "Object Detection not available for ZED or ZED Mini"); + res->message = "Object Detection not available for ZED or ZED Mini"; + res->success = false; + return; } - mObjDetEnabled = true; + if (req->data) { + RCLCPP_INFO(get_logger(), "Starting Object Detection"); + // Start + if (mObjDetEnabled && mObjDetRunning) { + RCLCPP_WARN(get_logger(), "Object Detection is just running"); + res->message = "Object Detection is just running"; + res->success = false; + return; + } - if (startObjDetect()) - { - res->message = "Object Detection started"; - res->success = true; - return; - } - else - { - res->message = "Error occurred starting Object Detection. See log for more info"; - res->success = false; - return; - } - } - else - { - RCLCPP_INFO(get_logger(), "Stopping Object Detection"); - // Stop - if (!mObjDetEnabled || !mObjDetRunning) - { - RCLCPP_WARN(get_logger(), "Object Detection was not running"); - res->message = "Object Detection was not running"; - res->success = false; - return; - } + mObjDetEnabled = true; - stopObjDetect(); + if (startObjDetect()) { + res->message = "Object Detection started"; + res->success = true; + return; + } else { + res->message = "Error occurred starting Object Detection. See log for more info"; + res->success = false; + return; + } + } else { + RCLCPP_INFO(get_logger(), "Stopping Object Detection"); + // Stop + if (!mObjDetEnabled || !mObjDetRunning) { + RCLCPP_WARN(get_logger(), "Object Detection was not running"); + res->message = "Object Detection was not running"; + res->success = false; + return; + } - res->message = "Object Detection stopped"; - res->success = true; - return; - } + stopObjDetect(); + + res->message = "Object Detection stopped"; + res->success = true; + return; + } } -void ZedCamera::callback_enableMapping(const std::shared_ptr request_header, - const std::shared_ptr req, - std::shared_ptr res) +void ZedCamera::callback_enableMapping( + const std::shared_ptr request_header, + const std::shared_ptr req, + std::shared_ptr res) { - (void)request_header; + (void)request_header; - RCLCPP_INFO(get_logger(), "** Enable Spatial Mapping service called **"); + RCLCPP_INFO(get_logger(), "** Enable Spatial Mapping service called **"); - std::lock_guard lock(mMappingMutex); + std::lock_guard lock(mMappingMutex); - if (req->data) - { - RCLCPP_INFO(get_logger(), "Starting Spatial Mapping"); - // Start - if (mMappingEnabled && mMappingRunning) - { - RCLCPP_WARN(get_logger(), "Spatial Mapping is just running"); - res->message = "Spatial Mapping is just running"; - res->success = false; - return; - } + if (req->data) { + RCLCPP_INFO(get_logger(), "Starting Spatial Mapping"); + // Start + if (mMappingEnabled && mMappingRunning) { + RCLCPP_WARN(get_logger(), "Spatial Mapping is just running"); + res->message = "Spatial Mapping is just running"; + res->success = false; + return; + } - mMappingEnabled = true; + mMappingEnabled = true; - if (start3dMapping()) - { - res->message = "Spatial Mapping started"; - res->success = true; - return; - } - else - { - res->message = "Error occurred starting Spatial Mapping. See log for more info"; - res->success = false; - return; - } - } - else - { - RCLCPP_INFO(get_logger(), "Stopping Spatial Mapping"); - // Stop - if (!mMappingEnabled || !mMappingRunning) - { - RCLCPP_WARN(get_logger(), "Spatial Mapping was not running"); - res->message = "Spatial Mapping was not running"; - res->success = false; - return; - } + if (start3dMapping()) { + res->message = "Spatial Mapping started"; + res->success = true; + return; + } else { + res->message = "Error occurred starting Spatial Mapping. See log for more info"; + res->success = false; + return; + } + } else { + RCLCPP_INFO(get_logger(), "Stopping Spatial Mapping"); + // Stop + if (!mMappingEnabled || !mMappingRunning) { + RCLCPP_WARN(get_logger(), "Spatial Mapping was not running"); + res->message = "Spatial Mapping was not running"; + res->success = false; + return; + } - stop3dMapping(); + stop3dMapping(); - res->message = "Spatial Mapping stopped"; - res->success = true; - return; - } + res->message = "Spatial Mapping stopped"; + res->success = true; + return; + } } -void ZedCamera::callback_startSvoRec(const std::shared_ptr request_header, - const std::shared_ptr req, - std::shared_ptr res) +void ZedCamera::callback_startSvoRec( + const std::shared_ptr request_header, + const std::shared_ptr req, + std::shared_ptr res) { - (void)request_header; + (void)request_header; - RCLCPP_INFO(get_logger(), "** Start SVO Recording service called **"); + RCLCPP_INFO(get_logger(), "** Start SVO Recording service called **"); - if (mSvoMode) - { - RCLCPP_WARN(get_logger(), "Cannot start SVO recording while playing SVO as input"); - res->message = "Cannot start SVO recording while playing SVO as input"; - res->success = false; - return; - } + if (mSvoMode) { + RCLCPP_WARN(get_logger(), + "Cannot start SVO recording while playing SVO as input"); + res->message = "Cannot start SVO recording while playing SVO as input"; + res->success = false; + return; + } - std::lock_guard lock(mRecMutex); + std::lock_guard lock(mRecMutex); - if (mRecording) - { - RCLCPP_WARN(get_logger(), "SVO Recording is just enabled"); - res->message = "SVO Recording is just enabled"; - res->success = false; - return; - } + if (mRecording) { + RCLCPP_WARN(get_logger(), "SVO Recording is just enabled"); + res->message = "SVO Recording is just enabled"; + res->success = false; + return; + } - mSvoRecBitrate = req->bitrate; - if ((mSvoRecBitrate != 0) && ((mSvoRecBitrate < 1000) || (mSvoRecBitrate > 60000))) - { - RCLCPP_WARN(get_logger(), "'bitrate' value not valid. Please use a value in range [1000,60000], or 0 for default"); - res->message = "'bitrate' value not valid. Please use a value in range [1000,60000], or 0 for default"; - res->success = false; - return; - } - mSvoRecCompr = static_cast(req->compression_mode); - if (mSvoRecCompr >= sl::SVO_COMPRESSION_MODE::LAST) - { - RCLCPP_WARN(get_logger(), "'compression_mode' mode not valid. Please use a value in range [0,2]"); - res->message = "'compression_mode' mode not valid. Please use a value in range [0,2]"; - res->success = false; - return; - } - mSvoRecFramerate = req->target_framerate; - mSvoRecTranscode = req->input_transcode; - mSvoRecFilename = req->svo_filename; + mSvoRecBitrate = req->bitrate; + if ((mSvoRecBitrate != 0) && ((mSvoRecBitrate < 1000) || (mSvoRecBitrate > 60000))) { + RCLCPP_WARN(get_logger(), + "'bitrate' value not valid. Please use a value " + "in range [1000,60000], or 0 for default"); + res->message = "'bitrate' value not valid. Please use a value in range " + "[1000,60000], or 0 for default"; + res->success = false; + return; + } + mSvoRecCompr = static_cast(req->compression_mode); + if (mSvoRecCompr >= sl::SVO_COMPRESSION_MODE::LAST) { + RCLCPP_WARN( + get_logger(), + "'compression_mode' mode not valid. Please use a value in range [0,2]"); + res->message = "'compression_mode' mode not valid. Please use a value in range [0,2]"; + res->success = false; + return; + } + mSvoRecFramerate = req->target_framerate; + mSvoRecTranscode = req->input_transcode; + mSvoRecFilename = req->svo_filename; - if (mSvoRecFilename.empty()) - { - mSvoRecFilename = "zed.svo"; - } + if (mSvoRecFilename.empty()) { + mSvoRecFilename = "zed.svo"; + } - std::string err; + std::string err; - if (!startSvoRecording(err)) - { - res->message = "Error starting SVO recording: " + err; - res->success = false; - return; - } + if (!startSvoRecording(err)) { + res->message = "Error starting SVO recording: " + err; + res->success = false; + return; + } - RCLCPP_INFO(get_logger(), "SVO Recording started: "); - RCLCPP_INFO_STREAM(get_logger(), " * Bitrate: " << mSvoRecBitrate); - RCLCPP_INFO_STREAM(get_logger(), " * Compression: " << mSvoRecCompr); - RCLCPP_INFO_STREAM(get_logger(), " * Framerate: " << mSvoRecFramerate); - RCLCPP_INFO_STREAM(get_logger(), " * Input Transcode: " << (mSvoRecTranscode ? "TRUE" : "FALSE")); - RCLCPP_INFO_STREAM(get_logger(), " * Filename: " << (mSvoRecFilename.empty() ? "zed.svo" : mSvoRecFilename)); + RCLCPP_INFO(get_logger(), "SVO Recording started: "); + RCLCPP_INFO_STREAM(get_logger(), " * Bitrate: " << mSvoRecBitrate); + RCLCPP_INFO_STREAM(get_logger(), " * Compression: " << mSvoRecCompr); + RCLCPP_INFO_STREAM(get_logger(), " * Framerate: " << mSvoRecFramerate); + RCLCPP_INFO_STREAM( + get_logger(), + " * Input Transcode: " << (mSvoRecTranscode ? "TRUE" : "FALSE")); + RCLCPP_INFO_STREAM( + get_logger(), + " * Filename: " << (mSvoRecFilename.empty() ? "zed.svo" : mSvoRecFilename)); - res->message = "SVO Recording started"; - res->success = true; + res->message = "SVO Recording started"; + res->success = true; } -void ZedCamera::callback_stopSvoRec(const std::shared_ptr request_header, - const std::shared_ptr req, - std::shared_ptr res) +void ZedCamera::callback_stopSvoRec( + const std::shared_ptr request_header, + const std::shared_ptr req, + std::shared_ptr res) { - (void)request_header; - (void)req; + (void)request_header; + (void)req; - RCLCPP_INFO(get_logger(), "** Stop SVO Recording service called **"); + RCLCPP_INFO(get_logger(), "** Stop SVO Recording service called **"); - std::lock_guard lock(mRecMutex); + std::lock_guard lock(mRecMutex); - if (!mRecording) - { - RCLCPP_WARN(get_logger(), "SVO Recording is NOT enabled"); - res->message = "SVO Recording is NOT enabled"; - res->success = false; - return; - } + if (!mRecording) { + RCLCPP_WARN(get_logger(), "SVO Recording is NOT enabled"); + res->message = "SVO Recording is NOT enabled"; + res->success = false; + return; + } - stopSvoRecording(); + stopSvoRecording(); - RCLCPP_WARN(get_logger(), "SVO Recording stopped"); - res->message = "SVO Recording stopped"; - res->success = true; + RCLCPP_WARN(get_logger(), "SVO Recording stopped"); + res->message = "SVO Recording stopped"; + res->success = true; } -void ZedCamera::callback_pauseSvoInput(const std::shared_ptr request_header, - const std::shared_ptr req, - std::shared_ptr res) +void ZedCamera::callback_pauseSvoInput( + const std::shared_ptr request_header, + const std::shared_ptr req, + std::shared_ptr res) { - (void)request_header; + (void)request_header; - RCLCPP_INFO(get_logger(), "** Pause SVO Input service called **"); + RCLCPP_INFO(get_logger(), "** Pause SVO Input service called **"); - std::lock_guard lock(mRecMutex); + std::lock_guard lock(mRecMutex); - if (!mSvoMode) - { - RCLCPP_WARN(get_logger(), "The node is not using an SVO as input"); - res->message = "The node is not using an SVO as inpu"; - res->success = false; - return; - } + if (!mSvoMode) { + RCLCPP_WARN(get_logger(), "The node is not using an SVO as input"); + res->message = "The node is not using an SVO as inpu"; + res->success = false; + return; + } - if (mSvoRealtime) - { - RCLCPP_WARN(get_logger(), "SVO input can be paused only if SVO is not in RealTime mode"); - res->message = "SVO input can be paused only if SVO is not in RealTime mode"; - res->success = false; - mSvoPause = false; - return; - } + if (mSvoRealtime) { + RCLCPP_WARN(get_logger(), + "SVO input can be paused only if SVO is not in RealTime mode"); + res->message = "SVO input can be paused only if SVO is not in RealTime mode"; + res->success = false; + mSvoPause = false; + return; + } - if (!mSvoPause) - { - RCLCPP_WARN(get_logger(), "SVO is paused"); - res->message = "SVO is paused"; - mSvoPause = true; - } - else - { - RCLCPP_WARN(get_logger(), "SVO is playing"); - res->message = "SVO is playing"; - mSvoPause = false; - } - res->success = true; + if (!mSvoPause) { + RCLCPP_WARN(get_logger(), "SVO is paused"); + res->message = "SVO is paused"; + mSvoPause = true; + } else { + RCLCPP_WARN(get_logger(), "SVO is playing"); + res->message = "SVO is playing"; + mSvoPause = false; + } + res->success = true; } -void ZedCamera::callback_updateDiagnostic(diagnostic_updater::DiagnosticStatusWrapper& stat) +void ZedCamera::callback_updateDiagnostic( + diagnostic_updater::DiagnosticStatusWrapper& stat) { - if (mConnStatus != sl::ERROR_CODE::SUCCESS) - { - stat.summary(diagnostic_msgs::msg::DiagnosticStatus::ERROR, sl::toString(mConnStatus).c_str()); - return; - } + if (mConnStatus != sl::ERROR_CODE::SUCCESS) { + stat.summary(diagnostic_msgs::msg::DiagnosticStatus::ERROR, + sl::toString(mConnStatus).c_str()); + return; + } - // if (mGrabActive) - { - if (mGrabStatus == sl::ERROR_CODE::SUCCESS /*|| mGrabStatus == sl::ERROR_CODE::NOT_A_NEW_FRAME*/) + // if (mGrabActive) { - stat.summary(diagnostic_msgs::msg::DiagnosticStatus::OK, "Camera grabbing"); + if (mGrabStatus == sl::ERROR_CODE::SUCCESS /*|| mGrabStatus == sl::ERROR_CODE::NOT_A_NEW_FRAME*/) { + stat.summary(diagnostic_msgs::msg::DiagnosticStatus::OK, + "Camera grabbing"); - double freq = 1. / mGrabPeriodMean_sec->getMean(); - double freq_perc = 100. * freq / mCamGrabFrameRate; - stat.addf("Capture", "Mean Frequency: %.1f Hz (%.1f%%)", freq, freq_perc); - stat.addf("Capture", "Tot. Processing Time: %.3f sec (Max. %.3f sec)", mElabPeriodMean_sec->getMean(), + double freq = 1. / mGrabPeriodMean_sec->getMean(); + double freq_perc = 100. * freq / mCamGrabFrameRate; + stat.addf("Capture", "Mean Frequency: %.1f Hz (%.1f%%)", freq, freq_perc); + stat.addf("Capture", + "Tot. Processing Time: %.3f sec (Max. %.3f sec)", + mElabPeriodMean_sec->getMean(), 1. / mCamGrabFrameRate); - if (mPublishingData) - { - freq = 1. / mVideoDepthPeriodMean_sec->getMean(); - freq_perc = 100. * freq / mPubFrameRate; - stat.addf("Video/Depth", "Mean Frequency: %.1f Hz (%.1f%%)", freq, freq_perc); - stat.addf("Video/Depth", "Processing Time: %.3f sec (Max. %.3f sec)", mVideoDepthElabMean_sec->getMean(), - 1. / mCamGrabFrameRate); - } - - if (mSvoMode) - { - int frame = mZed.getSVOPosition(); - int totFrames = mZed.getSVONumberOfFrames(); - double svo_perc = 100. * (static_cast(frame) / totFrames); - - stat.addf("Playing SVO", "Frame: %d/%d (%.1f%%)", frame, totFrames, svo_perc); - } - - if (isDepthRequired()) - { - stat.add("Depth status", "ACTIVE"); - - if (mPcPublishing) - { - double freq = 1. / mPcPeriodMean_sec->getMean(); - double freq_perc = 100. * freq / mPcPubRate; - stat.addf("Point Cloud", "Mean Frequency: %.1f Hz (%.1f%%)", freq, freq_perc); - stat.addf("Point Cloud", "Processing Time: %.3f sec (Max. %.3f sec)", mPcProcMean_sec->getMean(), - 1. / mPcPubRate); - } - else - { - stat.add("Point Cloud", "Topic not subscribed"); - } - - if (mFloorAlignment) - { - if (mInitOdomWithPose) - { - stat.add("Floor Detection", "NOT INITIALIZED"); - } - else - { - stat.add("Floor Detection", "INITIALIZED"); - } - } - - if (mPosTrackingStarted) - { - stat.addf("Tracking status", "%s", sl::toString(mPosTrackingStatus).c_str()); + if (mPublishingData) { + freq = 1. / mVideoDepthPeriodMean_sec->getMean(); + freq_perc = 100. * freq / mPubFrameRate; + stat.addf( + "Video/Depth", "Mean Frequency: %.1f Hz (%.1f%%)", freq, freq_perc); + stat.addf("Video/Depth", + "Processing Time: %.3f sec (Max. %.3f sec)", + mVideoDepthElabMean_sec->getMean(), + 1. / mCamGrabFrameRate); + } + + if (mSvoMode) { + int frame = mZed.getSVOPosition(); + int totFrames = mZed.getSVONumberOfFrames(); + double svo_perc = 100. * (static_cast(frame) / totFrames); + + stat.addf( + "Playing SVO", "Frame: %d/%d (%.1f%%)", frame, totFrames, svo_perc); + } + + if (isDepthRequired()) { + stat.add("Depth status", "ACTIVE"); + + if (mPcPublishing) { + double freq = 1. / mPcPeriodMean_sec->getMean(); + double freq_perc = 100. * freq / mPcPubRate; + stat.addf( + "Point Cloud", "Mean Frequency: %.1f Hz (%.1f%%)", freq, freq_perc); + stat.addf("Point Cloud", + "Processing Time: %.3f sec (Max. %.3f sec)", + mPcProcMean_sec->getMean(), + 1. / mPcPubRate); + } else { + stat.add("Point Cloud", "Topic not subscribed"); + } + + if (mFloorAlignment) { + if (mInitOdomWithPose) { + stat.add("Floor Detection", "NOT INITIALIZED"); + } else { + stat.add("Floor Detection", "INITIALIZED"); + } + } + + if (mPosTrackingStarted) { + stat.addf( + "Tracking status", "%s", sl::toString(mPosTrackingStatus).c_str()); + } else { + stat.add("Pos. Tracking status", "INACTIVE"); + } + + if (mObjDetRunning) { + if (mObjDetSubscribed) { + double freq = 1. / mObjDetPeriodMean_sec->getMean(); + double freq_perc = 100. * freq / mPubFrameRate; + stat.addf("Object detection", + "Mean Frequency: %.3f Hz (%.1f%%)", + freq, + freq_perc); + stat.addf("Object detection", + "Processing Time: %.3f sec (Max. %.3f sec)", + mObjDetElabMean_sec->getMean(), + 1. / mCamGrabFrameRate); + } else { + stat.add("Object Detection", "Active, topic not subscribed"); + } + } else { + stat.add("Object Detection", "INACTIVE"); + } + } else { + stat.add("Depth status", "INACTIVE"); + } + } else { + stat.summaryf(diagnostic_msgs::msg::DiagnosticStatus::ERROR, + "Camera error: %s", + sl::toString(mGrabStatus).c_str()); } - else - { - stat.add("Pos. Tracking status", "INACTIVE"); - } - - if (mObjDetRunning) - { - if (mObjDetSubscribed) - { - double freq = 1. / mObjDetPeriodMean_sec->getMean(); - double freq_perc = 100. * freq / mPubFrameRate; - stat.addf("Object detection", "Mean Frequency: %.3f Hz (%.1f%%)", freq, freq_perc); - stat.addf("Object detection", "Processing Time: %.3f sec (Max. %.3f sec)", mObjDetElabMean_sec->getMean(), - 1. / mCamGrabFrameRate); - } - else - { - stat.add("Object Detection", "Active, topic not subscribed"); - } - } - else - { - stat.add("Object Detection", "INACTIVE"); - } - } - else - { - stat.add("Depth status", "INACTIVE"); - } - } - else - { - stat.summaryf(diagnostic_msgs::msg::DiagnosticStatus::ERROR, "Camera error: %s", - sl::toString(mGrabStatus).c_str()); } - } - /*else + /*else { - stat.summary(diagnostic_msgs::msg::DiagnosticStatus::OK, "Waiting for data subscriber"); - stat.add("Capture", "INACTIVE"); + stat.summary(diagnostic_msgs::msg::DiagnosticStatus::OK, "Waiting for data + subscriber"); stat.add("Capture", "INACTIVE"); }*/ - if (mImuPublishing) - { - double freq = 1. / mImuPeriodMean_sec->getMean(); - stat.addf("IMU", "Mean Frequency: %.1f Hz", freq); - } - else - { - stat.add("IMU Sensor", "Topics not subscribed"); - } - - if (mMagPublishing) - { - double freq = 1. / mMagPeriodMean_sec->getMean(); - stat.addf("Magnetometer", "Mean Frequency: %.1f Hz", freq); - } - else - { - stat.add("Magnetometer Sensor", "Topics not subscribed"); - } - - if (mBaroPublishing) - { - double freq = 1. / mBaroPeriodMean_sec->getMean(); - stat.addf("Barometer", "Mean Frequency: %.1f Hz", freq); - } - else - { - stat.add("Barometer Sensor", "Topics not subscribed"); - } + if (mImuPublishing) { + double freq = 1. / mImuPeriodMean_sec->getMean(); + stat.addf("IMU", "Mean Frequency: %.1f Hz", freq); + } else { + stat.add("IMU Sensor", "Topics not subscribed"); + } - if (sl_tools::isZED2OrZED2i(mCamRealModel)) - { - stat.addf("Left CMOS Temp.", "%.1f °C", mTempLeft); - stat.addf("Right CMOS Temp.", "%.1f °C", mTempRight); + if (mMagPublishing) { + double freq = 1. / mMagPeriodMean_sec->getMean(); + stat.addf("Magnetometer", "Mean Frequency: %.1f Hz", freq); + } else { + stat.add("Magnetometer Sensor", "Topics not subscribed"); + } - if (mTempLeft > 70.f || mTempRight > 70.f) - { - stat.summary(diagnostic_msgs::msg::DiagnosticStatus::WARN, "Camera temperature"); + if (mBaroPublishing) { + double freq = 1. / mBaroPeriodMean_sec->getMean(); + stat.addf("Barometer", "Mean Frequency: %.1f Hz", freq); + } else { + stat.add("Barometer Sensor", "Topics not subscribed"); } - } - else - { - stat.add("Left CMOS Temp.", "N/A"); - stat.add("Right CMOS Temp.", "N/A"); - } - if (mRecording) - { - if (!mRecStatus.status) - { - // if (mGrabActive) - { - stat.add("SVO Recording", "ERROR"); - stat.summary(diagnostic_msgs::msg::DiagnosticStatus::WARN, "Error adding frames to SVO file while recording. " - "Check " - "free disk space"); - } - /*else + if (sl_tools::isZED2OrZED2i(mCamRealModel)) { + stat.addf("Left CMOS Temp.", "%.1f °C", mTempLeft); + stat.addf("Right CMOS Temp.", "%.1f °C", mTempRight); + + if (mTempLeft > 70.f || mTempRight > 70.f) { + stat.summary(diagnostic_msgs::msg::DiagnosticStatus::WARN, + "Camera temperature"); + } + } else { + stat.add("Left CMOS Temp.", "N/A"); + stat.add("Right CMOS Temp.", "N/A"); + } + + if (mRecording) { + if (!mRecStatus.status) { + // if (mGrabActive) + { + stat.add("SVO Recording", "ERROR"); + stat.summary(diagnostic_msgs::msg::DiagnosticStatus::WARN, + "Error adding frames to SVO file while recording. " + "Check " + "free disk space"); + } + /*else { stat.add("SVO Recording", "WAITING"); }*/ + } else { + stat.add("SVO Recording", "ACTIVE"); + stat.addf( + "SVO compression time", "%g msec", mRecStatus.average_compression_time); + stat.addf("SVO compression ratio", + "%.1f%%", + mRecStatus.average_compression_ratio); + } + } else { + stat.add("SVO Recording", "NOT ACTIVE"); } - else - { - stat.add("SVO Recording", "ACTIVE"); - stat.addf("SVO compression time", "%g msec", mRecStatus.average_compression_time); - stat.addf("SVO compression ratio", "%.1f%%", mRecStatus.average_compression_ratio); - } - } - else - { - stat.add("SVO Recording", "NOT ACTIVE"); - } } -} // namespace stereolabs +} // namespace stereolabs #include "rclcpp_components/register_node_macro.hpp" // Register the component with class_loader. -// This acts as a sort of entry point, allowing the component to be discoverable when its library -// is being loaded into a running process. +// This acts as a sort of entry point, allowing the component to be discoverable +// when its library is being loaded into a running process. RCLCPP_COMPONENTS_REGISTER_NODE(stereolabs::ZedCamera) diff --git a/zed_wrapper/config/zed2.yaml b/zed_wrapper/config/zed2.yaml index 5e94dbe3..88e31eec 100644 --- a/zed_wrapper/config/zed2.yaml +++ b/zed_wrapper/config/zed2.yaml @@ -26,7 +26,7 @@ qos_durability: 2 # '1': TRANSIENT_LOCAL - '2': VOLATILE object_detection: - od_enabled: false # True to enable Object Detection [only ZED 2] + od_enabled: true # True to enable Object Detection [only ZED 2] confidence_threshold: 50.0 # [DYNAMIC] - Minimum value of the detection confidence of an object [0,100] model: 0 # '0': MULTI_CLASS_BOX - '1': MULTI_CLASS_BOX_ACCURATE - '2': HUMAN_BODY_FAST - '3': HUMAN_BODY_ACCURATE - '4': MULTI_CLASS_BOX_MEDIUM - '5': HUMAN_BODY_MEDIUM mc_people: true # [DYNAMIC] - Enable/disable the detection of persons for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models From 07a282034449f48c1bdf37d4b0804c83267f67e5 Mon Sep 17 00:00:00 2001 From: Walter Lucetti Date: Thu, 2 Dec 2021 15:50:28 +0100 Subject: [PATCH 4/9] Add support for OD to ZEDM model --- zed_components/src/tools/include/sl_tools.h | 5 +++++ zed_components/src/tools/src/sl_tools.cpp | 18 ++++++++++++++++++ .../zed_camera/src/zed_camera_component.cpp | 10 ++++------ zed_wrapper/config/zed2.yaml | 4 ++-- zed_wrapper/config/zed2i.yaml | 2 +- zed_wrapper/config/zedm.yaml | 16 ++++++++++++++++ 6 files changed, 46 insertions(+), 9 deletions(-) diff --git a/zed_components/src/tools/include/sl_tools.h b/zed_components/src/tools/include/sl_tools.h index a9037949..c0ed51a2 100644 --- a/zed_components/src/tools/include/sl_tools.h +++ b/zed_components/src/tools/include/sl_tools.h @@ -88,6 +88,11 @@ rclcpp::Time slTime2Ros(sl::Timestamp t, rcl_clock_type_t clock_type = RCL_ROS_T */ bool isZED2OrZED2i(sl::MODEL camModel); +/*! \brief check if Object Detection is available + * \param camModel the camera model to check + */ +bool isObjDetAvailable(sl::MODEL camModel); + /*! \brief sl::Mat to ros message conversion * \param img : the image to publish * \param frameId : the id of the reference frame of the image diff --git a/zed_components/src/tools/src/sl_tools.cpp b/zed_components/src/tools/src/sl_tools.cpp index 4c1793e7..80ed2532 100644 --- a/zed_components/src/tools/src/sl_tools.cpp +++ b/zed_components/src/tools/src/sl_tools.cpp @@ -419,6 +419,24 @@ bool isZED2OrZED2i(sl::MODEL camModel){ return false; } +bool isObjDetAvailable(sl::MODEL camModel) +{ + if (camModel == sl::MODEL::ZED2) { + return true; + } +#if ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION >= 5 + if (camModel == sl::MODEL::ZED2i) { + return true; + } +#endif +#if ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION >= 6 + if (camModel == sl::MODEL::ZED_M) { + return true; + } +#endif + return false; +} + StopWatch::StopWatch() { tic(); // Start the timer at creation diff --git a/zed_components/src/zed_camera/src/zed_camera_component.cpp b/zed_components/src/zed_camera/src/zed_camera_component.cpp index fae29394..f083f693 100644 --- a/zed_components/src/zed_camera/src/zed_camera_component.cpp +++ b/zed_components/src/zed_camera/src/zed_camera_component.cpp @@ -280,7 +280,7 @@ void ZedCamera::initParameters() getMappingParams(); // OD PARAMETERS - if (sl_tools::isZED2OrZED2i(mCamUserModel)) + if (sl_tools::isObjDetAvailable(mCamUserModel)) getOdParams(); // Dynamic parameters callback @@ -2869,11 +2869,9 @@ void ZedCamera::stop3dMapping() bool ZedCamera::startObjDetect() { - if (!sl_tools::isZED2OrZED2i(mCamRealModel)) { + if (!sl_tools::isObjDetAvailable(mCamRealModel)) { RCLCPP_ERROR(get_logger(), - "Object detection not started. The module is " - "available only using a ZED2 and ZED2i " - "cameras"); + "Object detection not started. The camera model does not support it with the current version of the SDK"); return false; } @@ -3426,7 +3424,7 @@ void ZedCamera::threadFunc_zedGrab() mObjDetMutex.lock(); if (mObjDetEnabled && !mObjDetRunning) { startObjDetect(); - if (!sl_tools::isZED2OrZED2i(mCamRealModel)) { + if (!sl_tools::isObjDetAvailable(mCamRealModel)) { mObjDetEnabled = false; } } diff --git a/zed_wrapper/config/zed2.yaml b/zed_wrapper/config/zed2.yaml index 88e31eec..2fce9d8d 100644 --- a/zed_wrapper/config/zed2.yaml +++ b/zed_wrapper/config/zed2.yaml @@ -26,9 +26,9 @@ qos_durability: 2 # '1': TRANSIENT_LOCAL - '2': VOLATILE object_detection: - od_enabled: true # True to enable Object Detection [only ZED 2] + od_enabled: false # True to enable Object Detection [only ZED 2] confidence_threshold: 50.0 # [DYNAMIC] - Minimum value of the detection confidence of an object [0,100] - model: 0 # '0': MULTI_CLASS_BOX - '1': MULTI_CLASS_BOX_ACCURATE - '2': HUMAN_BODY_FAST - '3': HUMAN_BODY_ACCURATE - '4': MULTI_CLASS_BOX_MEDIUM - '5': HUMAN_BODY_MEDIUM + model: 2 # '0': MULTI_CLASS_BOX - '1': MULTI_CLASS_BOX_ACCURATE - '2': HUMAN_BODY_FAST - '3': HUMAN_BODY_ACCURATE - '4': MULTI_CLASS_BOX_MEDIUM - '5': HUMAN_BODY_MEDIUM - '6': PERSON_HEAD_BOX mc_people: true # [DYNAMIC] - Enable/disable the detection of persons for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models mc_vehicle: true # [DYNAMIC] - Enable/disable the detection of vehicles for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models mc_bag: true # [DYNAMIC] - Enable/disable the detection of bags for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models diff --git a/zed_wrapper/config/zed2i.yaml b/zed_wrapper/config/zed2i.yaml index 3cc26c37..7da3927d 100644 --- a/zed_wrapper/config/zed2i.yaml +++ b/zed_wrapper/config/zed2i.yaml @@ -28,7 +28,7 @@ object_detection: od_enabled: false # True to enable Object Detection [only ZED 2] confidence_threshold: 50.0 # [DYNAMIC] - Minimum value of the detection confidence of an object [0,100] - model: 2 # '0': MULTI_CLASS_BOX - '1': MULTI_CLASS_BOX_ACCURATE - '2': HUMAN_BODY_FAST - '3': HUMAN_BODY_ACCURATE - '4': MULTI_CLASS_BOX_MEDIUM - '5': HUMAN_BODY_MEDIUM + model: 2 # '0': MULTI_CLASS_BOX - '1': MULTI_CLASS_BOX_ACCURATE - '2': HUMAN_BODY_FAST - '3': HUMAN_BODY_ACCURATE - '4': MULTI_CLASS_BOX_MEDIUM - '5': HUMAN_BODY_MEDIUM - '6': PERSON_HEAD_BOX mc_people: true # [DYNAMIC] - Enable/disable the detection of persons for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models mc_vehicle: true # [DYNAMIC] - Enable/disable the detection of vehicles for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models mc_bag: true # [DYNAMIC] - Enable/disable the detection of bags for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models diff --git a/zed_wrapper/config/zedm.yaml b/zed_wrapper/config/zedm.yaml index d1a34e9b..6475fde6 100644 --- a/zed_wrapper/config/zedm.yaml +++ b/zed_wrapper/config/zedm.yaml @@ -24,3 +24,19 @@ qos_depth: 1 # Queue size if using KEEP_LAST qos_reliability: 1 # '1': RELIABLE - '2': BEST_EFFORT - qos_durability: 2 # '1': TRANSIENT_LOCAL - '2': VOLATILE + + object_detection: + od_enabled: true # True to enable Object Detection [only ZED 2] + confidence_threshold: 50.0 # [DYNAMIC] - Minimum value of the detection confidence of an object [0,100] + model: 6 # '0': MULTI_CLASS_BOX - '1': MULTI_CLASS_BOX_ACCURATE - '2': HUMAN_BODY_FAST - '3': HUMAN_BODY_ACCURATE - '4': MULTI_CLASS_BOX_MEDIUM - '5': HUMAN_BODY_MEDIUM - '6': PERSON_HEAD_BOX + mc_people: true # [DYNAMIC] - Enable/disable the detection of persons for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models + mc_vehicle: true # [DYNAMIC] - Enable/disable the detection of vehicles for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models + mc_bag: true # [DYNAMIC] - Enable/disable the detection of bags for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models + mc_animal: true # [DYNAMIC] - Enable/disable the detection of animals for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models + mc_electronics: true # [DYNAMIC] - Enable/disable the detection of electronic devices for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models + mc_fruit_vegetable: true # [DYNAMIC] - Enable/disable the detection of fruits and vegetables for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models + body_fitting: false # Enable/disable body fitting for 'HUMAN_BODY_FAST' and 'HUMAN_BODY_ACCURATE' models + qos_history: 1 # '1': KEEP_LAST - '2': KEEP_ALL + qos_depth: 1 # Queue size if using KEEP_LAST + qos_reliability: 1 # '1': RELIABLE - '2': BEST_EFFORT - + qos_durability: 2 # '1': TRANSIENT_LOCAL - '2': VOLATILE From 9faf9859e2e3fde1575a266a2eb99cf7a71a6278 Mon Sep 17 00:00:00 2001 From: Walter Lucetti Date: Thu, 2 Dec 2021 17:52:37 +0100 Subject: [PATCH 5/9] Add support for BODY_FORMAT::POSE_34 --- .../include/zed_camera_component.hpp | 3 +- .../zed_camera/src/zed_camera_component.cpp | 33 ++++++++++++------- zed_wrapper/config/zed2.yaml | 5 +-- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/zed_components/src/zed_camera/include/zed_camera_component.hpp b/zed_components/src/zed_camera/include/zed_camera_component.hpp index 6adb1328..09cca25a 100644 --- a/zed_components/src/zed_camera/include/zed_camera_component.hpp +++ b/zed_components/src/zed_camera/include/zed_camera_component.hpp @@ -320,7 +320,8 @@ class ZedCamera : public rclcpp::Node bool mObjDetAnimalsEnable = true; bool mObjDetElectronicsEnable = true; bool mObjDetFruitsEnable = true; - bool mBodyFitting = false; + bool mObjDetBodyFitting = false; + sl::BODY_FORMAT mObjDetBodyFmt = sl::BODY_FORMAT::POSE_34; sl::DETECTION_MODEL mObjDetModel = sl::DETECTION_MODEL::HUMAN_BODY_FAST; // QoS parameters // https://github.com/ros2/ros2/wiki/About-Quality-of-Service-Settings diff --git a/zed_components/src/zed_camera/src/zed_camera_component.cpp b/zed_components/src/zed_camera/src/zed_camera_component.cpp index f083f693..ac21ef4a 100644 --- a/zed_components/src/zed_camera/src/zed_camera_component.cpp +++ b/zed_components/src/zed_camera/src/zed_camera_component.cpp @@ -1119,15 +1119,15 @@ void ZedCamera::getOdParams() mObjDetConfidence, mObjDetConfidence, " * OD min. confidence: "); - // getParam( "object_detection.object_tracking_enabled", mObjDetTracking, - // mObjDetTracking ); RCLCPP_INFO_STREAM(get_logger(), " * OD tracking: " << - // (mObjDetTracking?"TRUE":"FALSE") ); + getParam("object_detection.object_tracking_enabled", mObjDetTracking, + mObjDetTracking); + RCLCPP_INFO_STREAM(get_logger(), " * OD tracking: " << (mObjDetTracking ? "TRUE" : "FALSE")); int model = 0; getParam("object_detection.model", model, model); mObjDetModel = static_cast(model); RCLCPP_INFO_STREAM(get_logger(), " * Object Detection model: " << model << " - " - << mObjDetModel); + << sl::toString(mObjDetModel).c_str()); getParam( "object_detection.mc_people", mObjDetPeopleEnable, mObjDetPeopleEnable); RCLCPP_INFO_STREAM( @@ -1160,9 +1160,14 @@ void ZedCamera::getOdParams() RCLCPP_INFO_STREAM(get_logger(), " * MultiClassBox fruits and vegetables: " << (mObjDetFruitsEnable ? "TRUE" : "FALSE")); - getParam("object_detection.body_fitting", mBodyFitting, mBodyFitting); + getParam("object_detection.body_fitting", mObjDetBodyFitting, mObjDetBodyFitting); RCLCPP_INFO_STREAM( - get_logger(), " * Skeleton fitting: " << (mBodyFitting ? "TRUE" : "FALSE")); + get_logger(), " * Skeleton fitting: " << (mObjDetBodyFitting ? "TRUE" : "FALSE")); + int bodyFormat = 0; + getParam("object_detection.body_format", bodyFormat, bodyFormat); + mObjDetBodyFmt = static_cast(bodyFormat); + //RCLCPP_INFO_STREAM(get_logger(), " * Body format: " << bodyFormat << " - " << sl::toString(mObjDetBodyFmt).c_str()); + RCLCPP_INFO_STREAM(get_logger(), " * Body format: " << bodyFormat); // ------------------------------------------ paramName = "object_detection.qos_history"; @@ -2899,7 +2904,8 @@ bool ZedCamera::startObjDetect() od_p.enable_tracking = mObjDetTracking; od_p.image_sync = true; od_p.detection_model = mObjDetModel; - od_p.enable_body_fitting = mBodyFitting; + od_p.enable_body_fitting = mObjDetBodyFitting; + od_p.body_format = mObjDetBodyFmt; mObjDetFilter.clear(); if (mObjDetPeopleEnable) { @@ -4916,6 +4922,8 @@ void ZedCamera::processDetectedObjects(rclcpp::Time t) &(data.dimensions[0]), 3 * sizeof(float)); + objMsg->objects[idx].body_format = static_cast(mObjDetBodyFmt); + if (mObjDetModel == sl::DETECTION_MODEL::HUMAN_BODY_ACCURATE || #if ZED_SDK_MAJOR_VERSION == 3 && ZED_SDK_MINOR_VERSION >= 5 mObjDetModel == sl::DETECTION_MODEL::HUMAN_BODY_MEDIUM || @@ -4937,15 +4945,16 @@ void ZedCamera::processDetectedObjects(rclcpp::Time t) &(data.head_position[0]), 3 * sizeof(float)); - if (data.keypoint_2d.size() == 18) { + uint8_t kp_size = data.keypoint_2d.size(); + if (kp_size == 18 || kp_size == 34) { + memcpy(&(objMsg->objects[idx].skeleton_2d.keypoints[0]), &(data.keypoint_2d[0]), - 36 * sizeof(float)); - } - if (data.keypoint_2d.size() == 18) { + 2 * kp_size * sizeof(float)); + memcpy(&(objMsg->objects[idx].skeleton_3d.keypoints[0]), &(data.keypoint[0]), - 54 * sizeof(float)); + 3 * kp_size * sizeof(float)); } } else { objMsg->objects[idx].skeleton_available = false; diff --git a/zed_wrapper/config/zed2.yaml b/zed_wrapper/config/zed2.yaml index 2fce9d8d..e09dfc2b 100644 --- a/zed_wrapper/config/zed2.yaml +++ b/zed_wrapper/config/zed2.yaml @@ -26,9 +26,9 @@ qos_durability: 2 # '1': TRANSIENT_LOCAL - '2': VOLATILE object_detection: - od_enabled: false # True to enable Object Detection [only ZED 2] + od_enabled: true # True to enable Object Detection [only ZED 2] confidence_threshold: 50.0 # [DYNAMIC] - Minimum value of the detection confidence of an object [0,100] - model: 2 # '0': MULTI_CLASS_BOX - '1': MULTI_CLASS_BOX_ACCURATE - '2': HUMAN_BODY_FAST - '3': HUMAN_BODY_ACCURATE - '4': MULTI_CLASS_BOX_MEDIUM - '5': HUMAN_BODY_MEDIUM - '6': PERSON_HEAD_BOX + model: 3 # '0': MULTI_CLASS_BOX - '1': MULTI_CLASS_BOX_ACCURATE - '2': HUMAN_BODY_FAST - '3': HUMAN_BODY_ACCURATE - '4': MULTI_CLASS_BOX_MEDIUM - '5': HUMAN_BODY_MEDIUM - '6': PERSON_HEAD_BOX mc_people: true # [DYNAMIC] - Enable/disable the detection of persons for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models mc_vehicle: true # [DYNAMIC] - Enable/disable the detection of vehicles for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models mc_bag: true # [DYNAMIC] - Enable/disable the detection of bags for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models @@ -36,6 +36,7 @@ mc_electronics: true # [DYNAMIC] - Enable/disable the detection of electronic devices for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models mc_fruit_vegetable: true # [DYNAMIC] - Enable/disable the detection of fruits and vegetables for 'MULTI_CLASS_BOX' and 'MULTI_CLASS_BOX_ACCURATE' models body_fitting: false # Enable/disable body fitting for 'HUMAN_BODY_FAST' and 'HUMAN_BODY_ACCURATE' models + body_format: 1 # '0': POSE_18 - '1': POSE_34 [Only if `HUMAN_BODY_*` model is selected] qos_history: 1 # '1': KEEP_LAST - '2': KEEP_ALL qos_depth: 1 # Queue size if using KEEP_LAST qos_reliability: 1 # '1': RELIABLE - '2': BEST_EFFORT - From a99ff4708a646cd3ad2b87f7287b6d6b31aa8377 Mon Sep 17 00:00:00 2001 From: Walter Lucetti Date: Thu, 2 Dec 2021 17:53:11 +0100 Subject: [PATCH 6/9] Add support for BODY_FORMAT::POSE_34 --- zed-ros2-interfaces | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zed-ros2-interfaces b/zed-ros2-interfaces index fc26b8ca..5fab23c6 160000 --- a/zed-ros2-interfaces +++ b/zed-ros2-interfaces @@ -1 +1 @@ -Subproject commit fc26b8ca6deca9cba89f03a2be927a89b3f65e88 +Subproject commit 5fab23c62ae6199021c35e6802d2c87ac87276a9 From 216edb7e788d6a261e225f8de18f936068275b17 Mon Sep 17 00:00:00 2001 From: Walter Lucetti Date: Fri, 3 Dec 2021 15:38:00 +0100 Subject: [PATCH 7/9] Fix URDFs --- .../zed_camera/src/zed_camera_component.cpp | 52 +++++++++---------- zed_wrapper/urdf/zed_macro.urdf.xacro | 6 +-- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/zed_components/src/zed_camera/src/zed_camera_component.cpp b/zed_components/src/zed_camera/src/zed_camera_component.cpp index ac21ef4a..be85b51a 100644 --- a/zed_components/src/zed_camera/src/zed_camera_component.cpp +++ b/zed_components/src/zed_camera/src/zed_camera_component.cpp @@ -75,32 +75,8 @@ ZedCamera::ZedCamera(const rclcpp::NodeOptions& options) exit(EXIT_FAILURE); } - // ----> Parameters initialization - getParam("general.debug_mode", mDebugMode, mDebugMode); - if (mDebugMode) { - rcutils_ret_t res = rcutils_logging_set_logger_level( - get_logger().get_name(), RCUTILS_LOG_SEVERITY_DEBUG); - - if (res != RCUTILS_RET_OK) { - RCLCPP_INFO(get_logger(), "Error setting DEBUG level fot logger"); - } else { - RCLCPP_INFO(get_logger(), "*** Debug Mode enabled ***"); - } - } else { - rcutils_ret_t res = rcutils_logging_set_logger_level( - get_logger().get_name(), RCUTILS_LOG_SEVERITY_INFO); - - if (res != RCUTILS_RET_OK) { - RCLCPP_INFO(get_logger(), "Error setting INFO level for logger"); - } - } - - RCLCPP_DEBUG(get_logger(), - "[ROS2] Using RMW_IMPLEMENTATION = %s", - rmw_get_implementation_identifier()); - + // Parameters initialization initParameters(); - // <---- Parameters initialization // ----> Diagnostic mDiagUpdater.add( @@ -294,6 +270,30 @@ void ZedCamera::getDebugParams() std::string paramName; RCLCPP_INFO(get_logger(), "*** DEBUG parameters ***"); + + getParam("general.debug_mode", mDebugMode, mDebugMode); + RCLCPP_INFO(get_logger(), " * Debug mode: %s", mDebugMode ? "TRUE" : "FALSE"); + if (mDebugMode) { + rcutils_ret_t res = rcutils_logging_set_logger_level( + get_logger().get_name(), RCUTILS_LOG_SEVERITY_DEBUG); + + if (res != RCUTILS_RET_OK) { + RCLCPP_INFO(get_logger(), "Error setting DEBUG level fot logger"); + } else { + RCLCPP_INFO(get_logger(), "*** Debug Mode enabled ***"); + } + } else { + rcutils_ret_t res = rcutils_logging_set_logger_level( + get_logger().get_name(), RCUTILS_LOG_SEVERITY_INFO); + + if (res != RCUTILS_RET_OK) { + RCLCPP_INFO(get_logger(), "Error setting INFO level for logger"); + } + } + + RCLCPP_DEBUG(get_logger(), + "[ROS2] Using RMW_IMPLEMENTATION = %s", + rmw_get_implementation_identifier()); } void ZedCamera::getGeneralParams() @@ -1848,7 +1848,7 @@ void ZedCamera::setTFCoordFrameNames() RCLCPP_INFO_STREAM(get_logger(), " * Barometer\t\t-> " << mBaroFrameId); RCLCPP_INFO_STREAM(get_logger(), " * Magnetometer\t\t-> " << mMagFrameId); RCLCPP_INFO_STREAM(get_logger(), - " * Left Temperature\t\t-> " << mTempLeftFrameId); + " * Left Temperature\t-> " << mTempLeftFrameId); RCLCPP_INFO_STREAM(get_logger(), " * Right Temperature\t-> " << mTempRightFrameId); } diff --git a/zed_wrapper/urdf/zed_macro.urdf.xacro b/zed_wrapper/urdf/zed_macro.urdf.xacro index 81e4d188..8d07dbd2 100644 --- a/zed_wrapper/urdf/zed_macro.urdf.xacro +++ b/zed_wrapper/urdf/zed_macro.urdf.xacro @@ -32,7 +32,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + @@ -46,14 +46,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + - + From 2eade55f92f7946ab9471f7fca39b552cd082794 Mon Sep 17 00:00:00 2001 From: Walter Lucetti Date: Fri, 3 Dec 2021 15:44:42 +0100 Subject: [PATCH 8/9] Update README.md --- README.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6d94b0fe..9caed0bc 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ $ sudo apt remove ros-foxy-image-transport-plugins ros-foxy-compressed-depth-ima ### Prerequisites - [Ubuntu 20.04 (Focal Fossa)](https://releases.ubuntu.com/focal/) -- [ZED SDK](https://www.stereolabs.com/developers/release/latest/) v3.5 +- [ZED SDK](https://www.stereolabs.com/developers/release/latest/) v3.6 - [CUDA](https://developer.nvidia.com/cuda-downloads) dependency - ROS2 Foxy Fitxroy: - [Ubuntu 20.04](https://docs.ros.org/en/foxy/Installation/Linux-Install-Debians.html) @@ -63,7 +63,7 @@ To install the **zed_ros2_wrapper**, open a bash terminal, clone the package fro ```bash $ cd ~/ros2_ws/src/ #use your current ros2 workspace folder -$ git clone https://github.com/stereolabs/zed-ros2-wrapper.git +$ git clone --recursive https://github.com/stereolabs/zed-ros2-wrapper.git $ cd .. $ rosdep install --from-paths src --ignore-src -r -y $ colcon build --symlink-install --cmake-args=-DCMAKE_BUILD_TYPE=Release @@ -79,6 +79,21 @@ $ source ~/.bashrc **Note:** If you are using a different console interface like zsh, you have to change the `source` command as follows: `echo source $(pwd)/install/local_setup.zsh >> ~/.zshrc` and `source ~/.zshrc`. +#### Update the local repository + +To update the repository to the latest release you must use the following command to retrieve the latest commits of `zed-ros2-wrapper` and of all the submodules: + + $ git checkout master # if you are not on the main branch + $ git pull --recurse-submodules # update recursively all the submodules + +Remember to always clean the cache of your colcon workspace before compiling with the `colcon build` command to be sure that everything will work as expected: + + $ cd + $ rm -rf install + $ rm -rf build + $ rm -rf log + $ colcon build --symlink-install --cmake-args=-DCMAKE_BUILD_TYPE=Release + ## Starting the ZED node To start the ZED node, open a terminal and use the [CLI](https://index.ros.org/doc/ros2/Tutorials/Introspection-with-command-line-tools/) command `ros2 launch`: From 34ad5ab01a802c96d4833fa7d4eda6fbb2daa247 Mon Sep 17 00:00:00 2001 From: Walter Lucetti Date: Fri, 3 Dec 2021 15:46:49 +0100 Subject: [PATCH 9/9] Update last_changes.md --- last_changes.md | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/last_changes.md b/last_changes.md index 1143d92b..1caf9af9 100644 --- a/last_changes.md +++ b/last_changes.md @@ -1,45 +1,25 @@ LATEST CHANGES ============== -2021-12-02 ----------- +v3.6.x (2021-12-03) +------------------- - Moved the `zed_interfaces` package to the `zed-ros2-interfaces` repository to match the same configuration of the ROS1 wrapper - The `zed-ros2-interfaces` repository has been added as a sub-module to this repository - -2021-12-01 ----------- - Add new _base_link frame on the base of the camera to easily handle camera positioning on robots. Thx @civerachb-cpr - Improve URDF by adding 3° slope for ZED and ZED2, X-offset for optical frames to correctly match the CMOS sensors position on the PCB, X-offset for mounting screw on ZED2i - Add `zed_macro.urdf.xacro` to be included by other xacro file to easily integrate ZED cameras in the robot descriptions. See ROS1 PR [#771](https://github.com/stereolabs/zed-ros-wrapper/pull/771) for details. Thx @civerachb-cpr - -2021-11-16 ----------- +- Fix URDF `height` value for ZED, ZED2 and ZED2i - Fix performances drop on slower platforms. Thx @roncapat - -2021-08-06 ----------- - Fix SVO LOOP wrong behavior. Thx @kevinanschau - -2021-07-21 ----------- - Add xacro support for automatic URDF configuration - Reworked launch files to support xacro and launch parameters - Use `ros2 launch zed_wrapper -s` to retrieve all the available parameters - Add `svo_path:=` as input for all the launch files to start the node using an SVO as input without modifying 'common.yaml` - -2021-07-15 ----------- - Improved diagnostic information adding elaboration time on all the main tasks - Improved diagnostic time and frequencies calculation - Added StopWatch to sl_tools - -2021-07-14 ----------- - Enabled Diagnostic status publishing - Changed the default values of the QoS parameter reliability for all topics from BEST_EFFORT to RELIABLE to guarantee compatibility with all ROS2 tools - -2021-07-12 ----------- - Fixed tab error in `zedm.yaml` - Fixed compatibility issue with ZED SDK older than v3.5 - Thanks @PhilippPolterauer - Migration to ROS2 Foxy Fitzroy