ROS2 learning (2. Write a simple C++ example)

After installing ROS2, I started to get in touch with many concepts of ros2. There are many things, including compilation, middleware, nodes, etc., which are different from ros1. Without the foundation of ROS1, it may be more difficult to understand these concepts directly.
The easiest way to start is to write a simple program to get familiar with it, use everything, and then understand the concept of each point used, which is easier to understand. Just like writing a "hello, world" before learning all languages. ROS2 also starts with "hello, world".
This article implements a timer, which triggers the sending of "hello world" at a fixed frequency through the timer.

1. Create a ros2 project package

All ros2 projects exist in the form of packages. The package includes package.xml and CMakeList.txt, and the basic files required by a package can be easily created through the ros2 pkg command, and the dependent library can be specified and added to the package when it is created.
The following statement is to create an ament_cmake type package, the node name is timer_sample, the package name is also timer_sample, and the specified dependency is rclcpp.

ros2 pkg create --build-type ament_cmake --node-name timer_sample timer_sample --dependencies rclcpp std_msgs
crabe@crabe-virtual-machine:~/Desktop/sample/timer_sample/src$ ros2 pkg create --build-type ament_cmake --node-name timer_sample timer_sample --dependencies rclcpp
going to create a new package
package name: timer_sample
destination directory: /home/crabe/Desktop/sample/timer_sample/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['crabe <[email protected]>']
licenses: ['TODO: License declaration']
build type: ament_cmake
dependencies: ['rclcpp']
node_name: timer_sample
creating folder ./timer_sample
creating ./timer_sample/package.xml
creating source and include folder
creating folder ./timer_sample/src
creating folder ./timer_sample/include/timer_sample
creating ./timer_sample/CMakeLists.txt
creating ./timer_sample/src/timer_sample.cpp

  • <build_type> specifies the build type, which can be ament_cmake, ament_python, etc.
  • <dependencies> Specifies the other packages that the package depends on. It can be the name of one or more dependent packages, separated by spaces.
  • <package_name> is the name of the package to create

Why is the added dependency rclcpp? This is because if you want to use C++ implementation in ros2, including the node implementation is in the rclcpp package.
In ROS2, a node is the basic unit for publishing and subscribing to messages, performing computations, and communicating with other nodes. Nodes need to interact with the ROS2 system, including creating subscribers and publishers, processing received messages, sending messages, etc. In order to implement these functions, the node needs to use some basic functions and tools provided by ROS2.
In C++, by inheriting the rclcpp::Node class, the node can obtain various functions and tools provided by ROS2. rclcpp::Node is a C++ class provided by ROS2, which encapsulates the node-related functions of ROS2, enabling developers to easily create, configure and manage nodes.

Nodes that inherit from the rclcpp::Node class can use the functions it provides to accomplish the following tasks:

Create publishers and subscribers to communicate with other nodes;
publish messages to specific topics;
process received messages;
execute periodic tasks regularly;
obtain parameter values ​​and set parameters;
communicate with ROS2 services;
obtain The name and namespace of the current node, etc.
Nodes that inherit from the rclcpp::Node class can also easily use ROS2 message types and execution contexts without having to implement these functions themselves.
In short, inheriting the rclcpp::Node class enables the node to interact with the ROS2 system conveniently, and utilizes the rich functions and tools provided by ROS2 to develop a powerful and reliable ROS2 node.
People with ROS1 experience may find it difficult to adapt. Because ROS1 is completely implemented through the roscpp library. There is no such rclcpp::Node class that can provide inheritance. But it is acceptable to use it. After all, the rclcpp::Node class of ROS2 is introduced to provide a more powerful and flexible development experience, and it is more convenient to develop and manage nodes.
The ros2 package has been created in the above way. Take a look at the directory structure


crabe@crabe-virtual-machine:~/Desktop/sample$ tree  timer_sample/
timer_sample/
└── src
    └── timer_sample
        ├── CMakeLists.txt
        ├── include
        │   └── timer_sample
        ├── package.xml
        └── src
            └── timer_sample.cpp

5 directories, 3 files

Through the directory structure, you can see the creation of include and src folders, which are usually created when writing C++ programs. In addition, CMakeList.txt was created to organize the compilation project. There is also a file package.xml, which is used to organize ros packages and add compilation and execution dependencies, which is the same as ROS1.

2. Write a ros2 node

The package has been created, and then the specific content is written in timer_sample.cpp. The following code is to implement a topic to send hell world at regular intervals.

