Home » ROS2与C++入门教程 » ROS2与C++入门教程-在C++包里增加python支持

ROS2与C++入门教程-在C++包里增加python支持

ROS2与C++入门教程-在C++包里增加python支持

说明:

  • 介绍如何在C++包增加Python支持,实现通过C++节点发布话题信息,通过Python节点接受话题信息
  • 一般情况下都是C++或Python都独立的包,这样对于各自管理的节点也很方便。
  • 但是有些情况也例外,如果C++包里想同时增加Python也是支持的。步骤也多一些

步骤:

  • 新建包my_cpp_py_pkg
cd ~/dev_ws/src
ros2 pkg create my_cpp_py_pkg --build-type ament_cmake
  • 内容如下:
my_cpp_py_pkg/
├── CMakeLists.txt
├── include
│   └── my_cpp_py_pkg
├── package.xml
└── src
  • 增加cpp节点和头文件
cd my_cpp_py_pkg/
touch src/cpp_node.cpp
touch include/my_cpp_py_pkg/cpp_talker.hpp
  • cpp_talker.hpp内容如下:
#include <chrono>
#include <memory>

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
  • cpp_node.cpp需要增加引入头文件,增加如下内容cpp_talker.cpp
#include "my_cpp_py_pkg/cpp_talker.hpp"

using namespace std::chrono_literals;

/* This example creates a subclass of Node and uses std::bind() to register a
 * member function as a callback from the timer. */

class MinimalPublisher : public rclcpp::Node
{
public:
  MinimalPublisher()
  : Node("minimal_publisher"), count_(0)
  {
    publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
    timer_ = this->create_wall_timer(
      500ms, std::bind(&MinimalPublisher::timer_callback, this));
  }

private:
  void timer_callback()
  {
    auto message = std_msgs::msg::String();
    message.data = "Hello, world! " + std::to_string(count_++);
    RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
    publisher_->publish(message);
  }
  rclcpp::TimerBase::SharedPtr timer_;
  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
  size_t count_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MinimalPublisher>());
  rclcpp::shutdown();
  return 0;
}
  • 增加python节点和模块导入
  • 新建pyton目录
mkdir my_cpp_py_pkg
touch my_cpp_py_pkg/__init__.py
mkdir scripts
  • 新建python文件
touch my_cpp_py_pkg/module_to_import.py
touch scripts/py_listener.py
  • module_to_import.py内容如下:
def listener_write(data) : 
  
  my_open = open("/tmp/my_write.txt", 'w')
  #打开/tmp路径下的my_write.txt文件,采用写入模式
  #若文件不存在,创建,若存在,清空并写入
  my_open.write(data)
  #在文件中写入一个字符串
  my_open.write('\n')
  my_open.close()
  • py_listener.py内容如下:
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
from my_cpp_py_pkg.module_to_import import listener_write

class MinimalSubscriber(Node):
    def __init__(self):
        super().__init__('minimal_subscriber')
        self.subscription = self.create_subscription(
                String,
                'topic',
                self.listener_callback,
                10)
        self.subscription #

    def listener_callback(self, msg):
        self.get_logger().info('I heard: "%s"' % msg.data)
        listener_write(msg.data)
        
def main(args=None):
    rclpy.init(args=args)
    minimal_subscriber = MinimalSubscriber()
    rclpy.spin(minimal_subscriber)
    minimal_subscriber.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()
  • 重要提示:您必须首先在 py_listener.py 文件中添加一个行:
#!/usr/bin/env python3
  • 你不增加这一行,用ros2 run运用就会出错。
  • 同时要增加执行权限
chmod +x scripts/py_listener.py
  • 如果要导入模块,可以在py_listener.py增加如下行。
from my_cpp_py_pkg.module_to_import import  listener_write
  • 最终的结构如下:
my_cpp_py_pkg/
# --> package info, configuration, and compilation
├── CMakeLists.txt
├── package.xml
# --> Python stuff
├── my_cpp_py_pkg
│   ├── __init__.py
│   └── module_to_import.py
├── scripts
│   └── py_listener.py
# --> Cpp stuff
├── include
│   └── my_cpp_py_pkg
│       └── cpp_talker.hpp
└── src
    └── cpp_talker.cpp
  • 配置package.xml
  • 内容如下:
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>my_cpp_py_pkg</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="your@email.com">Name</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>
  <buildtool_depend>ament_cmake_python</buildtool_depend>

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

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>
  • 增加<buildtool_depend>ament_cmake_python</buildtool_depend>行和rclpy,支持python使用。
  • 注意:在标准的 Python 包中,你应该有 ament_python,而不是 ament_cmake_python。 确保不要混合使用这 2 个。
  • 使用 ament_cmake_python 意味着我们将能够使用 cmake 设置我们的 Python 内容。
  • 配置CMakeLists.txt
  • 内容如下:
