AD-R1M software system architecture
The AD-R1M and its variants require first- and third-party software to run on multiple layers to ultimately support a ROS 2 app:
Linux system and kernel
Docker containers
ROS 2 application layer
This is an explanatory overview for each of these layers, what purpose they serve, what the AD-R1M project brings to each, and how they interact.
Linux system
The AD-R1M runs ADI Kuiper Linux, a Debian-based distribution with built-in support for a large number of Analog Devices products.
The AD-R1M linux system layer provides:
User utilities through the
ad-r1mcommandsystemd units for robot bringup after boot
Udev rules
Default robot configuration files
Extra kernel modules and device tree overlays
Of these, some are consistent between robot variants, while others are variant-specific. To handle this, we split them into composable features:
common - Files common to all AD-R1M variants
rpi5 - Support for variants using a Raspberry Pi 5 + ADRD4161
adrd3161 - Support for variants using the ADRD3161 motor drive
etc.
Each of these gets a folder in system/ which contains a rootfs “overlay” providing all files necessary for that feature, e.g.:
system/common/
├── usr
│ ├── bin
│ │ └── ad-r1m
│ ├── lib
│ │ ├── ad-r1m
│ │ │ └── cli
│ │ │ ├── start
│ │ │ └── ...
│ │ └── systemd
│ │ └── system
│ │ │ ├── ad-r1m.target
│ │ │ ├── ad-r1m-bringup.service
│ │ │ └── ...
│ └── share
│ └── ad-r1m
│ └── default-config
│ └── ...
└── ...
These features are combined into debian packages by the packaging logic defined in packaging/debian/. For example, the ad-r1m-system-rpi5 package, which installs all system-level files for the main AD-R1M variant, is a combination of the common, rpi5 and adrd3161 system features. These compositions are defined in packaging/debian/<package>.install, e.g.:
# packaging/debian/ad-r1m-system-rpi5.install
VERSION /usr/lib/ad-r1m/
system/common/* /
system/rpi5/* /
system/adrd3161/* /
Bringup at boot
Automatic robot software startup is defined by the systemd unit ad-r1m.target. Other units relate to it, and they can be split in two categories:
Units for setting up peripherals. In systemd terms, these are all
PartOf,WantedByandBeforead-r1m.target. That makes it so that all must be successful beforead-r1m.targetis considered active, and restartingad-r1m.targetrestarts them all.The
ad-r1m-bringup.serviceunit launches the Docker containers running the ROS app. This isPartOfandAfterad-r1m.target, meaning it will only start after all the previous hardware bringup units are done.
flowchart LR
tgt[ad-r1m.target]
tgt -- Requires --> c1(80-can0.network<br>Configure CAN)
tgt -- Requires --> c2(ad-r1m-imu.service<br>Configure IMU)
tgt -- Requires --> c3(ad-r1m-crsf.service<br>Power up RC receiver)
r[multi-user.target] -- Wants --> tgt
br[ad-r1m-bringup.service<br>Start Docker containers] -- After --> tgt -- Before --> br
r -- Wants --> br
ROS 2 reconfiguration
Runtime configurations are managed as docker compose folders, which by default contain:
ad-r1m.env– Environment variablescompose.yaml– Docker compose file describing which containers to start upros_data/– Directory with data to pass to containers and persist across runs: logs, configs, maps, etc. It is mounted to/ros_data/inside the containers.
The default configuration is present in /usr/share/ad-r1m/default-config and shouldn’t be modified by users. To create an alternate configuration, use the ad-r1m mkconfig command and edit as you please:
~$
ad-r1m mkconfig my-config
~$
cd my-config
~/my-config$
edit ad-r1m.env
ROS_DISTRO=jazzy
~/my-config$
edit compose.yaml
...
To start the robot software using that configuration, enter your configuration folder and run Docker compose commands, e.g.:
~$
cd my-config
~/my-config$
docker compose --env-file ad-r1m.env up
To automatically start an alternative configuration at boot-time, use ad-r1m enable <folder>:
~$
sudo ad-r1m enable my-config
Behind the curtain, this adds an override for ad-r1m-bringup.service that changes its working directory:
# /etc/systemd/system/ad-r1m-bringup.service.d/override.conf
[Service]
WorkingDirectory=/home/analog/my-config/
See also
The Manage AD-R1M runtime configurations tutorial contains more practical examples of the functionality described above.
Docker containers
Because ROS is “picky” about its environment, we use Docker containers for environment isolation.
In the default configuration, all robot nodes are brought up in a single container with ros2 launch ad_r1m_robot bringup.launch.py. Other containers may start up RMW servers (e.g. Zenoh daemon, DDS discovery) or extra application components of your choice.
Docker images should avoid hardware-specific variations as long as reasonable. All hardware differences should be handled by the previous system layer, and devices should be ultimately presented to the container in a hardware-agnostic manner (e.g. CAN adapter should be network interface can0 regardless of adapter type).
A single Dockerfile unifies docker container builds for development with devcontainers, CI builds, CI testing, production builds, development (“tinkering”) builds, etc. using Docker multi-stage builds.
flowchart LR
subgraph "3rd party images"
$BASE_IMAGE[$BASE_IMAGE<br>$ROS_DISTRO-ros-base]
ros-core[$ROS_DISTRO-ros-core]
end
$BASE_IMAGE --> common -- cache rosdeps --> depcacher
common -- install build deps --> build_common -- install exec deps --> devcontainer
build_common -- colcon build --> builder -- install exec deps --> dev
builder -. built packages .-> runner
ros-core --> runner
subgraph "User-facing images"
devcontainer
dev
runner
end
Multiple containers are built from the same commit by changing the build args:
ROS_DISTROhumble(default)jazzy
FROM_IMAGE– Image used as base for intermediary stagesros:$ROS_DISTRO(default)
OVERLAY_WS– Path inside container where to place ROS 2 overlay/ros2_ws(default)
BUILD_PACKAGES– Space-separated list of (meta-)packages to include in buildad_r1m_robot(default) – Metapackage that depends on everything needed for a real robotad_r1m_sim– Metapackage that depends on everything needed for a simulated robot…
The following combinations run in CI, as described in docker/dockers.yml:
Docker tag |
|
|
Supported architectures |
|
humble |
|
|
|
humble |
|
|
|
jazzy |
|
|