diff --git a/.gitignore b/.gitignore index 35d74bb..ed9549c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,41 @@ -devel/ +# +# Copyright (c) 2020 LG Electronics Inc. +# +# SPDX-License-Identifier: MIT +# + logs/ build/ -bin/ -lib/ -msg_gen/ -srv_gen/ -msg/*Action.msg -msg/*ActionFeedback.msg -msg/*ActionGoal.msg -msg/*ActionResult.msg -msg/*Feedback.msg -msg/*Goal.msg -msg/*Result.msg -msg/_*.py -build_isolated/ -devel_isolated/ - -# Generated by dynamic reconfigure -*.cfgc -/cfg/cpp/ -/cfg/*.py - -# Ignore generated docs -*.dox -*.wikidoc +install/ +tags + +# vscide stuff +.vscode +*.code-workspace # eclipse stuff .project .cproject +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ + +# Emacs +.#* + # qcreator stuff CMakeLists.txt.user @@ -38,14 +45,11 @@ srv/_*.py qtcreator-* *.user -/planning/cfg -/planning/docs -/planning/src - -*~ +# Auto-generated tag files +tags -# Emacs -.#* +# Persistent undo +[._]*.un~ -# Catkin custom files -CATKIN_IGNORE +# COLCON custom files +COLCON_IGNORE \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a54b330 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +############################################################################### +# Set minimum required version of cmake, project name and compile options +################################################################################ +cmake_minimum_required(VERSION 3.5) +project(realsense_adaptor) + +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 -O2) +endif() + +################################################################################ +# Find colcon packages and libraries for colcon and system dependencies +################################################################################ +find_package(ament_cmake REQUIRED) +find_package(rclcpp REQUIRED) +find_package(sensor_msgs REQUIRED) +find_package(message_filters REQUIRED) +find_package(realsense_adaptor_msgs REQUIRED) + + +################################################################################ +# Build +################################################################################ +include_directories(include) + +add_executable(${PROJECT_NAME} + src/realsense_adaptor.cpp + src/main.cpp) + +ament_target_dependencies(${PROJECT_NAME} + rclcpp + sensor_msgs + realsense_adaptor_msgs + message_filters) + + +################################################################################ +# Install +################################################################################ +install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME}) +install(DIRECTORY config launch DESTINATION share/${PROJECT_NAME}) + +################################################################################ +# Test +################################################################################ + +ament_package() \ No newline at end of file diff --git a/README.md b/README.md index 0198161..732d29a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # realsense_adaptor + synchronize the image data published from realsense node + +realsense_adaptor_msgs is required for this package \ No newline at end of file diff --git a/config/realsense_adaptor.yaml b/config/realsense_adaptor.yaml new file mode 100644 index 0000000..2738d9f --- /dev/null +++ b/config/realsense_adaptor.yaml @@ -0,0 +1,12 @@ +/**: + ros__parameters: + queue_size: 5 + + realsense_devices: ["realsense_front", "realsense_ground"] + + realsense_color_info_topic: "color/camera_info" + realsense_color_topic: "color/image_raw" + realsense_depth_info_topic: "depth/camera_info" + realsense_depth_topic: "depth/image_raw" + + realsense_adaptor_ouput_topic: "synced_image" \ No newline at end of file diff --git a/include/realsense_adaptor/realsense_adaptor.hpp b/include/realsense_adaptor/realsense_adaptor.hpp new file mode 100644 index 0000000..65b1b09 --- /dev/null +++ b/include/realsense_adaptor/realsense_adaptor.hpp @@ -0,0 +1,66 @@ +/** + * @file realsense_adaptor.hpp + * @date 2020-09-17 + * @author hyunseok Yang + * @brief ROS2 realsense adaptor utility + * @remark + * @warning + * LGE Advanced Robotics Laboratory + * Copyright(C) 2019 LG Electronics Co., LTD., Seoul, Korea + * All Rights are Reserved. + */ + +#ifndef _REALSENSE_ADAPTOR_H_ +#define _REALSENSE_ADAPTOR_H_ + +#include +#include +#include +#include +#include +#include + +#include + +class RealsenseAdaptor : public rclcpp::Node +{ +public: + explicit RealsenseAdaptor(const std::string node_name); + ~RealsenseAdaptor(); + +private: + struct CameraSet + { + void create(rclcpp::Node::SharedPtr node, std::string infoTopic, std::string dataTopic); + + rclcpp::Subscription::SharedPtr infoSub; + + sensor_msgs::msg::CameraInfo infoMessage; + message_filters::Subscriber imageSub; + }; + + struct AdaptorSet + { + std::string moduleName; + + CameraSet color; + CameraSet depth; + + // TODO: To be removed(?) + // message_filters::Subscriber deviceSub; + + std::unique_ptr> sync; + + realsense_adaptor_msgs::msg::SyncedImage pubMessage; + rclcpp::Publisher::SharedPtr pub; + + void callback(sensor_msgs::msg::Image::ConstSharedPtr, sensor_msgs::msg::Image::ConstSharedPtr); + }; + + std::vector> adaptorSetList_; + +private: + static void SetCameraInfo2CameraIntrinsic(const sensor_msgs::msg::CameraInfo* const, realsense_adaptor_msgs::msg::CameraIntrinsic&); +}; + +#endif \ No newline at end of file diff --git a/launch/realsense_adaptor.launch.py b/launch/realsense_adaptor.launch.py new file mode 100644 index 0000000..be01e3c --- /dev/null +++ b/launch/realsense_adaptor.launch.py @@ -0,0 +1,41 @@ +# +# LGE Advanced Robotics Laboratory +# Copyright (c) 2020 LG Electronics Inc., LTD., Seoul, Korea +# All Rights are Reserved. +# +# SPDX-License-Identifier: MIT +# + +import os +import launch.actions +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch_ros.actions import Node +from launch.substitutions import LaunchConfiguration + +def generate_launch_description(): + + # Get the launch directory + _package_name = "realsense_adaptor" + config_file_path = os.path.join(get_package_share_directory(_package_name), 'config', "realsense_adaptor.yaml") + + _namespace = LaunchConfiguration('robot_name') + + start_node_cmd = Node( + package=_package_name, + node_executable=_package_name, + node_name=_package_name, + node_namespace=_namespace, + parameters=[config_file_path], + output='screen') + + declare_launch_argument = DeclareLaunchArgument('robot_name', default_value='', description='') + + # Create the launch description and populate + ld = launch.LaunchDescription() + + ld.add_action(declare_launch_argument) + ld.add_action(start_node_cmd) + + return ld diff --git a/package.xml b/package.xml new file mode 100644 index 0000000..cd587a5 --- /dev/null +++ b/package.xml @@ -0,0 +1,23 @@ + + + + realsense_adaptor + 0.1.0 + synchronize time for each channel's data from realsense module + Hyunseok Yang + MIT + + ament_cmake + + rclcpp + sensor_msgs + message_filters + realsense_adaptor_msgs + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..1392576 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,24 @@ +/** + * @file main.cpp + * @date 2020-09-17 + * @author Hyunseok Yang + * @brief ROS2 adaptor node that controls realsense data + * @remark + * @copyright + * LGE Advanced Robotics Laboratory + * Copyright (c) 2020 LG Electronics Inc., LTD., Seoul, Korea + * All Rights are Reserved. + * + * SPDX-License-Identifier: MIT + */ + +#include "realsense_adaptor/realsense_adaptor.hpp" + +int main(int argc, char** argv) +{ + rclcpp::init(argc, argv); + auto node = std::make_shared("realsense_adaptor"); + rclcpp::spin(node); + rclcpp::shutdown(); + return 0; +} diff --git a/src/realsense_adaptor.cpp b/src/realsense_adaptor.cpp new file mode 100644 index 0000000..d2dc2d4 --- /dev/null +++ b/src/realsense_adaptor.cpp @@ -0,0 +1,159 @@ +/** + * @file realsense_adaptor.cpp + * @date 2020-09-24 + * @author hyunseok Yang + * @brief ROS2 realsense adaptor utility + * @remark + * @warning + * LGE Advanced Robotics Laboratory + * Copyright(C) 2019 LG Electronics Co., LTD., Seoul, Korea + * All Rights are Reserved. + */ + +#include "realsense_adaptor/realsense_adaptor.hpp" +#include + +using namespace std; +using placeholders::_1; +using placeholders::_2; +using sensor_msgs::msg::CameraInfo; +using sensor_msgs::msg::Image; +using realsense_adaptor_msgs::msg::SyncedImage; +using realsense_adaptor_msgs::msg::CameraIntrinsic; + +RealsenseAdaptor::RealsenseAdaptor(const string node_name) + : Node(node_name, + rclcpp::NodeOptions() + .allow_undeclared_parameters(true) + .automatically_declare_parameters_from_overrides(true)) +{ + // RCLCPP_INFO(this->get_logger(), "name %s namespace %s", get_name(), get_namespace()); + + int queueSize; + get_parameter_or("queue_size", queueSize, int(10)); + + vector deviceList; + get_parameter("realsense_devices", deviceList); + + string realsense_color_info_topic, realsense_color_topic; + string realsense_depth_info_topic, realsense_depth_topic; + string adaptor_ouput_topic; + + get_parameter("realsense_color_info_topic", realsense_color_info_topic); + get_parameter("realsense_color_topic", realsense_color_topic); + get_parameter("realsense_depth_info_topic", realsense_depth_info_topic); + get_parameter("realsense_depth_topic", realsense_depth_topic); + get_parameter("realsense_adaptor_ouput_topic", adaptor_ouput_topic); + + RCLCPP_INFO(this->get_logger(), "realsenseList count: %d", deviceList.size()); + RCLCPP_INFO(this->get_logger(), "queue_size: %d", queueSize); + RCLCPP_INFO(this->get_logger(), "realsense_color_topic (info, image): (%s, %s)", realsense_color_info_topic.c_str(), realsense_color_topic.c_str()); + RCLCPP_INFO(this->get_logger(), "realsense_depth_topic (info, image): (%s, %s)", realsense_depth_info_topic.c_str(), realsense_depth_topic.c_str()); + RCLCPP_INFO(this->get_logger(), "realsense_adaptor_ouput_topic: %s", adaptor_ouput_topic.c_str()); + + auto nodeHandle = shared_ptr(this, [](auto) {}); + + for (auto realsense : deviceList) + { + RCLCPP_INFO(this->get_logger(), "module: %s", realsense.c_str()); + + const auto topicPrefix = realsense + "/"; + auto adaptor = std::make_shared(); + + adaptor->moduleName = realsense; + + using namespace message_filters; + { + adaptor->color.create(nodeHandle, topicPrefix + realsense_color_info_topic, topicPrefix + realsense_color_topic); + adaptor->depth.create(nodeHandle, topicPrefix + realsense_depth_info_topic, topicPrefix + realsense_depth_topic); + + // TODO: To be removed(?) + // adaptor->deviceSub = make_unique>(this, "/camera/device_info"); + + adaptor->sync = std::make_unique>(adaptor->color.imageSub, adaptor->depth.imageSub, queueSize); + adaptor->sync->registerCallback(bind(&AdaptorSet::callback, adaptor.get(), _1, _2)); + } + + adaptor->pub = create_publisher(topicPrefix + adaptor_ouput_topic, rclcpp::SensorDataQoS()); + // RCLCPP_INFO(this->get_logger(), "-> Create publihser: %s ", realsense.c_str()); + + adaptorSetList_.push_back(adaptor); + } +} + +RealsenseAdaptor::~RealsenseAdaptor() +{ + for (auto adaptor : adaptorSetList_) + { + adaptor->color.imageSub.unsubscribe(); + adaptor->depth.imageSub.unsubscribe(); + + adaptor->sync.release(); + } +} + +void RealsenseAdaptor::CameraSet::create(rclcpp::Node::SharedPtr node, string infoTopic, string dataTopic) +{ + auto depthInfoMessage = &infoMessage; + auto callback_depth_info = [infoMessage = depthInfoMessage](CameraInfo::ConstSharedPtr msg) -> void { + if (infoMessage != nullptr) + *infoMessage = *msg; + }; + + infoSub = node->create_subscription(infoTopic, rclcpp::QoS(1), callback_depth_info); + imageSub.subscribe(node, dataTopic); +} + +void RealsenseAdaptor::SetCameraInfo2CameraIntrinsic(const CameraInfo* const src, CameraIntrinsic &dst) +{ + const auto fx = src->k[0]; + const auto fy = src->k[4]; + const auto hfov = 2 * atan((src->width/2) / fx); + const auto vfov = hfov * ((float)src->height/(float)src->width); + + dst.width = src->width; + dst.height = src->height; + dst.cx = src->k[2]; + dst.cy = src->k[5]; + dst.fx = fx; + dst.fy = fy; + dst.hfov = hfov; + dst.vfov = vfov; + dst.baseline = -src->p[3]/fx; + dst.coeff[0] = src->d[0]; + dst.coeff[1] = src->d[1]; + dst.coeff[2] = src->d[2]; + dst.coeff[3] = src->d[3]; + dst.coeff[4] = src->d[4]; + dst.depthscale = 1.0f; // ? +} + +void RealsenseAdaptor::AdaptorSet::callback( + Image::ConstSharedPtr colorData, + Image::ConstSharedPtr depthData) +{ + auto message_time = colorData->header.stamp; + + this->pubMessage.header.stamp = message_time; + + RealsenseAdaptor::SetCameraInfo2CameraIntrinsic(&this->color.infoMessage, this->pubMessage.color.intrinsic); + + this->pubMessage.color.data_name = "color"; + this->pubMessage.color.channels = 3; + this->pubMessage.color.data_unit_size = 1; + this->pubMessage.color.data.assign(colorData->data.size(), 0); + memcpy(&this->pubMessage.color.data[0], &colorData->data[0], colorData->data.size()); + + RealsenseAdaptor::SetCameraInfo2CameraIntrinsic(&this->depth.infoMessage, this->pubMessage.depth.intrinsic); + + this->pubMessage.depth.data_name = "depth"; + this->pubMessage.depth.channels = 1; + this->pubMessage.depth.data_unit_size = 2; + this->pubMessage.depth.data.assign(depthData->data.size(), 0); + memcpy(&this->pubMessage.depth.data[0], &depthData->data[0], depthData->data.size()); + + // printf("[%s] callback \n", this->moduleName.c_str()); + // printf("[%s] color width: %d height: %d\n", this->moduleName.c_str(), this->pubMessage.color.intrinsic.nwidth, this->pubMessage.color.intrinsic.nheight); + + this->pub->publish(this->pubMessage); +} \ No newline at end of file