Skip to content

Commit

Permalink
Merge pull request #68 from alliander-opensource/document-package-con…
Browse files Browse the repository at this point in the history
…ventions

Document and apply package conventions.
  • Loading branch information
Jelmerdw authored Jan 3, 2025
2 parents 95d9a69 + 9633b69 commit 48e9ed6
Show file tree
Hide file tree
Showing 41 changed files with 337 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# SPDX-FileCopyrightText: Alliander N. V.
#
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.5)
project(ros_package)

# CMake dependencies:
find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)

# Other dependencies:
find_package(geometry_msgs REQUIRED)
find_package(vision_msgs REQUIRED)

# Service definitions:
file(GLOB SRVS CONFIGURE_DEPENDS srv/*.srv*)

foreach(file IN LISTS SRVS)
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" file_relative ${file})
list(APPEND SRVS_STRIPPED ${file_relative})
endforeach()

rosidl_generate_interfaces(${PROJECT_NAME}
${SRVS_STRIPPED}
DEPENDENCIES geometry_msgs vision_msgs
)

# Default test:
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()

ament_package()
45 changes: 45 additions & 0 deletions docs/content/conventions/example_files/ros_package/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# SPDX-FileCopyrightText: Alliander N. V.
#
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.5)
project(ros_package)

# CMake dependencies:
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)

# Other dependencies:
find_package(geometry_msgs REQUIRED)
find_package(vision_msgs REQUIRED)

# C++ executables:
add_executable(cpp_node src/cpp_node.cpp)
ament_target_dependencies(cpp_node geometry_msgs vision_msgs)
install(
TARGETS cpp_node
DESTINATION lib/${PROJECT_NAME}
)

# Python executables:
install(
DIRECTORY src_py/
DESTINATION lib/${PROJECT_NAME}
)

# Python package:
ament_python_install_package(${PROJECT_NAME})

# Shared folders:
install(
DIRECTORY launch
DESTINATION share/${PROJECT_NAME}
)

# Default test:
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()

ament_package()
28 changes: 28 additions & 0 deletions docs/content/conventions/example_files/ros_package/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>

<!--
SPDX-FileCopyrightText: Alliander N. V.
SPDX-License-Identifier: Apache-2.0
-->

<package format="3">
<name>ros_package</name>
<version>0.1.0</version>
<description>A ros package.</description>
<maintainer email="[email protected]">RCDT</maintainer>
<license>Apache 2.0</license>

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

<depend>geometry_msgs</depend>
<depend>vision_msgs</depend>

<test_depend>ament_lint_auto</test_depend>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>
95 changes: 95 additions & 0 deletions docs/content/conventions/ros_package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<!--
SPDX-FileCopyrightText: Alliander N. V.
SPDX-License-Identifier: Apache-2.0
-->

# ros_package conventions

## Folder Structure

We try to follow this folder structure for the ROS packages:

```text
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
|
└───launch/
| launch_file.launch.py
```

- Every ROS package contains the `CMakeLists.txt` and `package.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 the `include/` 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 or `config/` for config files.

## CMakeLists.txt

This CMakeLists.txt file shows the different parts required to build a ROS package:

:::{literalinclude} example_files/ros_package/CMakeLists.txt
:language: CMake
:linenos:
:::

**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:

:::{literalinclude} example_files/ros_package/package.xml
:language: xml
:linenos:
:::

**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.
53 changes: 53 additions & 0 deletions docs/content/conventions/ros_package_msgs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!--
SPDX-FileCopyrightText: Alliander N. V.
SPDX-License-Identifier: Apache-2.0
-->

# ros_package_msgs conventions

## Folder Structure

We define custom messages, services and actions in a separate package, with a package name ending on *_msgs*, to avoid build problems. This package has the following folder structure:

```text
ros_package/
| CMakeLists.txt
| package.xml
|
└───srv/
| | custom_service_definition.srv
|
└───msg/
└───action/
```

- The structure is very similar to a normal ROS package.
- There is a *srv*, *msg* and/or *action* directory for custom service/message/action definitions.

## CMakeLists.txt

Also the CMakeLists.txt file is very similar. This shows an example where only custom services are generated, but the procedure for custom messages or actions is very similar:

:::{literalinclude} example_files/ros_msgs_package/CMakeLists.txt
:language: CMake
:linenos:
:::

**10**:\
The *rosidl_default_generators* dependency is now required, to generated the custom messages.

**16-22**:\
We use CMake's *GLOB* method to automatically obtain all the srv files.

**24-27**:\
We generate the custom service and link the dependencies (if any).

## package.xml

To generate custom messages successfully, we also need to specify the following dependencies in the package.xml file:

```xml
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
```
3 changes: 3 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
Home <self>

content/getting_started.md
content/conventions/ros_package.md
content/conventions/ros_package_msgs.md

content/pyflow.md
content/docker.md
content/writing_documentation.md
Expand Down
25 changes: 14 additions & 11 deletions ros2_ws/src/rcdt_detection/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,32 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.5)

project(rcdt_detection)

find_package(rclpy REQUIRED)
# CMake dependencies:
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(rcdt_utilities REQUIRED)

# Install project files:
install(
DIRECTORY launch
DESTINATION share/${PROJECT_NAME}
)
# Other dependencies:
find_package(rclpy REQUIRED)
find_package(rcdt_utilities REQUIRED)

# Install python executables:
# Python executables:
install(
DIRECTORY nodes/
DIRECTORY src_py/
DESTINATION lib/${PROJECT_NAME}
)

# Install python package:
# Python package:
ament_python_install_package(${PROJECT_NAME})

# Shared folders:
install(
DIRECTORY launch
DESTINATION share/${PROJECT_NAME}
)

# Default test:
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
Expand Down
4 changes: 2 additions & 2 deletions ros2_ws/src/rcdt_detection/launch/realsense.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ def launch_setup(context: LaunchContext) -> list:
if use_sim:
convert_32FC1_to_16UC1_node = Node( # noqa: N806
package="rcdt_sensors",
executable="convert_32FC1_to_16UC1_node.py",
executable="convert_32FC1_to_16UC1.py",
)
combine_camera_topics_node = Node(
package="rcdt_sensors",
executable="combine_camera_topics_node.py",
executable="combine_camera_topics.py",
)
realsense = LaunchDescription(
[
Expand Down
5 changes: 2 additions & 3 deletions ros2_ws/src/rcdt_detection_msgs/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ SPDX-License-Identifier: Apache-2.0
<license>Apache 2.0</license>

<buildtool_depend>ament_cmake</buildtool_depend>

<depend>geometry_msgs</depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>

<depend>geometry_msgs</depend>

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

Expand Down
15 changes: 8 additions & 7 deletions ros2_ws/src/rcdt_franka/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,25 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.5)

project(rcdt_franka)

# CMake dependencies:
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)

# Install project files:
# Python executables:
install(
DIRECTORY launch config urdf
DESTINATION share/${PROJECT_NAME}
DIRECTORY src_py/
DESTINATION lib/${PROJECT_NAME}
)

# Install python executables:
# Shared folders:
install(
DIRECTORY nodes/
DESTINATION lib/${PROJECT_NAME}
DIRECTORY launch config urdf
DESTINATION share/${PROJECT_NAME}
)

# Default test:
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
Expand Down
2 changes: 1 addition & 1 deletion ros2_ws/src/rcdt_franka/launch/controllers.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def launch_setup(context: LaunchContext) -> None:
if simulation:
fr3_gripper = Node(
package="rcdt_franka",
executable="franka_gripper_simulation_node.py",
executable="franka_gripper_simulation.py",
output="screen",
)
else:
Expand Down
Loading

0 comments on commit 48e9ed6

Please sign in to comment.