What we are going to learn:
- How to set up the package architecture to accommodate C++ and Python in the same package
- How to configure this package (by modifying
package.xml
andCMakeLists.txt
) - How to compile and run Python and C++ nodes from this package
If you want to learn ROS 2 Python in a practical, hands-on way, check out the course
ROS 2 Basics in 5 Days: https://app.theconstruct.ai/courses/132
In this course, you’ll cover the foundational concepts needed to start working with ROS 2, as well as more advanced topics, all while engaging in hands-on practice.
Opening the rosject
In order to follow this tutorial, we need to have ROS2 installed in our system, and ideally a ros2_ws (ROS2 Workspace). To make your life easier, we have already prepared a rosject for that: https://app.theconstructsim.com/l/5e01d324/
Just by copying the rosject (clicking the link above), you will have a setup already prepared for you.
After the rosject has been successfully copied to your own area, you should see a Run button. Just click that button to launch the rosject ( see this example).
After pressing the Run button, you should have the rosject loaded. Now, let’s head to the next section to get some real practice.
In order to interact with ROS2, we need a terminal.
Let’s open a terminal by clicking the Open a new terminal button.
Setup the package architecture
Create a standard C++ package
With dependencies on rclpy and rclcpp:
With dependencies on rclpy
:
cd ~/ros2_ws/src/
ros2 pkg create cpp_py_pkg –build-type ament_cmake –dependencies rclpy rclcpp
Run the “tree
.” command to see the folder structure. If you don’t have the command installed, you can install it using:
sudo apt-get update
sudo apt-get install -y tree
You should be able to see this structure which you are already familiar with:
cpp_py_pkg/
├── CMakeLists.txt
├── include
│ └── cpp_py_pkg
├── package.xml
└── src
Add a C++ node and header
cd cpp_py_pkg/
touch src/cpp_node.cpp
touch include/cpp_py_pkg/cpp_header.hpp
In order to compile the package later, we need at least a main function in the C++ node. For this tutorial for simplicity, we can just add this minimal code to the cpp_node.cpp
file:
#include “rclcpp/rclcpp.hpp”// Include your header file to use it#include “cpp_py_pkg/cpp_header.hpp”int main(int argc, char **argv){// Initiate ROS communicationsrclcpp::init(argc, argv);// Instantiate the nodeauto node = std::make_shared<rclcpp::Node>(“my_node_name”);// Make the node spinrclcpp::spin(node);// Shutdown ROS communicationsrclcpp::shutdown();return 0;}
Add a Python node and module to import
For Python, we need to create additional folders first:
mkdir cpp_py_pkg
touch cpp_py_pkg/__init__.py
mkdir scripts
touch cpp_py_pkg/module_to_import.py
touch scripts/py_node.py
You have to add a shebang line first thing in the py_node.py
file otherwise you will get an error when trying to run the node:
#!/usr/bin/env python3import rclpyfrom rclpy.node import Node# Import a specific function/class from your module# from cpp_py_pkg.module_to_import import …def main(args=None):# Initiate ROS communicationsrclpy.init(args=args)# Instantiate the nodenode = Node(‘my_node_name’)# Make the node spinrclpy.spin(node)# Shutdown ROS communicationsrclpy.shutdown()if __name__ == ‘__main__’:main()
Final package architecture
Run the “tree .” command to see the folder structure. I have added additional comments and files to make it clearer:
cpp_py_pkg/
# –> package info, configuration, and compilation
├── CMakeLists.txt
├── package.xml
# Python stuff
# –> empty init file & any python library or module files we want to import
├── cpp_py_pkg
│ ├── __init__.py
│ └── module_to_import.py
│ └── another_module_to_import.py
# –> python executables/nodes
├── scripts
│ └── py_node.py
# Cpp stuff
# –> cpp header files
├── include
│ └── cpp_py_pkg
│ └── cpp_header.hpp
│ └── another_cpp_header.hpp
# –> cpp executables/nodes
└── src
└── cpp_node.cpp
The CMakeLists.txt
and package.xml
will be shared by Python and C++, which is what we will edit in the next section to configure the package for both Python and C++.
Configure the package
package.xml
Add a buildtool_depend
tag for ament_cmake_python
:
<buildtool_depend>ament_cmake_python</buildtool_depend>
So your package.xml
should look like this:
<?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>cpp_py_pkg</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email=”user@todo.todo”>user</maintainer>
<license>TODO: License declaration</license><buildtool_depend>ament_cmake</buildtool_depend>
<buildtool_depend>ament_cmake_python</buildtool_depend><depend>rclpy</depend>
<depend>rclcpp</depend><test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend><export>
<build_type>ament_cmake</build_type>
</export>
</package>
cd ~/ros2_ws/
colcon build –packages-select py_pkg
CMakeLists.txt
Add this external dependency:
find_package(ament_cmake_python REQUIRED)
Add this for C++:
# Include Cpp “include” directory
include_directories(include)# Create Cpp executable and link with dependencies
add_executable(cpp_executable src/cpp_node.cpp)
ament_target_dependencies(cpp_executable rclcpp)# Install Cpp executables in the ros2_ws/install/cpp_py_pkg/lib/cpp_py_pkg/ folder
install(TARGETS
cpp_executable
DESTINATION lib/${PROJECT_NAME}
)
Add this for Python:
# Install Python modules
ament_python_install_package(${PROJECT_NAME})# Install Python executables in the ros2_ws/install/cpp_py_pkg/lib/cpp_py_pkg/ folder
install(PROGRAMS
scripts/py_node.py
DESTINATION lib/${PROJECT_NAME}
)
So your CMakeLists.txt
should look like this:
source ~/.bashrc # . ~/.bashrc
So your CMakeLists.txt should look like this:
cmake_minimum_required(VERSION 3.8)
project(cpp_py_pkg)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(rclpy REQUIRED)
find_package(rclcpp REQUIRED)
find_package(ament_cmake_python REQUIRED)# Include Cpp “include” directory
include_directories(include)# Create Cpp executable and link with dependencies
add_executable(cpp_executable src/cpp_node.cpp)
ament_target_dependencies(cpp_executable rclcpp)# Install Cpp executables in the ros2_ws/install/cpp_py_pkg/lib/cpp_py_pkg/ folder
install(TARGETS
cpp_executable
DESTINATION lib/${PROJECT_NAME}
)# Install Python modules
ament_python_install_package(${PROJECT_NAME})# Install Python executables in the ros2_ws/install/cpp_py_pkg/lib/cpp_py_pkg/ folder
install(PROGRAMS
scripts/py_node.py
DESTINATION lib/${PROJECT_NAME}
)if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()ament_package()
Compile package, then run C++ node and Python node
cd ~/ros2_ws/
colcon build –packages-select cpp_py_pkg
Source ROS2 environment and start C++ node in one terminal:
source ~/ros2_ws/install/setup.bash
ros2 run cpp_py_pkg cpp_executable
Source ROS2 environment and start Python node in another terminal:
source ~/ros2_ws/install/setup.bash
ros2 run cpp_py_pkg py_node.py
That’s it! If there are no errors when you run the nodes then it means that it was successful (remember that our nodes do not do anything except spin).
Congratulations. You now know how to use C++ and Python in the same ROS 2 package.
To learn more about ROS 2, have a look at the course below:
- ROS 2 Basics in 5 Days: https://app.theconstruct.ai/courses/132
We hope this post was really helpful to you.
This tutorial is created by Robotics Ambassador Ernest.
Comment
e6yfpXvx
Comment