Robotics Software Architecture: Design Patterns and Practices
Robotics software architecture addresses the unique challenges of controlling physical systems in real time. Unlike web applications or data processing pipelines, robotics software must manage asynchronous sensor streams, hard real-time deadlines, distributed computation across heterogeneous hardware, and system-level fault tolerance. This guide covers the architectural patterns that have proven effective in production robotics systems: component-based design, state machines and behavior trees, real-time design patterns, ROS architectural conventions, DDS middleware fundamentals, and testing strategies.
Software Challenges in Robotics
Robotics systems face architectural challenges that distinguish them from other software domains. Hard real-time constraints mean that missing a control loop deadline can cause physical damage or safety incidents. Multimodal sensor fusion requires integrating data from cameras, LiDAR, IMUs, and encoders that arrive at different rates and timestamps. Distributed deployment across microcontrollers, embedded PCs, and cloud servers demands communication patterns that tolerate variable latency and intermittent connectivity.
The complexity spectrum ranges from simple reactive systems (a line-following robot with no state) to deliberative systems (an autonomous vehicle with prediction, planning, and execution components). Brooks’ subsumption architecture from the 1980s proposed layered reactive behaviors as an alternative to the then-dominant sense-plan-act paradigm (Brooks, “A Robust Layered Control System for a Mobile Robot,” IEEE Journal of Robotics and Automation, vol. 2, no. 1, pp. 14-23, 1986). Modern architectures typically combine reactive and deliberative layers in hybrid structures that provide both fast reflex responses and considered planning.
Hardware abstraction is a key architectural concern. Sensors and actuators from different manufacturers with different interfaces must be interchangeable at the software level. ROS driver conventions standardize this: a LiDAR driver publishes sensor_msgs/LaserScan regardless of whether the hardware is a Hokuyo URG or SICK LMS.
Component-Based Architecture
Component-based software engineering decomposes the system into independently developed, tested, and deployed components that communicate through well-defined interfaces. ROS nodes are the canonical component model in robotics. Each node is a process that communicates through ROS topics, services, and actions. A typical autonomous mobile robot architecture includes nodes for: LiDAR driver, wheel encoder driver, IMU driver, odometry estimator, map server, global planner, local planner, motor controller, diagnostics monitor, and user interface bridge.
Interface contracts between components are defined by message types. Changing a message field is a breaking change that requires updating all producers and consumers. Proto files (Protocol Buffers) and IDL interfaces in ROS 2 provide versioning mechanisms, but in practice, interface stability is maintained through disciplined code review and semantic versioning.
The lifecycle node pattern in ROS 2 manages component initialization, activation, deactivation, and shutdown in a deterministic state machine. A camera driver lifecycle node transitions through: unconfigured to inactive to active to finalizing. The middleware manages these transitions, enabling the system to recover from component failures by restarting affected nodes. Loose coupling between components enables independent development and testing — a global planner component developed by one team can be replaced without modifying the local planner or motor controller components, as long as the interface (nav_msgs/Path) remains consistent.
Event-Driven and Data Distribution Patterns
Robotics systems are fundamentally event-driven. Sensor data arrives asynchronously, and the system must process and respond within bounded time. The publisher-subscriber pattern decouples data producers from consumers. A LiDAR driver publishes laser scans without knowing which nodes consume them. The navigation stack, visualization tools, and logging systems each subscribe independently.
ROS 2 uses DDS (Data Distribution Service) as its middleware layer. DDS provides automatic node discovery, configurable quality of service, and security through authentication and encryption. The DDS global data space model means any node can discover and communicate with any other node on the network without a central broker. This discovery mechanism is essential for large robot systems where nodes may start and stop dynamically.
Quality of service (QoS) policies tune communication behavior per topic. The sensor data profile uses best-effort reliability (dropping late messages) and a small queue depth to minimize latency. The control command profile uses reliable delivery within a bounded deadline. The deadline QoS policy enables the receiver to detect communication failures — if no command arrives within the deadline, the receiver triggers a failsafe. The liveliness QoS policy detects crashed publishers, enabling automated recovery.
Data distribution patterns beyond pub-sub include the request-response pattern for synchronous operations (service calls in ROS), the action pattern for long-running tasks with feedback and preemption, and the parameter pattern for runtime configuration. Choosing the right pattern for each communication channel simplifies system reasoning and debugging.
State Machines and Behavior Trees
Autonomous robots sequence behaviors using state machines or behavior trees. Finite state machines (FSM) define a set of states and transitions between them. A robot charging state machine might have states: APPROACHING, DOCKING, CHARGING, and UNDOCKING. Transitions fire on events: dock detected, charger connected, battery full.
Hierarchical state machines (HSM) extend FSMs with nested states. Within the NAVIGATING state, the robot might be in sub-states LOCALIZING, PLANNING, or EXECUTING. Hierarchical decomposition reduces state explosion — the total number of states equals the sum rather than the product of sub-state counts. The UML state machine formalism provides a standardized notation for hierarchical state machines widely used in robotics.
Behavior trees (BTs) represent behavior as a tree of composable nodes. Leaf nodes are actions (send a velocity command) or conditions (is the battery low?). Control nodes sequence or select child behaviors. The sequence node executes children in order until one fails. The fallback (selector) node executes children until one succeeds. Decorator nodes modify child behavior — inverting success, retrying on failure, or limiting execution time.
Behavior trees have several advantages over FSMs for robotics. Composability lets developers build complex behaviors from libraries of reusable condition and action nodes. Reactivity means the tree is traversed every tick (typically 10-100 Hz), so the robot responds immediately to changing conditions. Colledanchise and Ogren provide the comprehensive reference on behavior trees for robotics (Colledanchise and Ogren, Behavior Trees in Robotics and AI: An Introduction, CRC Press, 2018). Visualization tools like Groot provide graphical behavior tree editing.
Real-Time Constraints
Real-time software in robotics must complete computations within bounded time intervals. Missing a deadline in a control loop degrades performance; missing deadlines in a safety monitor can cause accidents. Real-time task scheduling on Linux uses SCHED_FIFO or SCHED_RR policies with priority assignment. The highest priority task — typically the motor control loop — runs at 1-10 kHz. Lower priority tasks handle sensor processing, planning, and communication.
Priority inheritance protocols prevent priority inversion where a low-priority task holds a lock needed by a high-priority task. The PREEMPT_RT kernel patch set, now largely merged into mainline Linux, provides fully preemptible kernel code, high-resolution timers, and priority inheritance for most kernel locks. These features make Linux suitable for soft real-time control up to approximately 10 kHz.
Lock-free programming patterns minimize priority inversion and blocking. Ring buffers for sensor data publication, atomic operations for shared state, and read-copy-update (RCU) patterns for configuration data are common in robotics real-time code. For hard real-time tasks, the control loop runs on a dedicated microcontroller communicating with the Linux application processor over a deterministic link.
Testing and Validation
Testing robotics software requires approaches beyond standard software testing. Simulation testing validates behavior in controlled, repeatable environments. Regression testing in simulation detects when changes break previously functional behavior. Unit tests verify individual components in isolation. A sensor driver’s unit test validates that it correctly parses manufacturer protocol data into ROS messages. A planner’s unit test verifies that given a known map and start/goal poses, the planned path avoids obstacles and reaches the goal.
Integration tests verify component interactions. A navigation integration test starts the LiDAR driver, odometry estimator, global planner, and local planner in simulation, sends a goal pose, and verifies the robot reaches the goal within tolerance. Hardware-in-the-loop (HIL) testing runs the production software against a simulated plant, validating timing, resource usage, and interface correctness.
Scenario-based testing uses libraries like ScenarioRunner to define concrete test cases with automated execution of scenario suites for regression coverage. Continuous integration pipelines for robotics typically include linters, unit tests, simulation-based integration tests, and static analysis for real-time properties.
FAQ
What is the difference between a state machine and a behavior tree?
State machines have fixed state topology and transitions. Behavior trees are composable trees of condition and action nodes evaluated each tick. BTs offer better modularity, reusability, and visualization for complex robot behaviors.
How do I handle sensor failures in the architecture?
Design for graceful degradation: each sensor node reports health status on /diagnostics. The state estimator uses sensor quality estimates to weight inputs — a failing sensor receives zero weight. The planner and controller receive valid estimates even when individual sensors fail.
Should I use ROS 2 for a production robot?
ROS 2 is suitable for production robots with its DDS-based middleware, real-time support, and security features. However, safety-critical applications require additional certification layers. ROS 2 provides libraries and conventions, not safety certification.
How do I split computation between embedded controllers and the main computer?
Hard real-time control loops (motor control at 1-10 kHz) run on microcontrollers with FreeRTOS. Mid-rate loops (state estimation at 100-1000 Hz) can run on either. High-level perception, planning, and UI run on the main computer with Linux. Communication uses ROS 2 over serial or Ethernet bridges.
How do I design for modularity so I can swap components?
Define stable message interfaces for each component boundary. Document assumed semantics: which frame a pose is in, the timestamp source, the expected update rate. Use factory patterns and pluginlib to load alternative implementations of the same interface.
Related: Study the robotics programming guide for the broader software stack context. Learn robot simulation for testing architectures before hardware deployment. Explore embedded robotics for real-time firmware patterns.