diff --git a/mg_odometry/CMakeLists.txt b/mg_odometry/CMakeLists.txt new file mode 100644 index 0000000..a6146ea --- /dev/null +++ b/mg_odometry/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.8) +project(mg_odometry) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# find dependencies +find_package(ament_cmake REQUIRED) +find_package(rclcpp REQUIRED) +find_package(tf2_ros REQUIRED) +find_package(tf2 REQUIRED) +find_package(tf2_geometry_msgs REQUIRED) +include(FindPkgConfig) +pkg_search_module(LIBSERIAL REQUIRED libserial) + +# uncomment the following section in order to fill in +# further dependencies manually. +# find_package( REQUIRED) + + +add_executable(mg_odom_publisher src/mg_odom_publisher.cpp) + +ament_target_dependencies( + mg_odom_publisher + tf2 + tf2_ros + tf2_geometry_msgs + rclcpp +) + +target_include_directories(mg_odom_publisher PUBLIC + $ + $ + ${LIBSERIAL_INCLUDE_DIRS}) + +target_link_libraries( + mg_odom_publisher + ${LIBSERIAL_LIBRARIES} +) + +target_compile_features(mg_odom_publisher PUBLIC c_std_99 cxx_std_17) # Require C99 and C++17 + +install(TARGETS mg_odom_publisher + DESTINATION lib/${PROJECT_NAME}) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + # the following line skips the linter which checks for copyrights + # comment the line when a copyright and license is added to all source files + set(ament_cmake_copyright_FOUND TRUE) + # the following line skips cpplint (only works in a git repo) + # comment the line when this package is in a git repo and when + # a copyright and license is added to all source files + set(ament_cmake_cpplint_FOUND TRUE) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/mg_odometry/package.xml b/mg_odometry/package.xml new file mode 100644 index 0000000..aca3299 --- /dev/null +++ b/mg_odometry/package.xml @@ -0,0 +1,24 @@ + + + + mg_odometry + 0.0.0 + TODO: Package description + Pimpest + TODO: License declaration + + ament_cmake + + rclcpp + tf2 + tf2_ros + tf2_geometry_msgs + libserial-dev + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/mg_odometry/src/mg_odom_publisher.cpp b/mg_odometry/src/mg_odom_publisher.cpp new file mode 100644 index 0000000..a811740 --- /dev/null +++ b/mg_odometry/src/mg_odom_publisher.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include + +#include "geometry_msgs/msg/transform_stamped.hpp" +#include "tf2_ros/static_transform_broadcaster.h" + +#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp" +#include "tf2/LinearMath/Transform.hpp" +#include "tf2/LinearMath/Quaternion.hpp" +#include "tf2/convert.hpp" + +#include "rclcpp/rclcpp.hpp" + +#define TIMEOUT 10u + +constexpr const char* ENCODER_SERIAL_PATH_DEFAULT = "/dev/ttyACM1"; + +class MgOdomPublisher : public rclcpp::Node { + public: + MgOdomPublisher() : Node("mg_odom_publisher") { + this->declare_parameter("odom", "odom"); + this->declare_parameter("target", "base-link"); + this->declare_parameter("serial_path", ENCODER_SERIAL_PATH_DEFAULT); + + tf_static_broadcaster_ = std::make_shared(this); + + timer_ = this->create_wall_timer(std::chrono::milliseconds(TIMEOUT), + std::bind(&MgOdomPublisher::timer_callback, this)); + } + + private: + std::shared_ptr tf_static_broadcaster_; + rclcpp::TimerBase::SharedPtr timer_; + LibSerial::SerialStream enc_serial_port_; + + void timer_callback() { + double _x = NAN; + double _y = NAN; + double _theta = NAN; + + RCLCPP_DEBUG(this->get_logger(), "Callback called"); + + try { + if (!enc_serial_port_.IsOpen()) { + enc_serial_port_.Open(this->get_parameter("serial_path").as_string()); + } + + enc_serial_port_ << "g;"; + + if (!(enc_serial_port_ >> _x >> _y >> _theta)) { + RCLCPP_INFO(this->get_logger(), "Reading Serial Port failed. Closing..."); + throw "Unfortunately bug with libserial"; + } + + RCLCPP_DEBUG(this->get_logger(), "Got following from rpi:{ x: %lf, y: %lf, z: %lf}\n", _x, _y, _theta); + + make_transform(_x, _y, _theta); + } catch (const std::exception& e) { RCLCPP_ERROR(this->get_logger(), "%s\n", e.what()); } + } + + void make_transform(double x, double y, double theta) { + geometry_msgs::msg::TransformStamped t; + + t.header.stamp = this->get_clock()->now(); + t.header.frame_id = this->get_parameter("odom").as_string(); + t.child_frame_id = this->get_parameter("target").as_string(); + + tf2::Quaternion q; + q.setRPY(0, 0, theta); + const tf2::Transform tf(tf2::Transform(q, tf2::Vector3(x, y, 0))); + tf2::convert(tf, t.transform); + + tf_static_broadcaster_->sendTransform(t); + } +}; + +int main(const int argc, const char** argv) { + rclcpp::init(argc, argv); + rclcpp::spin(std::make_shared()); + rclcpp::shutdown(); + + return 0; +}