Chapter 11: Robot Software Architecture
Lesson 5: Software Reliability in Physical Systems
This lesson develops a rigorous view of reliability for robot software that acts in the physical world. We formalize probabilistic reliability metrics, connect them to fault models for cyber–physical robots, derive key formulas for redundancy and monitoring, and show how testing, runtime assurance, and formal methods combine into dependable robot systems. Code examples illustrate watchdogs, residual-based fault detection, and safety assertions in Python, C++, Java, and Matlab/Simulink.
1. Conceptual Overview: What “Reliability” Means for Robots
In physical robots, software errors do not just crash a program; they can create unsafe motions, damage hardware, or endanger humans. We use the classical dependability triad: reliability, availability, and safety.
Let \( T \) be the random time-to-failure of a software-controlled robot operating in a specified environment and task. The reliability function is
\[ R(t) = \Pr(T > t), \quad t \ge 0. \]
The failure distribution is \( F(t)=1-R(t) \), and the hazard (failure rate) is
\[ \lambda(t) = \lim_{\Delta t \to 0^+} \frac{\Pr(t \le T < t+\Delta t \mid T \ge t)}{\Delta t} = \frac{f(t)}{R(t)}, \]
where \( f(t)=F'(t) \) is the density if it exists.
Theorem 1 (Hazard–Reliability Relation). If \( \lambda(t) \) is integrable, then
\[ R(t) = \exp\!\left(-\int_{0}^{t}\lambda(s)\,ds\right). \]
Proof. By definition, \( \lambda(t)= -\frac{d}{dt}\log R(t) \) because \( f(t) = -R'(t) \). Thus \( \frac{R'(t)}{R(t)} = -\lambda(t) \). Integrating from 0 to \( t \):
\[ \log R(t) - \log R(0) = -\int_{0}^{t}\lambda(s)\,ds. \]
Since \( R(0)=\Pr(T>0)=1 \), \( \log R(0)=0 \), giving the result. ∎
Availability adds repair/restart. If the mean time between failures is \( \text{MTBF} \) and the mean time to repair/recover is \( \text{MTTR} \), then steady-state availability is
\[ A = \frac{\text{MTBF}}{\text{MTBF} + \text{MTTR}}. \]
Robots typically require high reliability (rare faults) and high availability (fast recovery), because their tasks are continuous and safety-critical.
2. Fault Models and Physical Consequences
A fault is the cause of an error; an error is a corrupt internal state; a failure is an externally visible deviation from the specification. In robots, failures can be:
- Timing failures: missed deadlines, jitter, race conditions.
- Value failures: wrong control outputs, corrupted sensor data.
- Communication failures: message loss, duplication, delay.
- Mode failures: wrong state-machine transitions (e.g., “safe-stop” not entered).
Because students know linear control, we express fault detection using a familiar residual framework. Consider a discrete-time plant (introduced in Linear Control):
\[ x_{k+1} = A x_k + B u_k + w_k,\quad y_k = C x_k + v_k, \]
where \( w_k \) and \( v_k \) are process/measurement noise. Let the software run an observer (state estimator) producing \( \hat{x}_k \) and predicted output \( \hat{y}_k = C\hat{x}_k \). Define the residual
\[ r_k = y_k - \hat{y}_k. \]
Under nominal conditions, if \( w_k, v_k \) are zero-mean Gaussian and the observer is stable, residuals satisfy \( r_k \sim \mathcal{N}(0, S) \) with covariance \( S \). A simple software fault detector uses a quadratic test:
\[ g_k = r_k^\top S^{-1} r_k \;\;\;\text{raise fault if}\;\; g_k > \gamma. \]
Theorem 2 (False-Alarm Probability). If \( r_k \sim \mathcal{N}(0,S) \), then \( g_k \sim \chi^2_m \) where \( m=\dim(y) \). Hence for threshold \( \gamma \),
\[ \Pr(\text{false alarm}) = \Pr(g_k > \gamma) = 1 - F_{\chi^2_m}(\gamma). \]
Proof. Let \( z = S^{-1/2} r_k \). Then \( z \sim \mathcal{N}(0,I) \) and \( g_k = z^\top z = \sum_{i=1}^{m} z_i^2 \), the sum of squares of i.i.d. standard normals, which is \( \chi^2_m \). ∎
This gives a mathematically grounded way to tune monitoring thresholds in robot software.
3. Reliability-Enhancing Architectures
Reliability is not just a property of code; it is an architectural outcome. Common patterns for physical robots include:
- Redundancy: duplicated sensing/computation with voting.
- Watchdogs / heartbeats: detect stalled components.
- Recovery blocks: primary algorithm + fallback.
- Simple safe modes: minimal controller that enforces safe stop.
Suppose a robot depends on \( n \) independent software components in series (all must work). If component \( i \) has reliability \( R_i(t) \), then system reliability is
\[ R_{\text{series}}(t) = \prod_{i=1}^{n} R_i(t). \]
For parallel redundancy (system works if at least one of \( n \) replicas works), the reliability is
\[ R_{\text{parallel}}(t) = 1 - \prod_{i=1}^{n}\bigl(1 - R_i(t)\bigr). \]
k-out-of-n voting. If replicas are i.i.d. with reliability \( R(t) \) and the system needs at least \( k \) correct outputs, then
\[ R_{k/n}(t) = \sum_{j=k}^{n} \binom{n}{j} R(t)^j \bigl(1-R(t)\bigr)^{n-j}. \]
Proof. The number of functioning replicas is \( J \sim \text{Binomial}(n, R(t)) \). Thus \( R_{k/n}(t)=\Pr(J \ge k)=\sum_{j=k}^n \Pr(J=j) \) with binomial probabilities. ∎
flowchart TD
S["Robot software components running"] --> HB["Heartbeat sent periodically"]
HB --> M["Monitor checks last heartbeat time"]
M --> D{"Heartbeat late?"}
D -->|no| OK["Continue nominal control"]
D -->|yes| W["Trigger watchdog"]
W --> R["Restart component or switch to fallback"]
R --> SAFE{"Fallback stable?"}
SAFE -->|yes| LIM["Run safe-limited mode"]
SAFE -->|no| STOP["Emergency stop"]
The flow above expresses a minimal runtime reliability strategy: detect, isolate, recover, and degrade to safety.
4. Formal Verification and Runtime Assurance (Lightweight)
Before deployment, we want mathematical confidence that software components satisfy their contracts. A contract is an assume–guarantee pair:
\[ \mathcal{C}_i:\;\; (A_i \Rightarrow G_i), \]
where \( A_i \) is an assumption on inputs/environment and \( G_i \) is a guaranteed output behavior.
Lemma 1 (Assume–Guarantee Composition). Suppose two components \( i=1,2 \) are composed in series, and \( G_1 \Rightarrow A_2 \). If each satisfies its contract, then the composition satisfies \( A_1 \Rightarrow G_2 \).
Proof. From contract satisfaction: \( A_1 \Rightarrow G_1 \) and \( A_2 \Rightarrow G_2 \). If \( A_1 \) holds, then \( G_1 \) holds. Since \( G_1 \Rightarrow A_2 \), we get \( A_2 \). Then \( G_2 \) follows. Thus \( A_1 \Rightarrow G_2 \). ∎
At runtime, some guarantees are enforced by invariants and assertions. For a control loop with discrete time step \( k \), a simple invariant is:
\[ \|u_k\| \le u_{\max} \quad \text{and} \quad \|x_k\| \le x_{\max}. \]
Violations trigger safe-mode transitions handled by the architecture in Section 3.
5. Verification and Testing Pipeline
Because formal proofs never cover everything (unknown environments, hardware faults, integration bugs), robots rely on layered testing:
- Unit tests: pure functions, edge cases, numerical stability.
- Integration tests: message passing, timing, resource contention.
- System tests: end-to-end tasks in controlled environments.
- Hardware-in-the-loop (HIL): real sensors/actuators with simulated rest.
flowchart TD
REQ["Requirements"] --> DES["Design + contracts"]
DES --> UT["Unit tests"]
UT --> IT["Integration tests"]
IT --> ST["System tests"]
ST --> HIL["HIL / field trials"]
HIL --> DEP["Deployment with monitors"]
DEP --> LOG["Logs + diagnostics feed back"]
LOG --> DES
Notice the feedback loop: operational logs (Lesson 4) are not only for debugging but for updating the reliability model and strengthening tests.
6. Implementations: Monitoring, Residuals, and Assertions
The snippets below are minimal and middleware-agnostic. In the next chapter (ROS/ROS2), the same ideas map to nodes, topics, and lifecycle management.
6.1 Python: Heartbeat Watchdog + Residual Detector
import time
import threading
import numpy as np
class HeartbeatMonitor:
def __init__(self, timeout_s=0.5):
self.timeout_s = timeout_s
self.last_hb = time.time()
self.lock = threading.Lock()
self.alive = True
def heartbeat(self):
with self.lock:
self.last_hb = time.time()
def watch(self):
while self.alive:
time.sleep(self.timeout_s / 4)
with self.lock:
dt = time.time() - self.last_hb
if dt > self.timeout_s:
print("[WATCHDOG] heartbeat timeout; switching to safe mode.")
# Here you would restart or trigger safe-limited controller
self.alive = False
# Residual-based fault detector for y_k = C x_hat_k + noise
class ResidualDetector:
def __init__(self, S, gamma):
self.Sinv = np.linalg.inv(S)
self.gamma = gamma
def check(self, y, y_hat):
r = y - y_hat
g = float(r.T @ self.Sinv @ r)
if g > self.gamma:
return True, g
return False, g
# Example usage
monitor = HeartbeatMonitor(timeout_s=0.3)
threading.Thread(target=monitor.watch, daemon=True).start()
S = np.eye(2)
gamma = 9.21 # ~95% quantile of chi^2_2
detector = ResidualDetector(S=S, gamma=gamma)
for k in range(10):
# Simulated heartbeat from component
if k < 6:
monitor.heartbeat()
# Simulated residual check
y = np.random.randn(2)
y_hat = np.zeros(2)
fault, score = detector.check(y, y_hat)
if fault:
print(f"[FAULT] residual score={score:.2f}")
time.sleep(0.1)
6.2 C++: Watchdog Thread + Safety Assertion
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
class Watchdog {
public:
explicit Watchdog(double timeout_s)
: timeout(std::chrono::duration<double>(timeout_s)),
last_hb(std::chrono::steady_clock::now()),
alive(true) {}
void heartbeat() {
last_hb.store(std::chrono::steady_clock::now(),
std::memory_order_relaxed);
}
void start() {
wd_thread = std::thread([&](){
while (alive.load()) {
std::this_thread::sleep_for(timeout / 4);
auto dt = std::chrono::steady_clock::now() -
last_hb.load();
if (dt > timeout) {
std::cerr << "[WATCHDOG] timeout; enter safe-stop.\n";
alive.store(false);
}
}
});
}
void stop() {
alive.store(false);
if (wd_thread.joinable()) wd_thread.join();
}
private:
std::chrono::duration<double> timeout;
std::atomic<std::chrono::steady_clock::time_point> last_hb;
std::atomic<bool> alive;
std::thread wd_thread;
};
// Safety assertion for control saturation
inline void assert_control_limit(double u, double umax) {
if (std::abs(u) > umax) {
throw std::runtime_error("Control limit violated");
}
}
6.3 Java: Scheduled Heartbeat Checker
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
public class HeartbeatMonitor {
private final long timeoutMs;
private final AtomicLong lastHbMs = new AtomicLong(System.currentTimeMillis());
private final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1);
public HeartbeatMonitor(long timeoutMs) {
this.timeoutMs = timeoutMs;
}
public void heartbeat() {
lastHbMs.set(System.currentTimeMillis());
}
public void start() {
exec.scheduleAtFixedRate(() -> {
long dt = System.currentTimeMillis() - lastHbMs.get();
if (dt > timeoutMs) {
System.out.println("[WATCHDOG] heartbeat late; safe mode.");
exec.shutdown();
}
}, 0, timeoutMs / 4, TimeUnit.MILLISECONDS);
}
}
6.4 Matlab/Simulink: Residual Test + Assertion Block
In Matlab, the residual test mirrors Section 2. In Simulink, you would connect: Estimator block → Residual compute → Chi-square test → Assertion or Stateflow safe-mode.
% Residual-based detector for discrete signals
S = eye(2);
Sinv = inv(S);
gamma = 9.21; % chi^2_2 95% threshold
for k = 1:50
y = randn(2,1);
y_hat = zeros(2,1);
r = y - y_hat;
g = r' * Sinv * r;
if g > gamma
fprintf('[FAULT] k=%d, score=%.2f\n', k, g);
% In Simulink, trigger safe-mode via Stateflow transition
end
end
7. Problems and Solutions
Problem 1 (Series vs. Parallel Reliability): Two independent software modules have exponential reliability: \( R_1(t)=e^{-\lambda_1 t} \) and \( R_2(t)=e^{-\lambda_2 t} \). (a) Compute the reliability if they are required in series. (b) Compute the reliability if either one can provide the output (parallel redundancy).
Solution:
\[ \text{(a)}\;\; R_{\text{series}}(t)=R_1(t)R_2(t)=e^{-(\lambda_1+\lambda_2)t}. \]
\[ \text{(b)}\;\; R_{\text{parallel}}(t)=1-(1-R_1)(1-R_2) = 1-\bigl(1-e^{-\lambda_1 t}\bigr)\bigl(1-e^{-\lambda_2 t}\bigr). \]
Problem 2 (Choosing a Residual Threshold): A robot uses a 3D residual \( r_k\in\mathbb{R}^3 \), nominally Gaussian with covariance \( S=I_3 \). Choose \( \gamma \) so that false alarms occur with probability at most 1%.
Solution: Under nominal conditions, \( g_k=r_k^\top r_k \sim \chi^2_3 \). We need \( \Pr(g_k>\gamma)=0.01 \), so \( \gamma \) is the 99th percentile:
\[ \gamma = F_{\chi^2_3}^{-1}(0.99). \]
Numerically this is about 11.34 (from standard chi-square tables).
Problem 3 (k-out-of-n Proof Check): For i.i.d. replicas with reliability \( R(t) \), prove that 2-out-of-3 voting yields \( R_{2/3}(t)=3R(t)^2(1-R(t))+R(t)^3 \).
Solution: Apply the binomial formula with \( k=2, n=3 \):
\[ R_{2/3}(t)=\binom{3}{2}R^2(1-R)+\binom{3}{3}R^3 = 3R^2(1-R)+R^3. \]
Problem 4 (Availability Calculation): A mobile robot experiences on average one software failure every 10 hours, and automatic recovery takes 30 seconds. Compute steady-state availability.
Solution: Here \( \text{MTBF}=10 \) hours and \( \text{MTTR}=30 \) seconds \( = 30/3600 = 1/120 \) hours.
\[ A=\frac{10}{10+1/120}=\frac{10}{10.00833\ldots}\approx 0.99917. \]
So despite moderate reliability, fast recovery yields very high availability.
Problem 5 (Assume–Guarantee Reasoning): Component 1 contract: \( A_1: \|u\| \le 1 \Rightarrow G_1: \|y\| \le 2 \). Component 2 contract: \( A_2: \|y\| \le 2 \Rightarrow G_2: \|z\| \le 5 \). Show the composed contract from \( A_1 \) to \( G_2 \).
Solution: Since \( G_1 \Rightarrow A_2 \), Lemma 1 applies. Therefore:
\[ \|u\| \le 1 \Rightarrow \|z\| \le 5. \]
8. Summary
We defined reliability and availability for robot software, proved the hazard–reliability relation, and connected failure monitoring to residual statistics familiar from linear control. Architectural patterns such as redundancy, watchdogs, and safe fallback modes were quantified using series/parallel and k-out-of-n reliability formulas. Finally, we tied formal contracts and layered testing into a coherent reliability pipeline, illustrated by practical multi-language implementations.
9. References
- Laprie, J.-C. (1992). Dependability: Basic concepts and terminology. Springer Dependable Computing and Fault-Tolerant Systems, Vol. 5.
- Avizienis, A., Laprie, J.-C., Randell, B., & Landwehr, C. (2004). Basic concepts and taxonomy of dependable and secure computing. IEEE Transactions on Dependable and Secure Computing, 1(1), 11–33.
- Powell, D. (1994). Delta-4: A generic architecture for dependable distributed computing. Springer.
- Littlewood, B., & Strigini, L. (2000). Software reliability and dependability: A roadmap. Future of Software Engineering (ICSE), 175–188.
- Garlan, D., & Shaw, M. (1993). An introduction to software architecture. Advances in Software Engineering and Knowledge Engineering, 1, 1–39.
- Alur, R., Henzinger, T.A., & Sontag, E.D. (1996). Hybrid systems. Computers & Mathematics with Applications, 32(1), 1–3.
- Sha, L., Rajkumar, R., & Lehoczky, J.P. (1990). Priority inheritance protocols: An approach to real-time synchronization. IEEE Transactions on Computers, 39(9), 1175–1185.
- Benveniste, A., Caillaud, B., Carloni, L.P., et al. (2018). Contracts for system design. Foundations and Trends in Electronic Design Automation, 12(2–3), 124–400.