Creating a ROS 2 Package
Overview​
A ROS 2 package is the fundamental unit of organization in ROS 2. It contains source code, launch files, configuration files, and documentation. This lesson will guide you through creating ROS 2 packages, understanding their structure, and configuring them properly for development.
Package Structure​
A typical ROS 2 package follows a standardized structure:
my_package/
├── CMakeLists.txt # Build configuration for C++ packages
├── package.xml # Package metadata and dependencies
├── src/ # Source code files (for C++ packages)
├── include/my_package/ # Header files (for C++ packages)
├── scripts/ # Standalone executable scripts
├── launch/ # Launch files
├── config/ # Configuration files
├── test/ # Test files
└── setup.py # Build configuration for Python packages (if applicable)
Creating a Package with ros2 pkg create​
The easiest way to create a new ROS 2 package is using the ros2 pkg create command:
cd ~/ros2_ws/src
ros2 pkg create --help
Basic Package Creation​
To create a simple package:
ros2 pkg create my_robot_package
Creating a Package with Build Type​
For C++ packages:
ros2 pkg create --build-type ament_cmake my_cpp_package
For Python packages:
ros2 pkg create --build-type ament_python my_py_package
Creating a Package with Dependencies​
To create a package with dependencies:
ros2 pkg create --build-type ament_cmake --dependencies rclcpp std_msgs geometry_msgs my_cpp_package
Creating a Package with Node Templates​
To create a package with template files:
ros2 pkg create --build-type ament_cmake --node-name my_node my_cpp_package
Package.xml Configuration​
The package.xml file contains metadata about the package and its dependencies:
<?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_robot_package</name>
<version>0.0.0</version>
<description>Package for my robot functionality</description>
<maintainer email="user@example.com">Your Name</maintainer>
<license>Apache License 2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>rclpy</depend>
<depend>std_msgs</depend>
<depend>geometry_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>
Key Elements in package.xml​
- name: The package name (must be unique within the workspace)
- version: Package version in semantic versioning format (major.minor.patch)
- description: Brief description of the package
- maintainer: Contact information for the package maintainer
- license: Software license for the package
- buildtool_depend: Build system dependencies (e.g., ament_cmake)
- depend: Runtime dependencies for the package
- test_depend: Dependencies required for testing
- export/build_type: Specifies the build system type
CMakeLists.txt for C++ Packages​
For C++ packages, the CMakeLists.txt file configures the build process:
cmake_minimum_required(VERSION 3.8)
project(my_cpp_package)
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(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)
# Create executable
add_executable(my_node src/my_node.cpp)
ament_target_dependencies(my_node
rclcpp
std_msgs
geometry_msgs
)
# Install targets
install(TARGETS
my_node
DESTINATION lib/${PROJECT_NAME}
)
# Install other files
install(DIRECTORY
launch
config
DESTINATION share/${PROJECT_NAME}/
)
ament_package()
Key CMake Configuration Elements​
- cmake_minimum_required: Specifies minimum CMake version
- project: Defines the project name
- find_package: Locates required packages and their dependencies
- add_executable: Creates an executable target from source files
- ament_target_dependencies: Links dependencies to the target
- install: Specifies files to install when the package is built
- ament_package: Finalizes the package configuration
Setup.py for Python Packages​
For Python packages, the setup.py file handles the build configuration:
from setuptools import setup
package_name = 'my_py_package'
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='Your Name',
maintainer_email='user@example.com',
description='Package description',
license='Apache License 2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'my_node = my_py_package.my_node:main',
],
},
)
Key Setup.py Elements​
- name: The package name
- version: Version of the package
- packages: Python packages to include
- data_files: Additional files to install
- install_requires: Python dependencies
- entry_points: Console scripts that can be executed
- tests_require: Dependencies needed for testing
Package Development Best Practices​
Naming Conventions​
- Use lowercase letters with underscores separating words (snake_case)
- Avoid spaces and special characters
- Choose descriptive names that indicate the package's purpose
- Keep names relatively short but meaningful
Directory Structure​
- Organize source files in appropriate subdirectories
- Keep launch files in a
launch/directory - Store configuration files in a
config/directory - Place test files in a
test/directory - Document the package structure in README files
Dependency Management​
- Only list actual dependencies in package.xml
- Use specific versions when necessary to ensure compatibility
- Regularly review and update dependencies
- Document any special installation requirements
Creating Nodes within Packages​
C++ Node Example​
Create a simple C++ node in src/my_node.cpp:
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
class MyNode : public rclcpp::Node
{
public:
MyNode() : Node("my_node")
{
publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
timer_ = this->create_wall_timer(
std::chrono::milliseconds(500),
std::bind(&MyNode::timer_callback, this));
}
private:
void timer_callback()
{
auto message = std_msgs::msg::String();
message.data = "Hello, world!";
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_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MyNode>());
rclcpp::shutdown();
return 0;
}
Python Node Example​
Create a simple Python node in my_py_package/my_node.py:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MyNode(Node):
def __init__(self):
super().__init__('my_node')
self.publisher_ = self.create_publisher(String, 'topic', 10)
timer_period = 0.5 # seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
def timer_callback(self):
msg = String()
msg.data = 'Hello, world!'
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
def main(args=None):
rclpy.init(args=args)
my_node = MyNode()
rclpy.spin(my_node)
my_node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Package Verification​
After creating your package, verify it's properly set up:
-
Check package structure:
ros2 pkg executables my_robot_package -
Build the package:
cd ~/ros2_ws
colcon build --packages-select my_robot_package -
Source the workspace:
source install/setup.bash -
List available nodes:
ros2 run my_robot_package my_node
Common Issues and Troubleshooting​
Missing Dependencies​
- Problem: Build fails due to missing dependencies
- Solution: Add missing dependencies to package.xml and run
rosdep install
Build Errors​
- Problem: Compilation errors during build
- Solution: Check CMakeLists.txt configuration and source file locations
Package Not Found​
- Problem: ROS 2 cannot find the package after building
- Solution: Ensure you've sourced the workspace setup file
Learning Objectives​
By the end of this lesson, you should be able to:
- Create ROS 2 packages using the ros2 pkg create command
- Understand and configure the package.xml file properly
- Set up CMakeLists.txt for C++ packages or setup.py for Python packages
- Organize package structure according to ROS 2 conventions
- Create and configure nodes within packages
- Verify package functionality and troubleshoot common issues