ROS2 NODE 简单搭建
The Robot Operating System (ROS) is a set of software libraries and tools for building robot applications. From drivers and state-of-the-art algorithms to powerful developer tools, ROS has the open source tools you need for your next robotics project.
以上内容来自于ROS2-Rolling官网
ROS之于我来说有如下作用:
-
录制ROSBAG包,可以完美还原实车的环境(DDS通讯正常的情况下)
-
可以DEBUG TOPIC信号,PLOTJUGGLER、RVIZ等ROS自带的工具可以将数据拆开来看,逐帧分析。
-
可以回灌算法,如果调度写得合理,完全可以媲美公司级中间件调度。
下面就来说一说ROS2回灌节点的简单搭建:
.├── CMakeLists.txt├── inc│ ├── conv│ │ ├── img_conv.h│ │ └── psd_conv.h│ ├── psd_gaga.h│ └── ros2_all_msgs.h├── package.xml└── src └── psd_gaga.cppROS具有一个重要的功能:负责收发数据。在我的项目里,主要作用是:收上一个节点的模型的输出信息,跑自己的核心算法,将自己算法的输出发送出去。ROS可以做到比较精准地调度(按一定周期)核心算法。
如上所示,psd_gaga.cpp和psd_gaga.h构成了ros2 node的主要框架。
如果你打算和我一起搭建ROS2 NODE,必须使用ubuntu20.04以上的版本(ROS2的要求)。
首先来看CMakeLists.txt,让我们能有个宏观的概念。
cmake_minimum_required(VERSION 3.16)project(psd_gaga)set(CMAKE_CXX_STANDARD 11)set(CMAKE_BUILD_TYPE Debug)
set(ALGO_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
set(INC_PATH ${ALGO_PATH} ${ALGO_BASE_PATH} ${ROS2_BASE_PATH} ${OpenCV_INCLUDE_DIRS})
include_directories(${INC_PATH} )link_directories(${ALGO_PATH}/build)
find_package(ament_cmake REQUIRED)find_package(std_msgs REQUIRED)find_package(rclcpp REQUIRED)
find_package(rosidl_default_generators REQUIRED)find_package(cv_bridge REQUIRED)find_package(image_transport REQUIRED)
add_executable(psd_gaga src/psd_gaga.cpp ../../visualization/visualize.cpp)target_link_libraries(psd_gaga algo_.so ${OpenCV_LIBS})ament_target_dependencies(psd_gaga rclcpp std_msgs rosidl_default_generators cv_bridge image_transport)
install(TARGETS xxx DESTINATION lib/${PROJECT_NAME})
ament_package()如上所示,cmake_minimum_required project set 等常用的宏在此不做过多解释,有需要了解的朋友可以点此访问CMake官方手册。ros提供了ament_cmake的包供cmake使用,我使用的命令依赖ament_cmake的为`ament_package`,其作用为安装当前project到指定位置,方便其他包依赖此project的使用find package(),其实自定义的ros msg也是这个道理,当我们写完一个msg并完成了colcon build编译,在其他项目里可直接使用find package(xxx_msg)。
ament_cmake提供的另一个命令是ament_target_dependencies,其作用相当于cmake中的target_link_libraries,即为某个目标连接库。
当理解完上述内容后,即可开始着手代码,首先来看main函数,其存放于psd_gaga.cpp中
int main(int argc, char **argv){ auto options = rclcpp::InitOptions().auto_initialize_logging(false); rclcpp::init(argc, argv, options); std::shared_ptr<rclcpp::Node> node_ptr = std::make_shared<rclcpp::Node>("psd_gaga", rclcpp::NodeOptions().allow_undeclared_parameters(true).automatically_declare_parameters_from_overrides(true)); std::shared_ptr<PSD_GAGA> sp_psd_gaga = std::make_shared<PSD_GAGA>(node_ptr);
std::chrono::duration<int, std::ratio<1, 1000>> period{66}; auto timer = node_ptr->create_wall_timer(period, std::bind(&PSD_GAGA::algo_run, sp_psd_gaga.get())); while (true) { rclcpp::spin_some(node_ptr); usleep(1); } return 0;}auto options = rclcpp::InitOptions().auto_initialize_logging(false);是为了屏蔽ros2中自带的spd_log日志工具,由于我在自己的算法中引入了spd_log,两次init会导致程序崩溃。
std::shared_ptr<rclcpp::Node> node_ptr = std::make_shared<rclcpp::Node>("psd_gaga", rclcpp::NodeOptions().allow_undeclared_parameters(true).automatically_declare_parameters_from_overrides(true));创建一个基础节点,用于塞到自己node的初始化参数中。
auto timer = node_ptr->create_wall_timer(period, std::bind(&PSD_GAGA::algo_run, sp_psd_gaga.get()));创建定时器,按一个period周期调用run函数。
调度数据已经讲完,那收发数据是如何进行的。由于算法是运行在C++中的,而ros2 msg拥有自己的struct->*.msg,而后它对.msg编译生成一堆.hpp .h供msg创造者使用,这就引出一个问题,不同形式的msg怎么传递呢?
答案是,没办法传递,必须通过convert函数将其转换。在收到数据后将msg转换成c++ struct,在处理完输出后将c++ struct转换成msg再发送出去。
由于源码中涉及到过多的私人信息,计划写一个Node父类将框架搭建好后再公开。