Conventions
ROS packages
ROS packages are defined in the ros2_ws/src directory.
Folder Structure
We try to follow this folder structure for the ROS packages:
ros_package/
| CMakeLists.txt
| package.xml
|
└───src/
| | cpp_node.cpp
|
└───include/
| └───ros_package/
| | cpp_header.hpp
|
└───src_py/
| | py_node.py
|
└───ros_package/
| | __init__.py
| | py_module.py
│ └── test/
│ ├── conftest.py
│ ├── unit/
│ │ └── test_py_module.py
│ ├── integration/
│ │ └── test_service_client.py
│ └── end_to_end/
│ └── test_full_launch.py
└───launch/
| launch_file.launch.py
Every ROS package contains the
CMakeLists.txt
andpackage.xml
in the root directory.In case of development using C++, the executables are located in the
src/
directory. The corresponding headers are located in theinclude/
directory, inside a sub-directory that equals the package name.In case of development using Python, the executables are located in the
src_py/
directory.A sub-directory
ros_package/
with the same name as the ROS package can be used to create a Python package. This directory contains an__init__.py
file and the Python modules of the Python package.Possible launch files are located in the
launch/
directory.More directories are possible, like
urdf/
for urdf files orconfig/
for config files.The testing layout is placed alongside the Python package, with these subfolders:
unit/
for fast, logic-only testsintegration/
for multi-component or ROS client/server testsend_to_end/
for full launch/simulation tests
Use a top-level
test/conftest.py
to define shared fixtures, and name your files/functionstest_*
so pytest auto-discovers them. The most general fixtures are defined in theconftest.py
in the root of the repository.
CMakeLists.txt
This CMakeLists.txt file shows the different parts required to build a ROS package:
1# SPDX-FileCopyrightText: Alliander N. V.
2#
3# SPDX-License-Identifier: Apache-2.0
4
5cmake_minimum_required(VERSION 3.5)
6project(ros_package)
7
8# CMake dependencies:
9find_package(ament_cmake REQUIRED)
10find_package(ament_cmake_python REQUIRED)
11
12# Other dependencies:
13find_package(geometry_msgs REQUIRED)
14find_package(vision_msgs REQUIRED)
15
16# C++ executables:
17add_executable(cpp_node src/cpp_node.cpp)
18ament_target_dependencies(cpp_node geometry_msgs vision_msgs)
19install(
20 TARGETS cpp_node
21 DESTINATION lib/${PROJECT_NAME}
22)
23
24# Python executables:
25install(
26 DIRECTORY src_py/
27 DESTINATION lib/${PROJECT_NAME}
28)
29
30# Python package:
31ament_python_install_package(${PROJECT_NAME})
32
33# Shared folders:
34install(
35 DIRECTORY launch
36 DESTINATION share/${PROJECT_NAME}
37)
38
39# Default test:
40if(BUILD_TESTING)
41 find_package(ament_lint_auto REQUIRED)
42 ament_lint_auto_find_test_dependencies()
43endif()
44
45ament_package()
5-10:
The file always starts with a version definition, the package name and the CMake dependencies when building C++ and/or python files.
12-14:
If the package depends on other packages, these are defined. In this case, the packaged depends on the vision_msgs and geometry_msgs packages.
16-22:
Building a C++ executable requires 3 steps: defining the executable, linking dependencies (if any) and installing the targets to the lib directory.
24-28:
For Python executables, we can simply install them all at the same time, by providing the directory.
30-31:
If the package contains a Python package, it needs to be installed.
33-37:
All shared folders are installed into the share directory. This includes the directory of launch files, but also other possible directories, like urdf/
or config/
, if these exist.
39-45:
The file always ends with a default test and the ament_package() command.
package.xml
The package.xml file is related to the CMakeLists.txt file:
1<?xml version="1.0"?>
2<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3
4<!--
5SPDX-FileCopyrightText: Alliander N. V.
6
7SPDX-License-Identifier: Apache-2.0
8-->
9
10<package format="3">
11 <name>ros_package</name>
12 <version>0.1.0</version>
13 <description>A ros package.</description>
14 <maintainer email="researchcenter@alliander.com">RCDT</maintainer>
15 <license>Apache 2.0</license>
16
17 <buildtool_depend>ament_cmake</buildtool_depend>
18 <buildtool_depend>ament_cmake_python</buildtool_depend>
19
20 <depend>geometry_msgs</depend>
21 <depend>vision_msgs</depend>
22
23 <test_depend>ament_lint_auto</test_depend>
24
25 <export>
26 <build_type>ament_cmake</build_type>
27 </export>
28</package>
1-2:
The files starts with default xml definitions.
10-15:
Inside the package tag, we start with some general information about the package.
17-18:
Next, we define the build tool dependencies for building C++ and/or Python files.
20-21:
Next, we define other packages where our package depends on.
23-28:
The file ends with the default test dependency and an export definition.
Custom messages, services and actions
We define custom messages, services and actions in our rcdt_messages package. This package has the following folder structure:
ros_package/
| CMakeLists.txt
| package.xml
|
└───msg/
| | custom_message_definition.msg
|
└───srv/
| | custom_service_definition.srv
|
└───action/
| custom_action_definition.action
In the CMake file, we automatically look for all msg, srv and action files and generate interfaces for them:
1# SPDX-FileCopyrightText: Alliander N. V.
2#
3# SPDX-License-Identifier: Apache-2.0
4
5cmake_minimum_required(VERSION 3.5)
6project(ros_package)
7
8# CMake dependencies:
9find_package(ament_cmake REQUIRED)
10find_package(rosidl_default_generators REQUIRED)
11
12# Other dependencies:
13find_package(geometry_msgs REQUIRED)
14find_package(vision_msgs REQUIRED)
15
16file(GLOB MSGS CONFIGURE_DEPENDS msg/*.msg*)
17file(GLOB SRVS CONFIGURE_DEPENDS srv/*.srv*)
18file(GLOB ACTIONS CONFIGURE_DEPENDS action/*.action*)
19
20foreach(file IN LISTS MSGS)
21 string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" file_relative ${file})
22 list(APPEND MSGS_STRIPPED ${file_relative})
23endforeach()
24
25foreach(file IN LISTS SRVS)
26 string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" file_relative ${file})
27 list(APPEND SRVS_STRIPPED ${file_relative})
28endforeach()
29
30foreach(file IN LISTS ACTIONS)
31 string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" file_relative ${file})
32 list(APPEND ACTIONS_STRIPPED ${file_relative})
33endforeach()
34
35rosidl_generate_interfaces(${PROJECT_NAME}
36 ${MSGS_STRIPPED}
37 ${SRVS_STRIPPED}
38 ${ACTIONS_STRIPPED}
39 DEPENDENCIES geometry_msgs vision_msgs
40)
41
42# Default test:
43if(BUILD_TESTING)
44 find_package(ament_lint_auto REQUIRED)
45 ament_lint_auto_find_test_dependencies()
46endif()
47
48ament_package()
To generate custom messages successfully, we also need to specify the following dependencies in the package.xml file:
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<member_of_group>rosidl_interface_packages</member_of_group>