#include "rclcpp/rclcpp.hpp"
#include <iostream>
#include "std_msgs/msg/string.hpp"
#include <string>

using namespace std;

using iString = std_msgs::msg::String;

class TimerSample: public rclcpp::Node{
    
    
        public:
                TimerSample(const string & node_name): Node(node_name)
                {
    
    
                        publisher_ = this->create_publisher<iString>("string_topic", 10);
                };
                virtual ~TimerSample(){
    
    };

                void Start()
                {
    
    
                        timer_ = this->create_wall_timer(1s, std::bind(&TimerSample::topictimer_callback, this));
                };

                void topictimer_callback()
                {
    
    
                        iString message;
                        message.data = "Hello world!";
                        publisher_->publish(message);
                };

        private:
                rclcpp::Publisher<iString>::SharedPtr publisher_;
                rclcpp::TimerBase::SharedPtr timer_;

};

int main(int argc, char ** argv)
{
    
    

  rclcpp::init(argc, argv);

  string name = "timer_sample";
  auto node_ = make_shared<TimerSample>(name);
  rclcpp::executors::SingleThreadedExecutor executor_;
  node_->Start();

  executor_.add_node(node_);
  executor_.spin();

  rclcpp::shutdown();
  return 0;
}

After the code is written, execute colcon build in the outermost timer_sample folder to compile the project.

crabe@crabe-virtual-machine:~/Desktop/sample/timer_sample$ colcon build
Starting >>> timer_sample
Finished <<< timer_sample [2.17s]

Summary: 1 package finished [2.34s]

After the compilation is complete, a build and install folder will be generated.
The difference from ros1 is that all executable files generated by ros2 are generated under install by default. The install folder is directly prepared. In this way, the folder can be copied to any place and can be executed.
Before execution, source install/setup.sh to load environment variables.
Then you can execute ros2 run timer_sample timer_sample

source install/setup.sh
ros2 run timer_sample timer_sample

After the program is executed, there is no output to see because the log is not printed, but at this time, open another terminal, enter the same directory and execute source install/setup.sh, and you can see the hello world sent by subscribing to the topic.

crabe@crabe-virtual-machine:~/Desktop/sample/timer_sample$ source install/setup.bash
crabe@crabe-virtual-machine:~/Desktop/sample/timer_sample$ ros2 topic echo /string_topic
data: Hello world!
---
data: Hello world!
---
data: Hello world!
---
data: Hello world!
---
data: Hello world!
---
data: Hello world!
---
data: Hello world!
---
data: Hello world!
---

At this point, a simple ros2 release topic program is completed.

3. Program Description

I won’t talk about all diams in detail here, just a few points that need attention

  • Inherit rclcpp::Node
    You can see that the class of the example program inherits rclcpp::Node, making the object of the TimerSample class also become a node. This is the typical difference between ROS2 and ROS1. Through inheritance, ROS2 can easily perform many operations on nodes, such as creating timers directly.

  • Why should the timer pointer
    create rclcpp::TimerBase::SharedPtr timer_? In the above example, you can see that you can create a timer directly in the class through this->create_publisher(“string_topic”, 10); why do you want to return value as a member variable? This is because after the timer is created, the start function exits. Although the timer is created by Node, the lifetime of the created timer belongs to the object itself. If it is not saved, the Start function ends and the timer is released. There will be no subsequent trigger timerout.

  • rclcpp::init(argc, argv);
    All processes must execute the rclcpp::init() function at the beginning, otherwise there will be problems in creating and starting nodes.
    If you don't add this sentence, there is no problem in compiling, but when running, the following error will appear.

crabe@crabe-virtual-machine:~/Desktop/sample/timer_sample$ ros2 run timer_sample timer_sample
terminate called after throwing an instance of 'rclcpp::exceptions::RCLInvalidArgument'
  what():  failed to create interrupt guard condition: context argument is null, at /tmp/binarydeb/ros-foxy-rcl-1.1.14/src/rcl/guard_condition.c:65

  • executor_.spin();
    at the most executed spin(); this is a blocking function that will continuously monitor all the nodes that join the node executor to see if there are any triggered events. If you have learned ROS1, it is easier to understand, and the spin of ROS1 is the same effect. The only area here is the concept of multiple node executors. Because the ways of ROS2 and ROS1 nodes have changed a lot, I won’t go into details here, and I will talk about the concept of ROS2 nodes in detail elsewhere.

Guess you like

Origin blog.csdn.net/yangcunbiao/article/details/131820023