2.1.2 C++ 示例
ROS 2 在不同语言之间的接口几乎时相同的
在写 C++ 版本的 ROS 程序时,要在 main 函数中加入 argc 和 argv,这其实就是一个入口参数,用于获取用户输入的命令,并且将命令进行分割。
下面我们先打印一下 argc 和 argv
cpp
#include "iostream"
int main(int argc, char** argv)
{
std::cout << "参数数量 = " << argc << std::endl;
std::cout << "程序名称 = " << argv[0] << std::endl;
return 0;
}
bash
$ g++ ros2_cpp_node.cpp
$ ./a.out
参数数量 = 1
程序名称 = ./a.out
$ ./a.out --help
参数数量 = 2
程序名称 = ./a.out
$ ./a.out --help 1 2 3
参数数量 = 5
程序名称 = ./a.out
如果我们想要让 --help 有输出,应该怎么做呢?
cpp
#include "iostream"
int main(int argc, char** argv)
{
std::cout << "参数数量 = " << argc << std::endl;
std::cout << "程序名称 = " << argv[0] << std::endl;
std::string arg1 = argv[1];
if(arg1 == "--help")
{
std::cout << "其实这个函数什么用都没有" << std::endl;
}
return 0;
}
下面我们来写 C++ 节点
cpp
#include "rclcpp/rclcpp.hpp"
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<rclcpp::Node>("cpp_node"); // make_shared 智能指针
RCLCPP_INFO(node->get_logger(), "你好 C++ 节点");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
但是现在直接编译的话会报错
bash
$ g++ ros2_cpp_node.cpp
ros2_cpp_node.cpp:1:10: fatal error: rclcpp/rclcpp.hpp: No such file or directory
1 | #include "rclcpp/rclcpp.hpp"
| ^~~~~~~~~~~~~~~~~~~
compilation terminated.
原因时之前的 include 在系统库里面,但是 rclcpp 是不在系统库里面的。因为使用 g++ 去导库非常麻烦,所以我们使用 CMake 导库
txt
cmake_minimum_required(VERSION 3.8)
project(ros2_cpp)
add_executable(ros2_cpp_node ros2_cpp_node.cpp)
其中 add_executable(ros2_cpp_node ros2_cpp_node.cpp) 的第一个 ros2_cpp_node 表示可执行文件的名字,第二个 ros2_cpp_node.cpp 是文件名字。
现在我们还没有添加依赖,先使用 cmake
bash
$ cmake .
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/hw/Development/shx-notebooks/ros_study/chapt02
如果直接使用 make 编译,会出现和 g++ 一样的错误
bash
$ make
[ 50%] Building CXX object CMakeFiles/ros2_cpp_node.dir/ros2_cpp_node.cpp.o
/home/hw/Development/shx-notebooks/ros_study/chapt02/ros2_cpp_node.cpp:1:10: fatal error: rclcpp/rclcpp.hpp: No such file or directory
1 | #include "rclcpp/rclcpp.hpp"
| ^~~~~~~~~~~~~~~~~~~
compilation terminated.
make[2]: *** [CMakeFiles/ros2_cpp_node.dir/build.make:76: CMakeFiles/ros2_cpp_node.dir/ros2_cpp_node.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/ros2_cpp_node.dir/all] Error 2
make: *** [Makefile:91: all] Error 2
因为 make 这个命令也是根据 Makefile 这个文件去调用 g++ 编译,本质还是 g++。下面我们使用 CMakeLists 里的 find_package 去找到 rclcpp,其中 REQUIRED 是表示这个包是必需的
txt
cmake_minimum_required(VERSION 3.8)
project(ros2_cpp)
add_executable(ros2_cpp_node ros2_cpp_node.cpp)
find_package(rclcpp REQUIRED)
重新运行 cmake
bash
$ cmake .
-- Found rclcpp: 16.0.15 (/opt/ros/humble/share/rclcpp/cmake)
-- Found Python3: /usr/bin/python3 (found version "3.10.12") found components: Interpreter
-- Found rosidl_generator_c: 3.1.7 (/opt/ros/humble/share/rosidl_generator_c/cmake)
-- Found rosidl_adapter: 3.1.7 (/opt/ros/humble/share/rosidl_adapter/cmake)
-- Found rosidl_generator_cpp: 3.1.7 (/opt/ros/humble/share/rosidl_generator_cpp/cmake)
-- Using all available rosidl_typesupport_c: rosidl_typesupport_fastrtps_c;rosidl_typesupport_introspection_c
-- Using all available rosidl_typesupport_cpp: rosidl_typesupport_fastrtps_cpp;rosidl_typesupport_introspection_cpp
-- Found rmw_implementation_cmake: 6.1.2 (/opt/ros/humble/share/rmw_implementation_cmake/cmake)
-- Found rmw_fastrtps_cpp: 6.2.9 (/opt/ros/humble/share/rmw_fastrtps_cpp/cmake)
-- Found OpenSSL: /usr/lib/x86_64-linux-gnu/libcrypto.so (found version "3.0.2")
-- Found FastRTPS: /opt/ros/humble/include
-- Using RMW implementation 'rmw_fastrtps_cpp' as default
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE
-- Configuring done
-- Generating done
-- Build files have been written to: /home/hw/Development/shx-notebooks/ros_study/chapt02
从 -- Found rclcpp: 16.0.15 (/opt/ros/humble/share/rclcpp/cmake) 可以得知成功找到了 rclcpp 在/opt/ros/humble/share/ 下
因此,加上 find_package(rclcpp REQUIRED) 就可以找到 rclcpp 的路径,使用rclcpp_INCLUDE_DIRS 和 rclcpp_LIBRARIES 找到对应的头文件和库文件然后写到 rclcpp_INCLUDE_DIR 目录里面去,我们可以使用 CMakeList 打印消息的方法打印出来
cpp
cmake_minimum_required(VERSION 3.8)
project(ros2_cpp)
add_executable(ros2_cpp_node ros2_cpp_node.cpp)
find_package(rclcpp REQUIRED) # 直接查找对应的头文件和库文件
message(STATUS ${rclcpp_INCLUDE_DIRS}) # 头文件及 rclcpp 依赖的头文件
message(STATUS ${rclcpp_LIBRARIES}) # 库文件及 rclcpp 依赖的库文件
可以使用 cmake . | grep rclcpp 来查看,输出很多,不再展示
输出很多的原因是使用 find_package 查找 rclcpp 时,会将 rclcpp 的依赖也查找并添加进去,就像套娃一样,一层套一层。
找到头文件和库文件之后,必须添加进去才能执行 make ,否则依然会报错
cpp
cmake_minimum_required(VERSION 3.8)
project(ros2_cpp)
add_executable(ros2_cpp_node ros2_cpp_node.cpp)
find_package(rclcpp REQUIRED) # 直接查找对应的头文件和库文件
message(STATUS ${rclcpp_INCLUDE_DIRS}) # 头文件及 rclcpp 依赖的头文件
message(STATUS ${rclcpp_LIBRARIES}) # 库文件及 rclcpp 依赖的库文件
target_include_directories(ros2_cpp_node PUBLIC ${rclcpp_INCLUDE_DIRS}) # 头文件包含
target_link_libraries(ros2_cpp_node ${rclcpp_LIBRARIES}) # 库文件链接
target_include_directories(ros2_cpp_node PUBLIC ${rclcpp_INCLUDE_DIRS})其中第一个ros2_cpp_node是可执行文件的名字,第二个${rclcpp_INCLUDE_DIRS}是头文件target_link_libraries(ros2_cpp_node ${rclcpp_LIBRARIES})其中第一个ros2_cpp_node是可执行文件的名字,第二个${rclcpp_LIBRARIES}是库文件
下面就再次使用 cmake . 和 make 去编译
bash
$ make
[ 50%] Building CXX object CMakeFiles/ros2_cpp_node.dir/ros2_cpp_node.cpp.o
[100%] Linking CXX executable ros2_cpp_node
[100%] Built target ros2_cpp_node
执行可执行文件 ros2_cpp_node
bash
$ ./ros2_cpp_node
[INFO] [1764560464.478290756] [cpp_node]: 你好 C++ 节点
打开一个新终端,使用 ros2 node list 查看当前节点,使用 ros2 node info /cpp_node 查看节点的相关信息
bash
hw@LAPTOP-OF07PRPE:~/Development/shx-notebooks$ ros2 node list
/cpp_node
hw@LAPTOP-OF07PRPE:~/Development/shx-notebooks$ ros2 node info /cpp_node
/cpp_node
Subscribers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
Publishers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
/rosout: rcl_interfaces/msg/Log
Service Servers:
/cpp_node/describe_parameters: rcl_interfaces/srv/DescribeParameters
/cpp_node/get_parameter_types: rcl_interfaces/srv/GetParameterTypes
/cpp_node/get_parameters: rcl_interfaces/srv/GetParameters
/cpp_node/list_parameters: rcl_interfaces/srv/ListParameters
/cpp_node/set_parameters: rcl_interfaces/srv/SetParameters
/cpp_node/set_parameters_atomically: rcl_interfaces/srv/SetParametersAtomically
Service Clients:
Action Servers:
Action Clients: