TL;DR:
- ROS 2 (Robot Operating System 2) uses DDS for distributed, real-time communication between nodes, replacing ROS 1’s single-master architecture
- micro-ROS extends ROS 2 to bare-metal microcontrollers like the ESP32 and STM32, bridging deep edge sensors into the ROS 2 ecosystem
- For constrained hardware, pick a lightweight DDS implementation (iceoryx for shared memory, Micro-XRCE-DDS for MCUs) and tune your QoS profiles to reduce overhead
ROS 2 has become the standard communication and tooling framework for robots and autonomous systems in 2026. But most ROS 2 tutorials assume you have a capable workstation or a cloud-connected development machine. At the edge — on an NVIDIA Jetson Orin, a Raspberry Pi 5, or a bare STM32 microcontroller — the constraints are different: limited RAM, no guarantee of network connectivity, real-time requirements, and sometimes battery power. This guide covers what changes when you deploy ROS 2 in those environments.
Why ROS 2 at the Edge
ROS 2 replaced ROS 1’s roscore single-master architecture with DDS (Data Distribution Service), a publish-subscribe middleware standard that’s inherently distributed. There’s no central broker that can become a single point of failure. Nodes discover each other via multicast, communicate peer-to-peer, and continue operating if other nodes disconnect.
This makes ROS 2 much better suited for edge and fog deployments where:
- Nodes run on different physical devices (sensor board, compute board, actuator controller)
- Connectivity to a central system may be intermittent
- Real-time response is needed for safety-critical functions (motor control, obstacle avoidance)
The flip side is that DDS has overhead. On a standard workstation you won’t notice it. On a Cortex-M4 with 64KB RAM, you will.
Choosing Your Platform
NVIDIA Jetson Orin (Nano, NX, AGX): The best edge platform for ROS 2 deployments that need GPU acceleration — computer vision, LiDAR processing, neural network inference. Full Linux, full DDS support. Install via the standard apt packages from the ROS 2 repository. The Orin Nano at 8GB LPDDR5 runs a full ROS 2 Humble or Jazzy installation with Nav2 and local inference comfortably.
Raspberry Pi 5: Viable for ROS 2 nodes that don’t need GPU compute. Useful for sensor aggregation, lower-frequency control loops, and development/testing of nav stacks. Install ROS 2 from the official apt repository on Ubuntu 24.04 Server for Pi. Avoid using the Pi’s Wi-Fi for high-frequency ROS 2 topics — DDS multicast over Wi-Fi is unreliable; use Ethernet or configure your DDS discovery settings to avoid multicast where possible.
STM32 / ESP32 (micro-ROS): For nodes at the sensor/actuator level — IMUs, motor controllers, proximity sensors — micro-ROS provides a minimal ROS 2 implementation that runs on bare metal without an OS. It communicates with a micro-ROS agent running on a more capable host board.
micro-ROS: Bridging Microcontrollers into ROS 2
micro-ROS uses the Micro XRCE-DDS protocol, a lightweight serialisation layer designed for bandwidth-constrained links (UART, SPI, USB). Your microcontroller runs the micro-ROS client library; a micro-ROS agent on the host board translates between XRCE-DDS and standard DDS.
Example setup with ESP32 (using ESP-IDF):
# On your dev machine, install the micro-ROS build system
pip install catkin_pkg lark-parser empy colcon-common-extensions
# Create a micro-ROS workspace
mkdir microros_ws && cd microros_ws
git clone https://github.com/micro-ROS/micro_ros_espidf_component.git
# Configure for your ESP32 target
idf.py set-target esp32s3
idf.py menuconfig # Set transport to UART or UDP
idf.py build && idf.py flash
On the host (Jetson or Pi), run the micro-ROS agent:
docker run -it --rm -v /dev:/dev --privileged \
microros/micro-ros-agent:jazzy serial --dev /dev/ttyUSB0 -b 115200
The agent translates the microcontroller’s sensor publications into standard ROS 2 topics visible to the rest of your node graph.
DDS Configuration for Resource-Constrained Nodes
Default DDS settings are tuned for reliable networks with capable machines. At the edge, you need to tune Quality of Service (QoS) profiles and DDS configuration to reduce memory and bandwidth usage.
Reduce participant discovery overhead:
<!-- dds_profile.xml — tune for constrained environments -->
<dds>
<participant>
<rtps>
<builtin>
<discovery_config>
<leaseDuration>
<sec>30</sec>
</leaseDuration>
<leaseDuration_announcementperiod>
<sec>10</sec>
</leaseDuration_announcementperiod>
</discovery_config>
</builtin>
</rtps>
</participant>
</dds>
Use BEST_EFFORT reliability for high-frequency sensor data:
from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy
sensor_qos = QoSProfile(
reliability=ReliabilityPolicy.BEST_EFFORT,
history=HistoryPolicy.KEEP_LAST,
depth=1
)
For a 100Hz IMU, BEST_EFFORT with depth 1 means you always have the latest reading without buffering, and dropped packets are acceptable because the next one arrives in 10ms.
Eclipse iceoryx for shared-memory IPC: If multiple ROS 2 nodes run on the same board (common on a Jetson), iceoryx provides zero-copy shared memory transport. No serialisation overhead for large messages like camera frames. Install via ros-jazzy-rmw-iceoryx-cpp and set RMW_IMPLEMENTATION=rmw_iceoryx_cpp.
Nav2 on Constrained Hardware
Nav2 (Navigation2) is the standard ROS 2 navigation stack. On a Jetson Orin Nano with a 2D LiDAR, a full Nav2 deployment (SLAM, costmaps, path planner, controller) runs adequately but consumes most of your available RAM. Practical tips:
- Use
slam_toolboxin lifelong mapping mode rather than cartographer for better memory efficiency on maps that update over time - Set costmap resolution to 0.05m rather than the default 0.025m — halves the map memory at the cost of slightly coarser obstacle representation
- Disable the global inflation layer if your robot operates in a consistent environment where precise clearance isn’t critical
Real-Time Considerations
ROS 2 supports real-time execution via the rclcpp callback group API and POSIX real-time scheduling, but you need to configure it explicitly. For safety-critical loops:
# Pin a node's callbacks to SCHED_FIFO for deterministic latency
import subprocess
subprocess.run(['chrt', '--fifo', '80', str(os.getpid())])
Combine with a real-time kernel (PREEMPT_RT patch) on Linux for hard real-time guarantees. NVIDIA Jetson supports PREEMPT_RT on Ubuntu 22.04 and 24.04. The Raspberry Pi 5 requires a custom kernel build.