Directory Structure
- The directory structure of your ROS2 workspace (
training_ws
) would look like this:
training_ws/
├── build/ # Colcon build artifacts (build files)
├── install/ # Colcon install artifacts (setup files)
│ └── local_setup.bash # Setup script for the workspace
├── log/ # Logs from the build process
└── src/ # Source code of ROS packages (contains 'ros_tutorials' here)
└── ros_tutorials/ # Cloned repository
├── turtlesim/ # The turtlesim package
└── other_packages/ # Other packages inside 'ros_tutorials'
build/
: Contains the build files generated bycolcon build
.install/
: Contains the installed packages and setup files necessary for running the workspace.log/
: Contains logs of the build process for debugging and analysis.src/
: The source directory where all ROS2 packages are placed. In this case, it contains the clonedros_tutorials
repository, which includes theturtlesim
package.
When you source install/local_setup.bash
, it sets up your environment to use the packages in the training_ws
workspace.
Create a Package
https://articulatedrobotics.xyz/tutorials/ready-for-ros/packages/
ROS2 Python packages
my_package/ ├── package.xml # File containing meta-information about the package ├── resource/ │ └── my_package # Marker file for the package. ├── setup.cfg # Required when a package has executables, so `ros2 run` can locate them. ├── setup.py # Contains instructions for how to install the package. └── my_package/ # A directory with the same name as your package, used by ROS 2 tools to locate your package; it # contains `__init__.py`.
ROS2 CMake Packages
my_package/ ├── CMakeLists.txt # Describes how to build the code within the package ├── include/ │ └── my_package/ # Directory containing the public headers for the package ├── package.xml # File containing meta-information about the package └── src/ # Directory containing the source code for the package
- Set up workspace
mkdir -p ~/training_ws/src
cd ~/training_ws
- Create an empty package using CMake
ros2 pkg create --build-type ament_cmake my_package
- Open the workspace in VS Code
- Since we’re using python-only code, we can delete the
include
andsrc
folders which are used for C++ code
- Since we’re using python-only code, we can delete the
cd ..
code .

launch
- Create a
launch
folder and add atalker.launch.py
file
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription({
Node(
package='demo_nodes_cpp',
executable='talker'
)
})
- Add a
listener.launch.py
file
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='demo_nodes_py',
executable='listener'
)
])
- This will launch the
talker
andlistener
nodes from the demo_nodes_cpp package:
demo_nodes_cpp/
├── CMakeLists.txt
├── package.xml
├── src/
│ ├── talker.cpp
│ └── listener.cpp
└── launch/
└── demo_nodes_cpp_launch.py
CMakeLists.txt
- Update CMakeLists to tell
colcon
how to build the files- Add the following lines just before
ament_package()
- Add the following lines just before
install(DIRECTORY launch
DESTINATION share/${PROJECT_NAME}
)
package.xml
- Update
package.xml
to include additional information for ROS andcolcon
to recognize and manage the package, including dependencies (e.g. thedemo_nodes_cpp
anddemo_nodes_py
packages)- We use
<exec_depend>
because these dependencies are needed for execution. - Alternatively, we could use
<build_depend>
if the dependencies are required only for building or<depend>
for both building and executing.
- We use
<?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_package</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="email@gmail.com">Name</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<exec_depend>demo_nodes_cpp</exec_depend>
<exec_depend>demo_nodes_py</exec_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
- Additionally, it’s good practice to update other fields in the
package.xml
such as:name
: The name of the package.email
: The maintainer’s contact email.description
: A brief description of the package.license
: The license under which the package is distributed.
Build the Package
- Build the package from the workspace directory
- This option uses symlinks instead of copies, so you don’t need to rebuild when tweaking certain files
colcon build --symlink-install
Push the package to GitHub
- Create a repository on GitHub
- Initialize git (from inside your local
my_package
directory)
echo "# my_package" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:<github_username>/<repo_name>.git
git push -u origin main
Pull the package onto the Pi
- Create a
robot_ws
workspace on the other machine (e.g. Raspberry Pi 4B) and cd into it- Make sure SSH authentication is set up
mkdir -p ~/robot_ws/src
cd ~/robot_ws
- Clone the repo into the
src
folder
git clone git@github.com:<github_username>/<repo_name>.git
cd
back into the root of the workspace for both machines and run
colcon build --symlink-install
Test the package
cd
into the workspace directory and source the workspace (on both machines)
source install/setup.bash
Normally you would have separate terminals for building and running the package
- Run the publisher node on the dev machine
ros2 launch my_package talker.launch.py
- Run the publisher node on the pi
ros2 launch my_package listener.launch.py