9) Packages

A package is the high-level container in ROS 2. All code that is to be run with ros2 run ... must be in a package.

2 Types of Packages

  • Python Packages: These use setup.py and package.xml. It uses standard python installation tools under the hood.

  • C++ Packages: Uses CMakeLists.txt and package.xml. It will compile all code into binary executables.

Build System & Environment

Build System (colcon)

When we run colcon build, it looks for a package.xml file in every folder (package). This file defines the package name, version, and dependencies.

  • colcon: Like the main universal way of building packages regardless of the type of source files (python or C++).

  • ament: ament_python or ament_cmake are build types (specific ways we can build). colcon sees this and understands which ways we want to build our package.

    • ament_python: Always contains python source files and a setup.py file, sometimes containing a setup.cfg.

    • ament_cmake: Usually contains C++ source files, but also used for "resource-only" packages that hold models, maps, custom messages, or configuration files.

Build Type Tags (Found in package.xml)

  • For C++:
<export>
  <build_type>ament_cmake</build_type>
</export>
  • For Python:
<export>
  <build_type>ament_python</build_type>
</export>

package.xml

Helping way of thinking: Just an ID card and dependency checklist.

  • <buildtool_depend>: Tells ROS 2 what software is needed to actually compile the package.

Note: Python (an interpreted language) packages don't need this because it doesn't get compiled unlike C++. It simply gets copied over to /install, therefore we don't need this. C++ packages explicitly require this to be stated in package.xml cos it needs to be compiled into machine's binary code.

  • <exec_depend>: Called Execution Dependencies, which is what our launch files depend on when we are running it.

  • <test_depend>: Dependencies we require to test our code, such as pytest, flake8, and pep257 which are all standard Python code linters (tools that check your code for formatting errors).

  • <build_type>ament_python</build_type>: The tag that colcon requires to know whether to look for CMakeLists.txt or setup.py on how to handle the installation (into /install).

CMakeLists.txt (For C++ & Resource Packages)

  • project(...): This gives your package a name. It also creates a handy background variable called ${PROJECT_NAME}.

  • install(DIRECTORY ...): This is essentially a "copy-paste" command for the build process. When you run colcon build, ROS 2 doesn't use the files directly from your source code folder. It needs to put them in a central "install space" so the rest of the robot system can find them.

  • DESTINATION share/${PROJECT_NAME}/: This is where those folders get pasted. In ROS 2, description files, launch files, and models are standardly placed in the share/ directory of the installed package.

setup.py (For Python Packages)

  • packages=find_packages(exclude=['test']): find_packages is a python tool that automatically searches the package directory and grabs all the python code so they can be installed.

  • exclude=['test']: Intentionally ignores test/ folders so that testing scripts don't get installed onto the robot.

  • install_requires=['setuptools'], tests_require=['pytest']: Tells the system it needs the setuptools library to build the package, and the pytest package to run tests.

  • data_files=[...]:

    • Tuple 1: ('share/ament_index/resource_index/packages', ['resource/' + package_name]) -> Puts a blank marker file deep inside the ROS 2 system. When we type ros2 pkg list, ROS 2 searches this exact folder to know the existing packages.

    • Tuple 2: ('share/' + package_name, ['package.xml']) -> Copies the package.xml file into the share/rc_car_teleop installation folder so ROS 2 can read the dependencies at runtime.

Entry Points Layout

Python

entry_points={
    'console_scripts': [
        'teleop          = rc_car_teleop.teleop:main',
        'odometry        = rc_car_teleop.odom:main',
        'lidar_processor = rc_car_teleop.lidar_processor:main',
        'yolo            = rc_car_teleop.yolo:main',
        'camera          = rc_car_teleop.camera:main',
        'brain           = rc_car_teleop.brain:main',
    ],
},
  • teleop: This is the name of the executable to create. We use it when we run the command: ros2 run rc_car_teleop teleop.

  • rc_car_teleop.teleop:main: This is the path to the code. It tells ROS 2, "Look inside the rc_car_teleop folder, find the teleop.py script, and execute the function called main()."

launch.py

  • Structure: Split into 3 parts: Imports, Generator Function, and Execution/Return.

  • Purpose: Allows us to run multiple configured nodes simultaneously via:

ros2 launch my_package my_launch.py