ROS1迁移到ROS2基础教程

这篇教程详细介绍了如何将ROS1的talker节点迁移到ROS2。内容涵盖ROS1的工作空间目录、代码结构,以及如何修改C++文件、package.xml和CMakeLists.txt以适应ROS2。教程还指导了如何编译和运行ROS2代码,包括使用colcon build和运行节点的步骤。
摘要由CSDN通过智能技术生成


前言

假设我们有一个简单的名为 ROS 1 的包,它 在一个节点talker中使用, 此软件包位于 catkin 工作区中,位于.roscpptalker~/ros1_talker


一、ROS1代码

1. ros1工作空区的目录布局

$ cd ~/ros1_talker
$ find .
.
./src
./src/talker
./src/talker/package.xml
./src/talker/CMakeLists.txt
./src/talker/talker.cpp

2. 代码

2.1 src/talker/package.xml
<package>
  <name>talker</name>
  <version>0.0.0</version>
  <description>talker</description>
  <maintainer email="aamir.com">Aamir</maintainer>
  <license>Apache 2.0</license>
  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>std_msgs</build_depend>
  <run_depend>roscpp</run_depend>
  <run_depend>std_msgs</run_depend>
</package>
2.2 src/talker/CMakeLists.txt
cmake_minimum_required(VERSION 2.8.3)
project(talker)
find_package(catkin REQUIRED COMPONENTS roscpp std_msgs)
catkin_package()
include_directories(${catkin_INCLUDE_DIRS})
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
install(TARGETS talker
  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
2.3 src/talker/talker.cpp
#include <sstream>
#include "ros/ros.h"
#include "std_msgs/String.h"
int main(int argc, char **argv)
{
  ros::init(argc, argv, "talker");
  ros::NodeHandle n;
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
  ros::Rate loop_rate(10);
  int count = 0;
  std_msgs::String msg;
  while (ros::ok())
  {
    std::stringstream ss;
    ss << "hello world " << count++;
    msg.data = ss.str();
    ROS_INFO("%s", msg.data.c_str());
    chatter_pub.publish(msg);
    ros::spinOnce();
    loop_rate.sleep();
  }
  return 0;
}

3. 构建 ROS 1 代码

我们获取一个环境设置文件(在本例中为 noetic 使用 bash),然后我们使用以下命令构建我们的包:catkin_make install(也可以按照自己习惯编译,需要使用ros1的环境)

. /opt/ros/noetic/setup.bash
cd ~/ros1_talker
catkin_make install

4. 运行 ROS 1 节点

4.1 运行主节点

如果还没有运行,我们启动一个roscore,首先从我们的catkin安装树中获取安装文件(系统安装文件 /opt/ros/noetic/setup.bash):

. ~/ros1_talker/install/setup.bash
roscore
4.2 运行tlaker节点

在另一个 shell 中,我们使用 运行catkin安装空间中 的节点rosrun,再次首先获取安装文件(在这种情况下,它必须是我们工作区中的那个):

. ~/ros1_talker/install/setup.bash
rosrun talker talker

二、迁移到 ROS 2

创建一个新的工作区开始:

mkdir ~/ros2_talker
cd ~/ros2_talker

将 ROS 1 包中的源代码树复制到该工作区,我们可以在其中修改它:

mkdir src
cp -a ~/ros1_talker/src/talker src

现在我们将修改节点中的 C++ 代码。名为 的 ROS 2 C++ 库rclcpp提供的 API 与roscpp. 两个库之间的概念非常相似,这使得更改相当简单。

1. 修改C++文件

1. 1修改头文件

使用rclcpp/rclcpp.hpp代替ros/ros.h,它使我们能够访问roscpp库 API,它使我们能够访问rclcpp 库 API:

//#include "ros/ros.h"
#include "rclcpp/rclcpp.hpp"

要获得std_msgs/String消息定义, 需要将std_msgs/String.h替换为std_msgs/msg/string.hpp:

//#include "std_msgs/String.h"
#include "std_msgs/msg/string.hpp"
1.2. 更改 C++ 库调用

我们没有将节点名称传递给库初始化调用,而是进行初始化,然后将节点名称传递给节点对象的创建(我们可以使用auto关键字,因为现在我们需要 C++14 编译器):

//  ros::init(argc, argv, "talker");
//  ros::NodeHandle n;
    rclcpp::init(argc, argv);
    auto node = rclcpp::Node::make_shared("talker");

发布者和速率对象的创建看起来非常相似,只是对命名空间和方法的名称进行了一些更改。

//  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
//  ros::Rate loop_rate(10);
  auto chatter_pub = node->create_publisher<std_msgs::msg::String>("chatter",
    1000);
  rclcpp::Rate loop_rate(10);

发消息的创建在命名空间中有所不同:

//  std_msgs::String msg;
  std_msgs::msg::String msg;

将rclcpp::ok()替代ros::ok():

//  while (ros::ok())
  while (rclcpp::ok())

在发布循环中,我们data像以前一样访问该字段:

msg.data = ss.str();

要打印控制台消息,我们使用 RCLCPP_INFO()及其各种表式,而不是使用ROS_INFO()。关键的区别在于RCLCPP_INFO()它将 Logger 对象作为第一个参数。

//    ROS_INFO("%s", msg.data.c_str());
    RCLCPP_INFO(node->get_logger(), "%s\n", msg.data.c_str());

发布消息和之前一样:

chatter_pub->publish(msg);

spin(即让通信系统处理任何待处理的传入/传出消息)的不同之处在于调用现在将节点作为参数:

//    ros::spinOnce();
    rclcpp::spin_some(node);
1.3 C++完整代码
#include <sstream>
// #include "ros/ros.h"
#include "rclcpp/rclcpp.hpp"
// #include "std_msgs/String.h"
#include "std_msgs/msg/string.hpp"
int main(int argc, char **argv)
{
//  ros::init(argc, argv, "talker");
//  ros::NodeHandle n;
  rclcpp::init(argc, argv);
  auto node = rclcpp::Node::make_shared("talker");
//  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
//  ros::Rate loop_rate(10);
  auto chatter_pub = node->create_publisher<std_msgs::msg::String>("chatter", 1000);
  rclcpp::Rate loop_rate(10);
  int count = 0;
//  std_msgs::String msg;
  std_msgs::msg::String msg;
//  while (ros::ok())
  while (rclcpp::ok())
  {
    std::stringstream ss;
    ss << "hello world " << count++;
    msg.data = ss.str();
//    ROS_INFO("%s", msg.data.c_str());
    RCLCPP_INFO(node->get_logger(), "%s\n", msg.data.c_str());
    chatter_pub->publish(msg);
//    ros::spinOnce();
    rclcpp::spin_some(node);
    loop_rate.sleep();
  }
  return 0;
}

2 修改package.xml

ROS 2 不支持包规范的格式 1,但仅支持较新的格式版本(2 和更高版本)。我们首先在package标签中指定格式版本:

<!-- <package> -->
<package format="2">

ROS 2 使用更新版本的catkin,称为ament_cmake,我们在 buildtool_depend标签中指定:

<!--  <buildtool_depend>catkin</buildtool_depend> -->
  <buildtool_depend>ament_cmake</buildtool_depend>

在我们的构建依赖项中,roscpp我们使用rclcpp提供了我们使用的 C++ API 的而不是我们。

<!--  <build_depend>roscpp</build_depend> -->
  <build_depend>rclcpp</build_depend>

我们在运行依赖项中进行了相同的添加,并从 run_depend标记更新到exec_depend标记(升级到包格式版本 2 的一部分):

<!--  <run_depend>roscpp</run_depend> -->
  <exec_depend>rclcpp</exec_depend>
<!--  <run_depend>std_msgs</run_depend> -->
  <exec_depend>std_msgs</exec_depend>

在 ROS 1 中,我们使用来简化指定编译时和运行时的依赖关系。我们可以在 ROS 2 中做同样的事情:

<depend>rclcpp</depend>
<depend>std_msgs</depend>

我们还需要告诉构建工具我们是什么类型的包,以便它知道如何构建我们。因为我们使用的是amentCMake,所以我们添加以下行来声明我们的构建类型为ament_cmake:

<export>
  <build_type>ament_cmake</build_type>
</export>
2.1 package.xml完整代码
<!-- <package> -->
<package format="2">
  <name>talker</name>
  <version>0.0.0</version>
  <description>talker</description>
  <maintainer email="aamir.com">aamir</maintainer>
  <license>Apache License 2.0</license>
<!--  <buildtool_depend>catkin</buildtool_depend> -->
  <buildtool_depend>ament_cmake</buildtool_depend>
<!--  <build_depend>roscpp</build_depend> -->
<!--  <run_depend>roscpp</run_depend> -->
<!--  <run_depend>std_msgs</run_depend> -->
  <depend>rclcpp</depend>
  <depend>std_msgs</depend>
  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

3 修改CMake 代码

ROS 2 依赖于3.5或更高版本的 CMake:

#cmake_minimum_required(VERSION 2.8.3)
cmake_minimum_required(VERSION 3.5)

ROS 2 依赖于 C++14 标准。根据您使用的编译器,默认情况下可能不会启用对 C++14 的支持。使用gcc5.3(这是在 Ubuntu Xenial 上使用的),我们需要显式启用它,在所有平台上工作的首选方式是,我们通过在文件顶部附近添加以下行来实现:

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)
endif()