cmake_minimum_required(VERSION 3.5)
project(my_cpp_py_pkg)

# Default to C++14
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 dependencies
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclpy REQUIRED)
find_package(std_msgs REQUIRED)

# Include Cpp "include" directory
include_directories(include)

# Create Cpp executable
add_executable(cpp_talker src/cpp_talker.cpp)
ament_target_dependencies(cpp_executable rclcpp)

# Install Cpp executables
install(TARGETS
  cpp_talker
  DESTINATION lib/${PROJECT_NAME}
)

# Install Python modules
ament_python_install_package(${PROJECT_NAME})

# Install Python executables
install(PROGRAMS
  scripts/py_listener.py
  DESTINATION lib/${PROJECT_NAME}
)

ament_package()
  • 注意其中三个部分: 依赖部分,CPP部分和Python部分
  • 依赖部分
# Find dependencies
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclpy REQUIRED)
find_package(std_msgs REQUIRED)
  • CPP部分
# Include Cpp "include" directory
include_directories(include)

# Create Cpp executable
add_executable(cpp_talker src/cpp_talker.cpp)
ament_target_dependencies(cpp_talker rclcpp)

# Install Cpp executables
install(TARGETS
  cpp_talker 
  DESTINATION lib/${PROJECT_NAME}
)
  • python部分
# Install Python modules
ament_python_install_package(${PROJECT_NAME})

# Install Python executables
install(PROGRAMS
  scripts/py_listener.py
  DESTINATION lib/${PROJECT_NAME}
)
  • 使用 ament_python_install_package(${PROJECT_NAME}) 安装任何 Python 模块(在此示例中:my_cpp_py_pkg/ 文件夹下的文件),以便您可以从这个或另一个包中找到它们。

  • 然后,我们安装 scripts/py_listener.py 文件。 我们将此文件放在 install lib/ 文件夹中,该文件夹与 ROS2 Cpp 节点的文件夹相同。 因此,所有 Cpp/Python 可执行文件都将位于同一位置。

  • 对于您需要安装的任何新 Python 脚本,只需在此处添加新行。

  • 编译包

cd ~/dev_ws/
colcon build --symlink-install --packages-select my_cpp_py_pkg
  • 执行cpp发布话题信息
 ros2 run my_cpp_py_pkg cpp_talker
  • 效果如下:
$ ros2 run my_cpp_py_pkg cpp_talker 
[INFO] [1651803573.152324876] [minimal_publisher]: Publishing: 'Hello, world! 0'
[INFO] [1651803573.652296360] [minimal_publisher]: Publishing: 'Hello, world! 1'
[INFO] [1651803574.152555806] [minimal_publisher]: Publishing: 'Hello, world! 2'
[INFO] [1651803574.652385533] [minimal_publisher]: Publishing: 'Hello, world! 3'
[INFO] [1651803575.152389750] [minimal_publisher]: Publishing: 'Hello, world! 4'
  • 执行python接受话题信息
ros2 run my_cpp_py_pkg py_listener.py
  • 效果如下:
$ ros2 run my_cpp_py_pkg py_listener.py 
[INFO] [1651803635.112644593] [minimal_subscriber]: I heard: "Hello, world! 8"
[INFO] [1651803635.597138764] [minimal_subscriber]: I heard: "Hello, world! 9"
[INFO] [1651803636.097224630] [minimal_subscriber]: I heard: "Hello, world! 10"
[INFO] [1651803636.597222975] [minimal_subscriber]: I heard: "Hello, world! 11"
[INFO] [1651803637.096938082] [minimal_subscriber]: I heard: "Hello, world! 12"
[INFO] [1651803637.596770711] [minimal_subscriber]: I heard: "Hello, world! 13"
  • 重要提示:如果您希望使用 --symlink-install 选项进行编译(这样您就可以修改和重新运行脚本而无需重新编译),您必须使用 chmod +x 使您的脚本可执行。
  • 否则当你尝试运行你的节点时,你会得到这个错误:“No executable found”。

纠错,疑问,交流: 请进入讨论区点击加入Q群

获取最新文章: 扫一扫右上角的二维码加入“创客智造”公众号


标签: ROS2与C++入门教程