Develop Your Own Packages
Once you're comfortable operating your drone, the next step is building your own ROS packages on top of the MRS UAV System. To get started, you need a development environment that matches the drone's onboard stack.
What You're Building
A ROS workspace is just a folder with a src/ directory containing one or more packages, each with its own package.xml and CMakeLists.txt. A build tool compiles every package in src/ at once; you then source install/setup.bash to make them available.
Setting Up Your Workspace
- Docker
- Native installation
The drone's onboard computer might have a prepared ~/example_session/development session (similar to the Docker session used in Autonomous Flight), except its ros2_ws/ comes with the mrs_core_examples packages checked out and its tmux session pre-wired to launch them. That's the fastest way to see an example package fly before you write your own — see Starting From an Example Package below.
The template's docker/ folder builds a development container on top of the same MRS UAV System image used by the Apptainer and native installs, so you're developing against the exact stack your drone runs — just with ROS2 (Jazzy) tooling layered on top and a ros2_ws ready for your own packages. A few things the image and compose file set up for you:
- A non-root user matching your host UID/GID, so files the container creates in
ros2_ws/are owned by you, notroot. - Host networking, so ROS2's DDS discovery works between the container and any other machine on your network without extra configuration.
- X11 forwarding, so GUI tools like Rviz, Gazebo, or QGroundControl display on your desktop as if they ran natively.
- Bind-mounted
ros2_ws/andtmux/folders (not baked into the image), so edits you make on the host with your own editor take effect immediately — no rebuild needed to pick up source changes. UAV_NAME,UAV_TYPE,UAV_MASS, andROS_DOMAIN_IDenvironment variables, wired through so your packages address the right robot by default.
Steps
-
The workspace sources still need to be cloned on the host first, since they're bind-mounted rather than built into the image. Install vcstool and pull in the repositories listed in
.repos:cd ~/git/<your-repo>vcs import < .reposUpdate them later with
vcs pull -n. -
From the repository root, use the provided scripts — they set the correct user/group ID, GPU access, and X server access for you automatically:
./start.sh # build (if needed), start the container and launch the tmux sessions./start.sh dev # build (if needed), start the container and attach an interactive shell instead./build.sh # install rosdep dependencies and (re)build the workspace with colcon inside the container, then stop it./kill.sh # stop the running containerGPU support is auto-detected: an AMD GPU needs nothing extra, while an NVIDIA GPU is picked up automatically when
nvidia-smiworks on the host (this requires the NVIDIA Container Toolkit).If you'd rather drive
docker composedirectly instead of the wrapper scripts:docker compose -f docker/compose.yaml --project-directory . up -d --build# On an NVIDIA host, also layer in the GPU reservation:# docker compose -f docker/compose.yaml -f docker/compose.nvidia.yaml --project-directory . up -d --builddocker exec -it meta-package bash
Installs the workspace directly on your host — useful if you'd rather not run everything in a container, but you're responsible for keeping your system's ROS2 install in sync with what the drone expects.
Steps
-
Clone your repository and link it into a ROS2 workspace:
mkdir -p ~/ros2_ws/srccd ~/ros2_ws/srcgit clone git@github.com:<your-org>/<your-repo>.git -
Install vcstool and pull in the repositories listed in
.repos:cd ~/ros2_ws/src/<your-repo>vcs import < .reposUpdate them later with
vcs pull -n. -
Install dependencies with
rosdepand build withcolcon:cd ~/ros2_wssudo apt update && rosdep updaterosdep install --from-paths src --ignore-src -r -ycolcon build --symlink-installsource install/setup.bash
Starting From an Example Package
Rather than writing a package from scratch, base it on one of the mrs_core_examples packages — each one is a working, documented starting point for a specific kind of node:
example_waypoint_flier(C++) — a full MRS component that flies a list of waypoints loaded from a config file, exposed as services. The best starting point for a custom mission node, and the one already wired up in the onboard~/example_session/developmenttmux session (see below).example_waypoint_flier_native(C++) — the same idea, but built with vanilla ROS2 APIs instead of the MRS wrappers.example_controller_plugin,example_tracker_plugin,example_estimator_plugin(C++) — plugins for theControlManager, for when you need custom control, trajectory generation, or state estimation.example_pluginlib(C++) — a minimal example of the ROS pluginlib mechanism the plugins above are built on.example_sweeping_generator(Python) — a minimal Python node that generates a sweeping path.
Pull in an example and rename it as your own
-
Add
mrs_core_examplesto your workspace's.reposfile (or clone it directly intoros2_ws/src/mrs_core_examples):repositories:ros2_ws/src/mrs_core_examples:type: giturl: https://github.com/ctu-mrs/mrs_core_examples.gitversion: mainvcs import < .repos -
Repurpose the example you picked into your own package. The repo ships a
repurpose_package.shscript that renames every occurrence of the old package name — in files, filenames, andCamelCasesymbols — to your new one, and re-initializes it as a fresh git repo:cd ~/ros2_ws/src/mrs_core_examples./repurpose_package.sh example_waypoint_flier my_first_packagetipThe script asks for confirmation before each change (and supports
--dry-runto preview them first). Run it from insidemrs_core_examplesso it can find the package directory by name; it only touches files under that package. -
Move the renamed package into your workspace and drop the scratch clone:
mv cpp/example_waypoint_flier ~/ros2_ws/src/my_first_packagerm -rf ~/ros2_ws/src/mrs_core_examplesRemove the
mrs_core_examplesentry from.repostoo, unless you plan to pull more examples from it later. -
Build it like any other package in your workspace (see Setting Up Your Workspace above), then launch it:
colcon build --packages-select my_first_package --symlink-installsource install/setup.bashros2 launch my_first_package my_first_package.launch.py uav_name:=$UAV_NAME
Try the stock example on the drone first
Before forking example_waypoint_flier into your own package, it's worth flying the unmodified version once. The onboard ~/example_session/development session already has mrs_core_examples checked out in its ros2_ws/src/ and its tmux session has an example_waypoint_flier window with the launch command and service calls pre-typed:
cd ~/example_session/development
./start.sh
Switch to the example_waypoint_flier window, launch it, then call fly_to_first_waypoint and start_waypoints_following from the pre-typed commands. See Autonomous Flight for the takeoff/arming procedure, and Simulation to try it without touching real hardware first.
Next Steps
Once your workspace is built (either way), ./start.sh launches tmux sessions for the simulator and your UAVs. See the template repository's tmux/README.md for how those sessions are organized and how to add your own nodes to them. To try things out before touching real hardware, see Simulation.