From 39995b404607ddb9e11b7e2aba3c05ce410b4e21 Mon Sep 17 00:00:00 2001 From: Manato HIRABAYASHI Date: Tue, 10 Dec 2024 12:00:50 +0900 Subject: [PATCH 1/2] feat: add periodic_transfrom_publisher Signed-off-by: Manato HIRABAYASHI --- .../CMakeLists.txt | 15 + .../broadcaster.hpp | 87 ++++ src/periodic_transform_publisher/package.xml | 23 ++ .../src/periodic_transform_publisher.cpp | 387 ++++++++++++++++++ 4 files changed, 512 insertions(+) create mode 100644 src/periodic_transform_publisher/CMakeLists.txt create mode 100644 src/periodic_transform_publisher/include/periodic_transform_publisher/broadcaster.hpp create mode 100644 src/periodic_transform_publisher/package.xml create mode 100644 src/periodic_transform_publisher/src/periodic_transform_publisher.cpp diff --git a/src/periodic_transform_publisher/CMakeLists.txt b/src/periodic_transform_publisher/CMakeLists.txt new file mode 100644 index 0000000..924d3b0 --- /dev/null +++ b/src/periodic_transform_publisher/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.8) +project(periodic_transform_publisher) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake_auto REQUIRED) +ament_auto_find_build_dependencies() + +ament_auto_add_executable(${PROJECT_NAME} + src/periodic_transform_publisher.cpp +) + +ament_auto_package() diff --git a/src/periodic_transform_publisher/include/periodic_transform_publisher/broadcaster.hpp b/src/periodic_transform_publisher/include/periodic_transform_publisher/broadcaster.hpp new file mode 100644 index 0000000..39c1cfb --- /dev/null +++ b/src/periodic_transform_publisher/include/periodic_transform_publisher/broadcaster.hpp @@ -0,0 +1,87 @@ +#ifndef PERIODIC_TRANSFORM_PUBLISHER__BROADCASTER_HPP_ +#define PERIODIC_TRANSFORM_PUBLISHER__BROADCASTER_HPP_ +/* + * ref: + * https://github.com/ros2/geometry2/blob/humble/tf2_ros/src/static_transform_broadcaster_node.cpp + */ + +#include +#include +#include + +#include "rclcpp/rclcpp.hpp" +#include "tf2_ros/transform_broadcaster.h" +#include "geometry_msgs/msg/transform_stamped.hpp" + +std::string get_unique_node_name() +{ + static const std::string chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + static std::random_device rd; + static std::minstd_rand g{rd()}; + + // The uniform_int_distribution takes a closed interval of [a, b]; since that + // would include the \0, we remove one from our string length. + static std::uniform_int_distribution pick(0, chars.length() - 1); + + std::string s{"periodic_transform_publisher_"}; + + size_t orig_length = s.length(); + s.resize(orig_length + 16); + + for (size_t i = orig_length; i < s.length(); ++i) { + s[i] = chars[pick(g)]; + } + + return s; +} + +class Broadcaster : public rclcpp::Node +{ + public: + explicit Broadcaster(const rclcpp::NodeOptions & options) + : rclcpp::Node(get_unique_node_name(), options) { + + rcl_interfaces::msg::ParameterDescriptor descriptor; + descriptor.read_only = true; + + tf_msg_.transform.translation.x = this->declare_parameter("translation.x", 0.0, descriptor); + tf_msg_.transform.translation.y = this->declare_parameter("translation.y", 0.0, descriptor); + tf_msg_.transform.translation.z = this->declare_parameter("translation.z", 0.0, descriptor); + tf_msg_.transform.rotation.x = this->declare_parameter("rotation.x", 0.0, descriptor); + tf_msg_.transform.rotation.y = this->declare_parameter("rotation.y", 0.0, descriptor); + tf_msg_.transform.rotation.z = this->declare_parameter("rotation.z", 0.0, descriptor); + tf_msg_.transform.rotation.w = this->declare_parameter("rotation.w", 1.0, descriptor); + tf_msg_.header.frame_id = this->declare_parameter("frame_id", std::string("/frame"), descriptor); + tf_msg_.child_frame_id = + this->declare_parameter("child_frame_id", std::string("/child"), descriptor); + + double interval_sec = this->declare_parameter("interval_sec", 1.0, descriptor); + + // check frame_id != child_frame_id + if (tf_msg_.header.frame_id == tf_msg_.child_frame_id) { + RCLCPP_ERROR( + this->get_logger(), + "cannot publish transform from %s to %s, exiting", + tf_msg_.header.frame_id.c_str(), tf_msg_.child_frame_id.c_str() + ); + throw std::runtime_error("child_frame_id cannot equal frame_id"); + } + + tf_broadcaster_ = std::make_unique(*this); + timer_ = rclcpp::create_timer(this, get_clock(), rclcpp::Duration::from_seconds(interval_sec), + std::bind(&Broadcaster::callback, this)); + } + + void callback() { + tf_msg_.header.stamp = this->get_clock()->now(); + tf_broadcaster_->sendTransform(tf_msg_); + } + + protected: + geometry_msgs::msg::TransformStamped tf_msg_; + std::unique_ptr tf_broadcaster_; + rclcpp::TimerBase::SharedPtr timer_; + +}; + +#endif // PERIODIC_TRANSFORM_PUBLISHER__BROADCASTER_HPP_ diff --git a/src/periodic_transform_publisher/package.xml b/src/periodic_transform_publisher/package.xml new file mode 100644 index 0000000..89cebe3 --- /dev/null +++ b/src/periodic_transform_publisher/package.xml @@ -0,0 +1,23 @@ + + + + periodic_transform_publisher + 0.0.0 + TODO: Package description + manatohirabayashi + TODO: License declaration + + ament_cmake + + geometry_msgs + rclcpp + tf2_ros + tf2_msgs + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/src/periodic_transform_publisher/src/periodic_transform_publisher.cpp b/src/periodic_transform_publisher/src/periodic_transform_publisher.cpp new file mode 100644 index 0000000..0eb70fc --- /dev/null +++ b/src/periodic_transform_publisher/src/periodic_transform_publisher.cpp @@ -0,0 +1,387 @@ +/* + * This publisher based on the implementation of tf2_ros::static_transform_publisher. + * ref: + * https://github.com/ros2/geometry2/blob/humble/tf2_ros/src/static_transform_broadcaster_program.cpp + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "tf2/LinearMath/Quaternion.h" +#include "tf2/LinearMath/Vector3.h" + +#include "rclcpp/rclcpp.hpp" + +#include "periodic_transform_publisher/broadcaster.hpp" + +struct Option +{ + explicit Option(bool has_arg) + : has_argument(has_arg) + { + } + + bool has_argument{false}; + + virtual std::string visit(const std::string & optname, const std::string & stringval) = 0; + + virtual ~Option() {} +}; + +struct DoubleOption final : Option +{ + explicit DoubleOption(bool has_arg, std::function cb) + : Option(has_arg), + callback(cb) + { + } + + std::function callback; + + std::string visit(const std::string & optname, const std::string & stringval) override + { + double value; + try { + value = std::stod(stringval); + } catch (const std::invalid_argument &) { + return "Failed to parse " + optname + " argument as float"; + } + + callback(value); + + return ""; + } +}; + +struct StringOption final : Option +{ + explicit StringOption(bool has_arg, std::function cb) + : Option(has_arg), + callback(cb) + { + } + + std::function callback; + + std::string visit(const std::string & optname, const std::string & stringval) override + { + (void)optname; + callback(stringval); + return ""; + } +}; + +static std::string parse_args( + const std::vector & args, + bool & help, + tf2::Quaternion & quat, + tf2::Vector3 & trans, + std::string & frame_id, + std::string & child_frame_id) +{ + size_t size = args.size(); + + help = false; + + if (size < 1) { + return "Not enough arguments to parse"; + } + + size_t last_index = size - 1; + + bool saw_frame_flag = false; + bool saw_quat_flag = false; + bool saw_rpy_flag = false; + bool saw_trans_flag = false; + double roll = 0.0; + double pitch = 0.0; + double yaw = 0.0; + + auto qx_opt = std::make_shared( + true, [&quat, &saw_quat_flag](double value) { + quat.setX(value); + saw_quat_flag = true; + }); + + auto qy_opt = std::make_shared( + true, [&quat, &saw_quat_flag](double value) { + quat.setY(value); + saw_quat_flag = true; + }); + + auto qz_opt = std::make_shared( + true, [&quat, &saw_quat_flag](double value) { + quat.setZ(value); + saw_quat_flag = true; + }); + + auto qw_opt = std::make_shared( + true, [&quat, &saw_quat_flag](double value) { + quat.setW(value); + saw_quat_flag = true; + }); + + auto roll_opt = std::make_shared( + true, [&roll, &saw_rpy_flag](double value) { + roll = value; + saw_rpy_flag = true; + }); + + auto pitch_opt = std::make_shared( + true, [&pitch, &saw_rpy_flag](double value) { + pitch = value; + saw_rpy_flag = true; + }); + + auto yaw_opt = std::make_shared( + true, [&yaw, &saw_rpy_flag](double value) { + yaw = value; + saw_rpy_flag = true; + }); + + auto trans_x_opt = std::make_shared( + true, [&trans, &saw_trans_flag](double value) { + trans.setX(value); + saw_trans_flag = true; + }); + + auto trans_y_opt = std::make_shared( + true, [&trans, &saw_trans_flag](double value) { + trans.setY(value); + saw_trans_flag = true; + }); + + auto trans_z_opt = std::make_shared( + true, [&trans, &saw_trans_flag](double value) { + trans.setZ(value); + saw_trans_flag = true; + }); + + auto frame_id_opt = std::make_shared( + true, [&frame_id, &saw_frame_flag](const std::string & value) { + frame_id = value; + saw_frame_flag = true; + }); + + auto child_frame_id_opt = std::make_shared( + true, [&child_frame_id, &saw_frame_flag](const std::string & value) { + child_frame_id = value; + saw_frame_flag = true; + }); + + auto help_opt = std::make_shared( + false, [&help](const std::string & value) { + (void)value; + help = true; + }); + + std::unordered_map> options = { + {"--qx", qx_opt}, + {"--qy", qy_opt}, + {"--qz", qz_opt}, + {"--qw", qw_opt}, + {"--roll", roll_opt}, + {"--pitch", pitch_opt}, + {"--yaw", yaw_opt}, + {"--x", trans_x_opt}, + {"--y", trans_y_opt}, + {"--z", trans_z_opt}, + {"--frame-id", frame_id_opt}, + {"--child-frame-id", child_frame_id_opt}, + {"--help", help_opt}, + {"-h", help_opt}, + }; + + std::vector no_flag_args; + + size_t i = 1; + while (i < size) { + const std::string & optname = args[i]; + if (options.count(optname) == 0) { + no_flag_args.push_back(optname); + } else { + std::shared_ptr