用,我们通过在最初发现自己时catkin将它们作为参数传递来指定我们想要构建的包。使用,我们分别找到每个包,从 开始:COMPONENTScatkinament_cmakeament_cmake

#find_package(catkin REQUIRED COMPONENTS roscpp std_msgs)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

可以像以前一样找到系统依赖项:

find_package(Boost REQUIRED COMPONENTS system filesystem thread)

我们调用catkin_package()为使用我们包的其他包自动生成诸如 CMake 配置文件之类的东西。虽然该调用发生在指定要构建的目标之前,但我们现在在目标ament_package() 之后调用类似的调用:

# catkin_package()
# At the bottom of the file:
ament_package()

唯一需要手动包含的目录是本地目录和不是ment包的依赖项:

#include_directories(${catkin_INCLUDE_DIRS})
include_directories(include ${Boost_INCLUDE_DIRS})

更好的选择是单独为每个目标指定包含目录,而不是包含所有目标的所有目录:

target_include_directories(target include ${Boost_INCLUDE_DIRS})

与我们分别找到每个依赖包的方式类似,我们需要将每个依赖包链接到构建目标。与作为ment 包的依赖包链接,而不是使用 target_link_libraries(),ament_target_dependencies()是处理构建标志的更简洁和更彻底的方法。它会自动处理定义的包含目录和定义的 _INCLUDE_DIRS链接库_LIBRARIES。

