- English
- Urdu
- Personalize
Chapter 5: Building ROS 2 Packages
Learning Objectives
By the end of this section, you will be able to:
- Understand ROS 2 package structure and organization
- Create Python and C++ ROS 2 packages
- Use colcon to build and manage workspaces
- Configure package dependencies and metadata
- Organize code following ROS 2 best practices
Introduction
In ROS 2, a package is the fundamental unit of code organization. It's a directory containing nodes, libraries, configuration files, and metadata that together provide specific functionality. Think of packages as modular building blocks—each robot system is composed of multiple packages working together.
This section teaches you how to create, build, and manage ROS 2 packages professionally.
ROS 2 Package Structure
Anatomy of a Package
A typical ROS 2 Python package looks like this:
my_robot_package/
├── package.xml # Package metadata and dependencies
├── setup.py # Python package configuration
├── setup.cfg # Python package setup config
├── resource/ # Package marker files
│ └── my_robot_package
├── my_robot_package/ # Python source code
│ ├── __init__.py
│ ├── my_node.py
│ └── utils.py
└── test/ # Unit tests
├── test_copyright.py
└── test_flake8.py
A C++ package looks like this:
my_cpp_package/
├── package.xml # Package metadata
├── CMakeLists.txt # Build configuration
├── include/ # Header files
│ └── my_cpp_package/
│ └── my_class.hpp
├── src/ # Source files
│ ├── my_node.cpp
│ └── my_class.cpp
└── launch/ # Launch files (optional)
└── my_launch.py
Creating a ROS 2 Workspace
Workspace Structure
A ROS 2 workspace organizes multiple packages:
ros2_ws/
├── src/ # Source space (your packages)
│ ├── package_1/
│ ├── package_2/
│ └── package_3/
├── build/ # Build artifacts (auto-generated)
├── install/ # Installed files (auto-generated)
└── log/ # Build logs (auto-generated)
Creating a Workspace
# Chapter 5: Create workspace directory
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
# Chapter 5: Build the workspace (even if empty)
colcon build
# Chapter 5: Source the workspace
source install/setup.bash
Important: Always source your workspace before using packages in it!
Creating a Python Package
Using ros2 pkg create
cd ~/ros2_ws/src
# Chapter 5: Create a Python package
ros2 pkg create --build-type ament_python \
--node-name my_first_node \
my_robot_package \
--dependencies rclpy geometry_msgs sensor_msgs
# Chapter 5: This creates:
# Chapter 5: - Package structure
# Chapter 5: - A sample node (my_first_node.py)
# Chapter 5: - package.xml with dependencies
# Chapter 5: - setup.py configured
Understanding package.xml
<?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.1</version>
<description>My robot control package</description>
<maintainer email="you@example.com">Your Name</maintainer>
<license>Apache-2.0</license>
<!-- Build tool dependency -->
<buildtool_depend>ament_python</buildtool_depend>
<!-- Runtime dependencies -->
<exec_depend>rclpy</exec_depend>
<exec_depend>geometry_msgs</exec_depend>
<exec_depend>sensor_msgs</exec_depend>
<!-- Test dependencies -->
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>
Understanding setup.py
from setuptools import setup
package_name = 'my_robot_package'
setup(
name=package_name,
version='0.0.1',
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='you@example.com',
description='My robot control package',
license='Apache-2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'my_first_node = my_robot_package.my_first_node:main',
'velocity_controller = my_robot_package.velocity_controller:main',
],
},
)
Key Point: The entry_points section defines executable nodes!
Creating a C++ Package
Using ros2 pkg create
cd ~/ros2_ws/src
# Chapter 5: Create a C++ package
ros2 pkg create --build-type ament_cmake \
--node-name my_cpp_node \
my_cpp_package \
--dependencies rclcpp geometry_msgs sensor_msgs
Understanding CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(my_cpp_package)
# Chapter 5: Compiler settings
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# Chapter 5: Find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
# Chapter 5: Add executable
add_executable(my_cpp_node src/my_cpp_node.cpp)
ament_target_dependencies(my_cpp_node
rclcpp
geometry_msgs
sensor_msgs
)
# Chapter 5: Install targets
install(TARGETS
my_cpp_node
DESTINATION lib/${PROJECT_NAME}
)
# Chapter 5: Install launch files (if any)
install(DIRECTORY
launch
DESTINATION share/${PROJECT_NAME}/
)
ament_package()
Building with colcon
Basic Build Commands
# Chapter 5: Build all packages in workspace
cd ~/ros2_ws
colcon build
# Chapter 5: Build specific package
colcon build --packages-select my_robot_package
# Chapter 5: Build with symbolic links (Python only, for development)
colcon build --symlink-install
# Chapter 5: Build with verbose output
colcon build --event-handlers console_direct+
# Chapter 5: Clean build
rm -rf build install log
colcon build
Build Options
| Option | Description |
|---|---|
--packages-select PKG | Build only specified package |
--packages-up-to PKG | Build package and its dependencies |
--symlink-install | Use symlinks (no rebuild for Python changes) |
--cmake-args | Pass arguments to CMake |
--parallel-workers N | Use N parallel jobs |
Example: Development Workflow
# Chapter 5: Create package
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_python my_pkg --dependencies rclpy
# Chapter 5: Write code
cd my_pkg/my_pkg
# Chapter 5: Edit your_node.py
# Chapter 5: Add entry point in setup.py
# Chapter 5: 'your_node = my_pkg.your_node:main'
# Chapter 5: Build with symlinks (for development)
cd ~/ros2_ws
colcon build --packages-select my_pkg --symlink-install
# Chapter 5: Source workspace
source install/setup.bash
# Chapter 5: Run node
ros2 run my_pkg your_node
# Chapter 5: Make changes to Python code
# Chapter 5: No rebuild needed with --symlink-install!
# Chapter 5: Just re-run: ros2 run my_pkg your_node
Package Dependencies
Types of Dependencies
- buildtool_depend: Tools needed to build (e.g.,
ament_python,ament_cmake) - build_depend: Packages needed at build time (C++ headers)
- exec_depend: Packages needed at runtime
- depend: Both build and exec dependency (shorthand)
- test_depend: Packages needed for testing
Example: Complete Dependencies
<package format="3">
<name>humanoid_controller</name>
<!-- Build tool -->
<buildtool_depend>ament_cmake</buildtool_depend>
<!-- Core ROS 2 -->
<depend>rclcpp</depend>
<!-- Messages -->
<depend>geometry_msgs</depend>
<depend>sensor_msgs</depend>
<depend>std_msgs</depend>
<!-- Custom messages (if you have them) -->
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<!-- Testing -->
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
</package>
Best Practices
Package Organization
✅ Do:
- One package per major functionality (e.g.,
navigation,perception,control) - Keep packages focused and modular
- Use meaningful package names (
my_robot_navigation, notpkg1) - Include README.md in each package
❌ Don't:
- Put everything in one giant package
- Mix unrelated functionality
- Use generic names like
utilsorcommon
Code Organization
my_robot_package/
├── my_robot_package/
│ ├── __init__.py
│ ├── nodes/ # Executable nodes
│ │ ├── __init__.py
│ │ ├── controller.py
│ │ └── planner.py
│ ├── utils/ # Utility functions
│ │ ├── __init__.py
│ │ ├── math_utils.py
│ │ └── transforms.py
│ └── config/ # Configuration classes
│ ├── __init__.py
│ └── robot_config.py
├── launch/ # Launch files
│ └── robot.launch.py
├── config/ # YAML config files
│ └── params.yaml
└── test/ # Tests
└── test_controller.py
Naming Conventions
- Packages:
snake_case(e.g.,my_robot_navigation) - Nodes:
snake_case(e.g.,velocity_controller) - Topics:
/snake_case(e.g.,/cmd_vel,/robot/odom) - Services:
/snake_case(e.g.,/get_plan,/reset_odom) - Python files:
snake_case.py - C++ files:
snake_case.cpp/.hpp
Example: Complete Package Creation
Let's create a complete package for a simple robot controller:
# Chapter 5: Create workspace
mkdir -p ~/humanoid_ws/src
cd ~/humanoid_ws/src
# Chapter 5: Create package
ros2 pkg create --build-type ament_python \
humanoid_controller \
--dependencies rclpy geometry_msgs sensor_msgs
# Chapter 5: Create node file
cd humanoid_controller/humanoid_controller
cat > balance_controller.py << 'EOF'
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Imu
from geometry_msgs.msg import Twist
class BalanceController(Node):
def __init__(self):
super().__init__('balance_controller')
# Subscribe to IMU
self.imu_sub = self.create_subscription(
Imu, '/imu/data', self.imu_callback, 10
)
# Publish velocity commands
self.cmd_pub = self.create_publisher(Twist, '/cmd_vel', 10)
self.get_logger().info('Balance Controller started')
def imu_callback(self, msg):
# Simple balance control (placeholder)
cmd = Twist()
# If tilting forward, move backward
if msg.linear_acceleration.x > 1.0:
cmd.linear.x = -0.1
self.cmd_pub.publish(cmd)
def main(args=None):
rclpy.init(args=args)
node = BalanceController()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
EOF
# Chapter 5: Update setup.py entry points
# Chapter 5: Add: 'balance_controller = humanoid_controller.balance_controller:main'
# Chapter 5: Build
cd ~/humanoid_ws
colcon build --packages-select humanoid_controller --symlink-install
# Chapter 5: Source and run
source install/setup.bash
ros2 run humanoid_controller balance_controller
Key Takeaways
✅ Packages are the fundamental unit of code organization in ROS 2
✅ colcon is the build tool (replaces catkin from ROS 1)
✅ Workspaces contain multiple packages in the src/ directory
✅ package.xml defines metadata and dependencies
✅ setup.py (Python) or CMakeLists.txt (C++) configures the build
✅ --symlink-install enables rapid Python development (no rebuild needed)
Reflection Questions
- Why is it better to have multiple small packages instead of one large package?
- What's the difference between
build_dependandexec_depend? - When should you use
--symlink-installand when should you avoid it? - How would you organize a package that includes both perception and control nodes?
Further Reading
- ROS 2 Packages: docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Creating-Your-First-ROS2-Package.html
- colcon Documentation: colcon.readthedocs.io
- ament_cmake: docs.ros.org/en/humble/How-To-Guides/Ament-CMake-Documentation.html
Previous Section: ← 2.2 Nodes and Communication
Next Section: 2.4 Launch Files and Parameters →
Chapter 5: ROS 2 پیکجز بنانا (Building ROS 2 Packages)
سیکھنے کے مقاصد (Learning Objectives)
اس سیکشن کے اختتام تک، آپ اس قابل ہو جائیں گے کہ:
- ROS 2 پیکج کی ساخت اور تنظیم کو سمجھ سکیں
- Python اور C++ میں ROS 2 پیکجز بنا سکیں
- ورک اسپیس (Workspace) کے انتظام کے لیے
colconکا استعمال کر سکیں - پیکج کی وابستگیوں (Dependencies) اور میٹا ڈیٹا کو کنفیگر کر سکیں
- بہترین روایات (Best Practices) کے مطابق کوڈ کو منظم کر سکیں
تعارف (Introduction)
ROS 2 میں، ایک پیکج (Package) کوڈ کی تنظیم کی بنیادی اکائی ہے۔ یہ ایک ڈائریکٹری ہے جس میں نوڈس، لائبریریز، کنفیگریشن فائلز اور میٹا ڈیٹا شامل ہوتا ہے جو مل کر مخصوص فعالیت (Functionality) فراہم کرتے ہیں۔ پیکجز کو ماڈیولر بلڈنگ بلاکس کے طور پر سمجھیں — ہر روبوٹ سسٹم مل کر کام کرنے والے متعدد پیکجز پر مشتمل ہوتا ہے۔
ROS 2 پیکج کی ساخت (Package Structure)
ایک پیکج کے اجزاء
ایک عام ROS 2 Python پیکج کی ساخت درج ذیل ہوتی ہے:
my_robot_package/
├── package.xml # پیکج کا میٹا ڈیٹا اور ڈیپینڈنسیز
├── setup.py # پائتھن پیکج کنفیگریشن
├── my_robot_package/ # پائتھن سورس کوڈ
│ ├── __init__.py
│ └── my_node.py
└── test/ # یونٹ ٹیسٹ
ROS 2 ورک اسپیس بنانا
ورک اسپیس کی ساخت
ایک ورک اسپیس متعدد پیکجز کو منظم کرتا ہے:
ros2_ws/
├── src/ # سورس اسپیس (آپ کے پیکجز)
├── build/ # آٹو جنریٹڈ فائلز
├── install/ # انسٹال شدہ فائلز
└── log/ # بلڈ لاگز
ورک اسپیس بنانے کا طریقہ
# Chapter 5: ورک اسپیس ڈائریکٹری بنائیں
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
# Chapter 5: ورک اسپیس کو بلڈ کریں
colcon build
# Chapter 5: ورک اسپیس کو سورس کریں (لازمی قدم)
source install/setup.bash
پائتھن پیکج بنانا (Creating a Python Package)
ros2 pkg create کا استعمال
cd ~/ros2_ws/src
# Chapter 5: پائتھن پیکج بنائیں
ros2 pkg create --build-type ament_python \
--node-name my_first_node \
my_robot_package \
--dependencies rclpy geometry_msgs
colcon کے ساتھ بلڈ کرنا
بنیادی بلڈ کمانڈز
# Chapter 5: تمام پیکجز کو بلڈ کریں
colcon build
# Chapter 5: کسی مخصوص پیکج کو بلڈ کریں
colcon build --packages-select my_robot_package
# Chapter 5: سم لنک انسٹال (ڈیولپمنٹ کے لیے بہترین)
colcon build --symlink-install
--symlink-install کا فائدہ: پائتھن کوڈ میں تبدیلی کے بعد آپ کو دوبارہ بلڈ کرنے کی ضرورت نہیں پڑتی، صرف نوڈ دوبارہ چلانا کافی ہوتا ہے۔
بہترین روایات (Best Practices)
✅ کیا کریں:
-ہر بڑی فنکشنلٹی کے لیے ایک الگ پیکج بنائیں (مثلاً navigation, perception)۔
- پیکج کے نام چھوٹے اور واضح رکھیں (جیسے
my_robot_control)۔ -کوڈ کو منظم رکھنے کے لیےsnake_caseکا استعمال کریں۔
❌ کیا نہ کریں:
- تمام کوڈ ایک ہی بڑے پیکج میں نہ ڈالیں۔
- غیر متعلقہ فنکشنلٹی کو مکس نہ کریں۔
کلیدی نکات (Key Takeaways)
✅ پیکجز ROS 2 میں کوڈ آرگنائزیشن کی بنیادی اکائی ہیں
✅ colcon بلڈ کرنے کا جدید ٹول ہے
✅ package.xml میں پیکج کی تمام ڈیپینڈنسیز لکھی جاتی ہیں
✅ --symlink-install پائتھن ڈیولپمنٹ کو تیز بناتا ہے
گزشتہ سیکشن: ← 2.2 نوڈس اور کمیونیکیشن
اگلا سیکشن: 2.4 لانچ فائلز اور پیرامیٹرز →
Personalization features coming soon...