Chapter 12: ROS/ROS2 Foundations (Practical Intro)
Lesson 1: ROS Philosophy and Ecosystem
This lesson introduces the design philosophy behind the Robot Operating System (ROS) and its successor ROS 2, treating them as principled middleware for building distributed robot software. We formalize ROS as a computation graph, analyze timing and reliability properties using tools from linear systems and networks, and survey the core ecosystem (tools, packages, and client libraries). We finish with minimal cross-language examples that embody the ROS mindset.
1. What Problem Does ROS Solve?
Robots are distributed cyber-physical systems. Even simple robots combine multiple sensors, controllers, estimators, and planners that must communicate. Writing all communication and tooling from scratch leads to fragile, non-reusable code. ROS provides:
- A distributed runtime where independent processes communicate via standard patterns.
- A software packaging/build system and shared conventions.
- Introspection, logging, visualization, and simulation tooling.
- A large open-source library ecosystem.
We can model a robot software stack as a set of concurrently running computational blocks. Let \( \mathcal{B} = \{b_1,\dots,b_n\} \) be blocks, each with input/output signals (messages). Each block is an operator mapping input streams to output streams:
\[ b_i: \ \mathcal{U}_i(t) \rightarrow \mathcal{Y}_i(t), \quad \mathcal{U}_i(t)=\{u_{i1}(t),\dots,u_{im_i}(t)\}. \]
The core difficulty is implementing the interconnection \( u_{ij}(t)=y_{k\ell}(t-\delta_{k\ell\rightarrow ij}) \) across heterogeneous hardware and networks, while keeping the system debuggable and reusable. ROS abstracts this interconnection.
2. ROS Philosophy
ROS is not an operating system in the classic sense. It is a set of conventions and middleware enabling modular, distributed robotics. The philosophy is summarized by five principles:
- Computation Graph First: a robot is a graph of nodes connected by typed channels.
- Loose Coupling via Message Passing: processes exchange data without shared memory assumptions.
- Single Responsibility Nodes: small nodes are easier to test, replace, and reuse.
- Tools are as Important as Runtime: record/replay, visualization, and debugging are first-class.
- Open Ecosystem: standardized packages allow research code to become shared infrastructure.
These principles can be formalized using graph theory. Define the ROS computation graph as a directed multigraph \( G = (V,E,\mathcal{T}) \) where:
- \( V \) is the set of nodes (processes).
- \( E \subseteq V \times V \) is the set of directed communication edges.
- \( \mathcal{T} \) is a set of message types, and each edge has a type label.
For node \( v_i \), let its incoming topics be \( \mathcal{I}_i = \{e_{k\rightarrow i}\} \) and outgoing topics be \( \mathcal{O}_i = \{e_{i\rightarrow j}\} \). The runtime guarantees that each edge carries a time-stamped message stream \( x_{e}(t) \in \mathbb{R}^{d_e} \) matching its type.
flowchart LR
S["Sensors"] --> N1["Node: driver"]
N1 -->|topic| N2["Node: filter"]
N2 -->|topic| N3["Node: controller"]
N3 --> A["Actuators"]
subgraph Tools
RQ["Introspection/plotting"]
RV["Visualization"]
RB["Record/Replay"]
end
N1 -.-> RQ
N2 -.-> RV
N2 -.-> RB
The graph view emphasizes replaceability: any node can be swapped as long as it preserves type and timing contracts on its incident edges.
3. Timing and the Control View
Students already know linear control; ROS adds a new ingredient: distributed sampling and delay. Consider a discrete-time control loop split across nodes:
\[ x[k+1] = A x[k] + B u[k], \quad y[k] = C x[k]. \]
Suppose sensor node publishes \( y[k] \), controller node computes \( u[k] \), and actuator node applies it. Let network+processing delay be \( d \) samples. Then the closed-loop input becomes: \( u[k]=K\,y[k-d] \). The delayed closed-loop system is:
\[ x[k+1] = A x[k] + B K C x[k-d]. \]
This is a linear time-delay system. A standard sufficient stability condition uses the augmented state \( \xi[k] = [x[k]^T, x[k-1]^T,\dots,x[k-d]^T]^T \). Define:
\[ \xi[k+1] = \underbrace{\begin{bmatrix} A & 0 & \cdots & 0 & BKC \\ I & 0 & \cdots & 0 & 0 \\ 0 & I & \cdots & 0 & 0 \\ \vdots & & \ddots & & \vdots \\ 0 & 0 & \cdots & I & 0 \end{bmatrix}}_{A_d} \xi[k]. \]
Proposition (Delay Stability): If all eigenvalues of \( A_d \) satisfy \( |\lambda_i(A_d)| < 1 \), then the delayed ROS-distributed loop is asymptotically stable.
Proof sketch: The augmented system is an ordinary LTI discrete-time system. Asymptotic stability of LTI systems is equivalent to the spectral radius \( \rho(A_d) < 1 \). Since \( \xi[k] \) contains all delayed states, its convergence to zero implies \( x[k]\rightarrow 0 \). ∎
ROS tooling (message timestamps, rate monitors, recorded bags) exists largely to measure and control \( d \) and its jitter. If jitter is bounded by \( d\in[d_{\min},d_{\max}] \), robust stability requires \( \rho(A_d(d)) < 1 \) for all admissible delays.
4. ROS vs ROS 2: Ecosystem and Design Shift
ROS (often “ROS 1”) prioritized rapid research development on a single LAN. ROS 2 redesigns the middleware to support real-time behavior, multiple robots, and unreliable networks. Mathematically, this is a shift from a best-effort channel model to explicit Quality-of-Service (QoS) constraints.
Let a topic edge \( e \) experience packet loss probability \( p_e \). If a publisher sends \( N \) messages in an interval, the number received is binomial:
\[ R_e \sim \mathrm{Binomial}(N,1-p_e), \quad \mathbb{E}[R_e]=N(1-p_e), \quad \mathrm{Var}(R_e)=Np_e(1-p_e). \]
Reliability QoS in ROS 2 can be interpreted as constraining \( p_e \) by retransmission or by requiring acknowledgement. Under a simple geometric retransmission model, expected number of sends per successful delivery is:
\[ \mathbb{E}[S_e] = \sum_{k=1}^{\infty} k(1-p_e)p_e^{k-1} = \frac{1}{1-p_e}. \]
Longer chains in the graph accumulate delay. If each edge adds random delay \( D_e \) with mean \( \mu_e \), then along a path \( \pi=(e_1,\dots,e_L) \):
\[ D_{\pi}=\sum_{\ell=1}^{L} D_{e_\ell}, \quad \mathbb{E}[D_{\pi}] = \sum_{\ell=1}^{L}\mu_{e_\ell}. \]
ROS 2 exposes QoS parameters (reliability, durability, history depth, deadlines) so designers can shape these path-level delay/reliability trade-offs.
flowchart TB
OS["Operating System"] --> DDS["DDS / transport layer"]
DDS --> RCL["Core client layer"]
RCL --> CL["Client libraries: C++ / Python / Java / Matlab"]
CL --> PKG["Packages and nodes"]
PKG --> TOOLS["Tools: build, launch, viz, record"]
5. Core Tooling and Package Culture
ROS defines conventions so that packages interoperate. A package is a unit of build, dependencies, and runtime content. Let \( \mathcal{P}=\{P_1,\dots,P_m\} \) be packages. Dependencies form a directed acyclic graph (DAG) \( G_P=(\mathcal{P},E_P) \) where \( (P_i\rightarrow P_j)\in E_P\) means “\(P_j\) depends on \(P_i\)”. A valid build order is any topological sort of \( G_P \).
Key tools you will use in later lessons:
- Build systems: catkin (ROS1), colcon/ament (ROS2).
- Launch: start many nodes with parameterization and remapping.
- Introspection: list nodes/topics, view message types, graph visualizers.
- Visualization: 3D viewer, plots, GUIs.
- Record/Replay: logging message streams for offline analysis.
The open package ecosystem implements a “reusable research” philosophy: if two modules satisfy the same interface (topics & types), they are interchangeable. Formally, two nodes \( v_a \) and \( v_b \) are interface-equivalent if:
\[ \mathcal{I}_a = \mathcal{I}_b,\quad \mathcal{O}_a = \mathcal{O}_b,\quad \forall e\in\mathcal{I}_a\cup\mathcal{O}_a:\ \mathrm{type}_a(e)=\mathrm{type}_b(e). \]
This equivalence underpins plug-and-play robotics.
6. Minimal Cross-Language Examples
We do not yet write full applications; these snippets only illustrate ROS’s modular pub/sub style. Later lessons will deepen each concept.
6.1 Python (ROS 2, rclpy) — Simple Publisher
# publisher_py.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import Float64
class SinePublisher(Node):
def __init__(self):
super().__init__('sine_publisher')
self.pub = self.create_publisher(Float64, 'sine', 10)
self.k = 0
self.timer = self.create_timer(0.01, self.step) # 100 Hz
def step(self):
msg = Float64()
msg.data = float(__import__('math').sin(0.1 * self.k))
self.pub.publish(msg)
self.k += 1
def main():
rclpy.init()
node = SinePublisher()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
6.2 C++ (ROS 2, rclcpp) — Simple Subscriber
// subscriber_cpp.cpp
#include <rclcpp/rclcpp.hpp>
#include <std_msgs/msg/float64.hpp>
class SineSubscriber : public rclcpp::Node {
public:
SineSubscriber() : Node("sine_subscriber") {
sub_ = this->create_subscription<std_msgs::msg::Float64>(
"sine", 10,
[this](const std_msgs::msg::Float64::SharedPtr msg) {
RCLCPP_INFO(this->get_logger(), "sine=%f", msg->data);
}
);
}
private:
rclcpp::Subscription<std_msgs::msg::Float64>::SharedPtr sub_;
};
int main(int argc, char **argv) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<SineSubscriber>());
rclcpp::shutdown();
return 0;
}
6.3 Java (ROS 1 rosjava or ROS 2 rcljava) — Publisher Skeleton
// PublisherJava.java (conceptual skeleton)
import org.ros.node.AbstractNodeMain;
import org.ros.node.ConnectedNode;
import org.ros.node.topic.Publisher;
import std_msgs.Float64;
public class PublisherJava extends AbstractNodeMain {
@Override
public void onStart(final ConnectedNode connectedNode) {
final Publisher<Float64> pub =
connectedNode.newPublisher("sine", Float64._TYPE);
connectedNode.executeCancellableLoop(new org.ros.concurrent.CancellableLoop() {
private int k = 0;
@Override
protected void loop() throws InterruptedException {
Float64 msg = pub.newMessage();
msg.setData(Math.sin(0.1 * k));
pub.publish(msg);
k++;
Thread.sleep(10); // 100 Hz
}
});
}
@Override
public org.ros.namespace.GraphName getDefaultNodeName() {
return org.ros.namespace.GraphName.of("sine_publisher_java");
}
}
6.4 MATLAB / Simulink (ROS Toolbox) — Subscribe and Plot
% matlab_subscribe_plot.m
% Requires ROS Toolbox
node = ros2node("/matlab_node");
sub = ros2subscriber(node, "/sine", "std_msgs/Float64");
data = zeros(1,200);
for k = 1:200
msg = receive(sub, 1.0); % wait up to 1 second
data(k) = msg.data;
end
plot(data); grid on;
title("Received sine samples");
xlabel("k"); ylabel("sine(k)");
In Simulink, the same idea is realized with a publisher block feeding a topic and a subscriber block reading it, aligning with your prior control-block-diagram intuition.
7. Problems and Solutions
Problem 1 (Graph Formalization): Let a ROS computation graph be \( G=(V,E,\mathcal{T}) \). Suppose each node has at most \( d_{\max} \) outgoing topics and \( |V|=n \). Show that \( |E| \le n d_{\max} \).
Solution: Each node contributes at most \( d_{\max} \) outgoing edges. Summing over all nodes: \( |E| = \sum_{v\in V} \mathrm{outdeg}(v) \le \sum_{v\in V} d_{\max} = n d_{\max} \). ∎
Problem 2 (Expected Delivery Under Loss): A publisher sends \( N=500 \) messages and the loss probability is \( p=0.04 \). Compute the expected number received and the standard deviation.
Solution: \( R\sim\mathrm{Binomial}(500,0.96) \). Hence \( \mathbb{E}[R]=500\cdot 0.96=480 \). Variance is \( 500\cdot 0.04\cdot 0.96=19.2 \), so standard deviation is \( \sqrt{19.2}\approx 4.381 \). ∎
Problem 3 (Delay Stability Check): Consider scalar dynamics \( x[k+1]=ax[k]+bu[k] \) with \( a=0.7,\ b=1 \), controller \( u[k]=Kx[k-d] \), \( K=-0.5 \). For \( d=1 \), write the augmented matrix \( A_d \) and determine if the system is stable.
Solution: Augmented state \( \xi[k]=[x[k],x[k-1]]^T \).
\[ A_d = \begin{bmatrix} a & bK \\ 1 & 0 \end{bmatrix} = \begin{bmatrix} 0.7 & -0.5 \\ 1 & 0 \end{bmatrix}. \]
The characteristic polynomial is \( \lambda^2-0.7\lambda+0.5=0 \). Its roots are \( \lambda=\frac{0.7\pm \sqrt{0.49-2}}{2} \), a complex conjugate pair with magnitude \( \sqrt{0.5}=0.7071 < 1 \). Therefore stable. ∎
Problem 4 (Build-Order Reasoning): Packages \( P_1,P_2,P_3,P_4 \) have dependencies \( P_2\rightarrow P_1 \), \( P_3\rightarrow P_1 \), \( P_4\rightarrow P_2 \). Give one valid build order.
Solution: A topological sort must place \( P_1 \) before \( P_2,P_3 \), and \( P_2 \) before \( P_4 \). One valid order: \( (P_1,P_2,P_4,P_3) \). ∎
8. Summary
ROS embodies a computation-graph philosophy: robots are built from small, replaceable nodes communicating via typed message channels. We formalized the graph structure, connected ROS timing to delay-affected linear control, and interpreted ROS 2 QoS as explicit constraints on delay and reliability in distributed systems. The ecosystem’s package culture and tooling enable reusable, debuggable robot software. Next lesson: the concrete primitives—nodes, topics, services, and actions.
9. References
- Quigley, M., Conley, K., Gerkey, B., Faust, J., Foote, T., Leibs, J., Wheeler, R., & Ng, A.Y. (2009). ROS: An open-source Robot Operating System. ICRA Workshop on Open Source Software.
- Gerkey, B.P., Vaughan, R.T., & Howard, A. (2003). The Player/Stage Project: Tools for Multi-Robot and Distributed Sensor Systems. Proceedings of the 11th International Conference on Advanced Robotics.
- Koubaa, A. (2017). Robot Operating System (ROS): The Complete Reference (Volume 1) — foundational middleware principles. Springer Tracts in Advanced Robotics.
- Maruyama, Y., Kato, S., & Azumi, T. (2016). Exploring the performance of ROS2. International Conference on Embedded and Real-Time Computing Systems and Applications.
- Pardo-Castellote, G. (2003). OMG Data-Distribution Service: Architectural Overview. IEEE International Conference on Distributed Computing Systems Workshops.
- Sha, L., Abdelzaher, T., Årzén, K-E., Cervin, A., Baker, T., Burns, A., Buttazzo, G., Caccamo, M., Lehoczky, J., & Mok, A. (2004). Real-time scheduling theory: A historical perspective. Real-Time Systems, 28(2–3), 101–155.
- Khatib, O., & Brock, O. (2008). Middleware for robotics: A survey and perspectives. Robotics Research, 623–641.