#target_link_libraries(talker ${catkin_LIBRARIES})
ament_target_dependencies(talker
  rclcpp
  std_msgs)

要链接不是修改包的包,例如系统依赖项Boost,或构建在相同的库CMakeLists.txt,请使用 target_link_libraries():

target_link_libraries(target ${Boost_LIBRARIES})

对于安装,catkin定义变量,如CATKIN_PACKAGE_BIN_DESTINATION. 使用ament_cmake,我们只给出一个相对于安装根目录的路径,例如bin 可执行文件:

#install(TARGETS talker
#  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
install(TARGETS talker
  DESTINATION lib/${PROJECT_NAME})
3.1 CMakeLists.txt完整代码
#cmake_minimum_required(VERSION 2.8.3)
cmake_minimum_required(VERSION 3.5)
project(talker)
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)
endif()
#find_package(catkin REQUIRED COMPONENTS roscpp std_msgs)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
#catkin_package()
#include_directories(${catkin_INCLUDE_DIRS})
include_directories(include)
add_executable(talker talker.cpp)
#target_link_libraries(talker ${catkin_LIBRARIES})
ament_target_dependencies(talker
  rclcpp
  std_msgs)
#install(TARGETS talker
#  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
install(TARGETS talker
  DESTINATION lib/${PROJECT_NAME})
install(DIRECTORY include/
  DESTINATION include)
ament_export_include_directories(include)
ament_export_dependencies(std_msgs)
ament_package()

4 编译和运行ROS2代码

4.1 编译

我们获取一个环境设置文件(在本例中是通过遵循 ROS 2 安装教程生成的,它内置于~/ros2_ws,然后我们使用以下方式构建我们的包:colcon build

. ~/ros2_ws/install/setup.bash
cd ~/ros2_talker
colcon build
4.2 运行 ROS 2 节点

因为我们将talker可执行文件安装到bin中,在获取安装文件后,从我们的安装树中,我们可以直接按名称调用它(此外,还没有 ROS 2 等效项rosrun):

. ~/ros2_ws/install/setup.bash
talker

注:如果这个编译不行,那么就按照自己习惯的方式就行。进入ros2环境,运行:

ros2 run talker talker

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值