Chapter 17: Applications and Case Studies
Lesson 6: Lessons Learned from Real Deployments
This lesson distills mathematical and systems-level lessons from real robot deployments. We focus on reliability, availability, latency and control degradation, data-driven model updating, and fault-handling patterns that repeatedly appear when robots move from laboratory prototypes to field systems. Although the examples refer to different application domains, the emphasis is on general principles and their quantitative formulation.
1. Lab vs. Field: Modeling the Gap
In the laboratory, we typically treat the robot and environment as a well-modeled plant under a designed controller. In the field, additional phenomena appear:
- Component failures (motors, sensors, connectors).
- Environmental disturbances (dust, rain, temperature).
- Computation and communication delays, jitter, and drops.
- Human–robot interaction uncertainties (misuse, unexpected behavior).
A useful abstraction is to treat the field deployment as a stochastic process on top of the nominal deterministic closed-loop dynamics. Let \( x_k \) be the state of a (discrete-time) controlled robot and \( u_k \) the control input. In ideal lab conditions:
\[ x_{k+1} = f(x_k, u_k) \]
In the field, we augment this with disturbance and fault variables \( w_k \) and \( \delta_k \):
\[ x_{k+1} = f(x_k, u_k) + w_k + \delta_k, \]
where:
- \( w_k \) captures zero-mean disturbances (e.g., bumps, wind, minor sensor noise) that are modeled in design.
- \( \delta_k \) represents unmodeled deployment effects (e.g., intermittent sensor dropout, rare actuator saturation) that are not captured in nominal simulations.
A key lesson from real deployments is that \( \delta_k \) can dominate the behavior over long time horizons, even if it is rare per time step. This motivates probabilistic reliability modeling.
2. Reliability and Mission Success Probability
Reliability quantifies the probability that a robot performs without catastrophic failure over a given time interval. Let \( T \) be the random time until first failure, and define:
\[ R(t) := \mathbb{P}(T > t) \]
A common model for rare, independent failures is the exponential lifetime model. Assume catastrophic failure events follow a Poisson process with rate \( \lambda > 0 \) (failures per hour). Then the number of failures in time \( t \) is Poisson with mean \( \lambda t \). The probability of zero failures up to time \( t \) is:
\[ \mathbb{P}(T > t) = \mathbb{P}(\text{0 failures in }[0,t]) = e^{-\lambda t}, \]
so the reliability function is:
\[ R(t) = e^{-\lambda t}. \]
The mean time between failures (MTBF) (or mean time to failure) is the expected value of \( T \):
\[ \text{MTBF} = \mathbb{E}[T] = \int_0^{\infty} t \lambda e^{-\lambda t} \, dt = \frac{1}{\lambda}. \]
From deployment data, if a fleet accumulates total uptime \( H_{\text{tot}} \) hours and experiences \( N_{\text{fail}} \) independent failures, a natural estimator is
\[ \hat{\lambda} = \frac{N_{\text{fail}}}{H_{\text{tot}}}, \qquad \widehat{\text{MTBF}} = \frac{1}{\hat{\lambda}}. \]
For a mission of duration \( T_m \), the estimated mission success probability (no catastrophic failure) is
\[ \hat{R}(T_m) = \exp(-\hat{\lambda} T_m). \]
One lesson from long deployments is that even very small \( \lambda \) can produce non-negligible failure probability over long \( T_m \). Mathematically, for fixed \( \lambda \), \( R(t) \to 0 \) as \( t \to \infty \), so reliable robots must have both small \( \lambda \) and bounded mission durations or maintenance cycles.
3. Availability and Service-Level Targets
Reliability describes time to first failure; deployments also care about availability, the fraction of time the robot is operational in the long run. Let \( T_{\text{up}} \) be the random duration of an up-period and \( T_{\text{down}} \) the random duration of a repair or downtime period. The steady-state availability is:
\[ A = \frac{\mathbb{E}[T_{\text{up}}]} {\mathbb{E}[T_{\text{up}}] + \mathbb{E}[T_{\text{down}}]}. \]
In the exponential model with failure rate \( \lambda \) and repair rate \( \mu \), we have \( \mathbb{E}[T_{\text{up}}] = 1/\lambda \) and \( \mathbb{E}[T_{\text{down}}] = 1/\mu \), so:
\[ A = \frac{\mu}{\lambda + \mu}. \]
It is common to talk about "n-nines" availability. For example, \( A = 0.999 \) corresponds to roughly 8.76 hours of downtime per year, while \( A = 0.9999 \) corresponds to about 52.6 minutes. Practical deployments reveal that reaching even \( 0.99 \) availability can be challenging for mobile robots due to both hardware and software failures.
If we measure mean time to repair (MTTR) from logs and estimate \( \hat{\lambda} \) as before, an empirical availability estimate is:
\[ \hat{A} = \frac{\widehat{\text{MTBF}}} {\widehat{\text{MTBF}} + \widehat{\text{MTTR}}}. \]
A key design lesson is that improving availability can be attacked from both sides: decreasing failure rate (hardware robustness, fault-tolerant control) and decreasing MTTR (diagnostics, modularity, fast swap).
4. Latency, Jitter, and Control Stability
On real robots, sensing, computation, and actuation introduce delays. Even if a controller is stable in simulation (ideal sampling, no delay), deployment can destabilize it. Consider a simple discrete-time integrator model for a 1D robot position:
\[ x_{k+1} = x_k + u_k. \]
A proportional controller in the lab uses \( u_k = -K x_k \), giving
\[ x_{k+1} = (1 - K) x_k. \]
Stability requires the eigenvalue magnitude be < 1, i.e. \( \lvert 1 - K \rvert < 1 \), which yields:
\[ -1 < 1 - K < 1 \quad \Longrightarrow \quad 0 < K < 2. \]
In deployment, suppose there is one-step actuation delay: the control computed at time \( k \) is applied at \( k+1 \). A simple model is:
\[ x_{k+1} = x_k + u_{k-1}, \qquad u_k = -K x_k. \]
Eliminating \( u_{k-1} \) gives the second-order recursion:
\[ x_{k+1} = x_k - K x_{k-1}. \]
Writing the state as \( z_k = \begin{bmatrix} x_k \\ x_{k-1} \end{bmatrix} \), we obtain:
\[ z_{k+1} = \underbrace{\begin{bmatrix} 1 & -K \\[4pt] 1 & 0 \end{bmatrix}}_{\mathbf{A}(K)} z_k. \]
The characteristic polynomial of \( \mathbf{A}(K) \) is
\[ p(z) = z^2 - z + K. \]
For a real second-order polynomial \( z^2 + a_1 z + a_0 \), Jury stability conditions for all roots inside the unit disk are:
\[ 1 + a_1 + a_0 > 0,\quad 1 - a_1 + a_0 > 0,\quad 1 - a_0 > 0. \]
In our case \( a_1 = -1 \), \( a_0 = K \), so the conditions become:
\[ 1 - 1 + K = K > 0,\qquad 1 - (-1) + K = 2 + K > 0,\qquad 1 - K > 0. \]
Combining these gives \( 0 < K < 1 \). Compared to the lab condition \( 0 < K < 2 \), the stable range of gains is halved. This simple derivation captures a ubiquitous deployment lesson: unmodeled delay reduces stability margins, so gains tuned aggressively in simulation can cause oscillations or instability on hardware.
5. Data Logging and Model Refinement
Another recurring lesson from real deployments is that the initial model of the robot and environment is incomplete. However, log data can be used to refine dynamical models. Consider a simple linear discrete-time model for a scalar output:
\[ y_k = a y_{k-1} + b u_{k-1} + e_k, \]
where \( e_k \) is zero-mean measurement noise. Collecting \( N \) samples, define the regressor matrix and parameter vector:
\[ \boldsymbol{\theta} = \begin{bmatrix} a \\[4pt] b \end{bmatrix},\quad \mathbf{\Phi} = \begin{bmatrix} y_0 & u_0 \\ y_1 & u_1 \\ \vdots & \vdots \\ y_{N-1} & u_{N-1} \end{bmatrix},\quad \mathbf{y} = \begin{bmatrix} y_1 \\ y_2 \\ \vdots \\ y_N \end{bmatrix}. \]
The least-squares estimate minimizes \( \|\mathbf{y} - \mathbf{\Phi} \boldsymbol{\theta}\|_2^2 \), giving
\[ \hat{\boldsymbol{\theta}} = (\mathbf{\Phi}'\mathbf{\Phi})^{-1} \mathbf{\Phi}' \mathbf{y}. \]
Iterating this process (deployment → logging → identification → controller retuning) is one of the most powerful ways to harden robots for real environments.
flowchart TD
A["Design in lab (models, controllers)"] --> B["Simulate and bench test"]
B --> C["Limited field pilot"]
C --> D["Collect logs (states, commands, faults)"]
D --> E["Analyze data (identify params, estimate lambda)"]
E --> F["Update models and controllers"]
F --> G["Next release / deployment"]
G --> C
6. Fault Handling and Safe States
Real deployments teach that failures are inevitable; what matters is how safely and transparently the system reacts. A common pattern is to embed a fault-detection and safe-state mechanism around the core controller:
- Monitor invariants (bounds on speeds, currents, temperatures).
- Detect violations or communication loss.
- Transition the robot into a predefined safe state.
- Log context for post-mortem analysis.
The following schematic shows a typical decision flow:
flowchart TD
S["Normal operation"] --> M["Monitor sensors and heartbeats"]
M -->|ok| S
M -->|limit violated or timeout| F["Fault detected"]
F --> SS["Enter safe state (e.g., stop motors, brake)"]
SS --> L["Record logs and diagnostics"]
L --> R["Recover or request human intervention"]
Mathematically, a safe state often corresponds to restricting the state space to an invariant set \( \mathcal{S} \subset \mathbb{R}^n \) where collisions or hazards are impossible. For example, for a mobile base with speed \( v \) and maximum braking deceleration \( a_{\max} > 0 \), the stopping distance is
\[ d_{\text{stop}} = \frac{v^2}{2 a_{\max}}. \]
Requiring a minimum distance \( d_{\text{obs}} \) to obstacles, a simple safety constraint is
\[ d_{\text{obs}} \ge d_{\text{stop}} + d_{\text{margin}}, \]
where \( d_{\text{margin}} \) accounts for reaction time and sensing uncertainty. Violations of this inequality should trigger safe-state behaviors in the deployment software.
7. Implementation Examples: Estimating Failure Rate and Availability
Suppose deployment logs provide total fleet uptime \( H_{\text{tot}} \), number of failures \( N_{\text{fail}} \), and average repair time \( \widehat{\text{MTTR}} \). We compute
\[ \hat{\lambda} = \frac{N_{\text{fail}}}{H_{\text{tot}}},\qquad \widehat{\text{MTBF}} = \frac{1}{\hat{\lambda}},\qquad \hat{A} = \frac{\widehat{\text{MTBF}}} {\widehat{\text{MTBF}} + \widehat{\text{MTTR}}}. \]
The following snippets compute these quantities and the mission success probability for a mission of duration \( T_m \).
7.1 Python Example
import math
def deployment_metrics(total_uptime_hours, n_failures, mttr_hours, mission_hours):
if total_uptime_hours <= 0:
raise ValueError("total_uptime_hours must be positive")
if n_failures <= 0:
# assume at least one virtual failure to get a conservative estimate
n_failures = 1
lam_hat = n_failures / total_uptime_hours # failure rate lambda
mtbf_hat = 1.0 / lam_hat
availability_hat = mtbf_hat / (mtbf_hat + mttr_hours)
mission_success = math.exp(-lam_hat * mission_hours)
return {
"lambda_hat": lam_hat,
"MTBF_hat": mtbf_hat,
"availability_hat": availability_hat,
"mission_success_prob": mission_success,
}
if __name__ == "__main__":
metrics = deployment_metrics(
total_uptime_hours=10000.0,
n_failures=5,
mttr_hours=2.0,
mission_hours=8.0
)
for k, v in metrics.items():
print(f"{k}: {v:.6f}")
7.2 C++ Example
#include <iostream>
#include <cmath>
struct Metrics {
double lambda_hat;
double mtbf_hat;
double availability_hat;
double mission_success_prob;
};
Metrics deploymentMetrics(double totalUptimeHours,
int nFailures,
double mttrHours,
double missionHours) {
if (totalUptimeHours <= 0.0) {
throw std::runtime_error("totalUptimeHours must be positive");
}
if (nFailures <= 0) {
nFailures = 1; // conservative
}
double lambdaHat = static_cast<double>(nFailures) / totalUptimeHours;
double mtbfHat = 1.0 / lambdaHat;
double availabilityHat = mtbfHat / (mtbfHat + mttrHours);
double missionSuccess = std::exp(-lambdaHat * missionHours);
return {lambdaHat, mtbfHat, availabilityHat, missionSuccess};
}
int main() {
Metrics m = deploymentMetrics(10000.0, 5, 2.0, 8.0);
std::cout << "lambda_hat: " << m.lambda_hat << "\n";
std::cout << "MTBF_hat: " << m.mtbf_hat << "\n";
std::cout << "availability_hat: " << m.availability_hat << "\n";
std::cout << "mission_success_prob: " << m.mission_success_prob << "\n";
return 0;
}
7.3 Java Example
public class DeploymentMetrics {
public static class Metrics {
public double lambdaHat;
public double mtbfHat;
public double availabilityHat;
public double missionSuccessProb;
}
public static Metrics compute(double totalUptimeHours,
int nFailures,
double mttrHours,
double missionHours) {
if (totalUptimeHours <= 0.0) {
throw new IllegalArgumentException("totalUptimeHours must be positive");
}
if (nFailures <= 0) {
nFailures = 1; // conservative
}
double lambdaHat = nFailures / totalUptimeHours;
double mtbfHat = 1.0 / lambdaHat;
double availabilityHat = mtbfHat / (mtbfHat + mttrHours);
double missionSuccess = Math.exp(-lambdaHat * missionHours);
Metrics m = new Metrics();
m.lambdaHat = lambdaHat;
m.mtbfHat = mtbfHat;
m.availabilityHat = availabilityHat;
m.missionSuccessProb = missionSuccess;
return m;
}
public static void main(String[] args) {
Metrics m = compute(10000.0, 5, 2.0, 8.0);
System.out.println("lambda_hat: " + m.lambdaHat);
System.out.println("MTBF_hat: " + m.mtbfHat);
System.out.println("availability_hat: " + m.availabilityHat);
System.out.println("mission_success_prob: " + m.missionSuccessProb);
}
}
7.4 MATLAB / Simulink Example
function metrics = deployment_metrics(totalUptimeHours, nFailures, mttrHours, missionHours)
if totalUptimeHours <= 0
error('totalUptimeHours must be positive');
end
if nFailures <= 0
nFailures = 1; % conservative
end
lambdaHat = nFailures / totalUptimeHours;
mtbfHat = 1 / lambdaHat;
availabilityHat = mtbfHat / (mtbfHat + mttrHours);
missionSuccess = exp(-lambdaHat * missionHours);
metrics.lambda_hat = lambdaHat;
metrics.MTBF_hat = mtbfHat;
metrics.availability_hat = availabilityHat;
metrics.mission_success_prob = missionSuccess;
end
% In Simulink, you could:
% - Add Constant blocks for totalUptimeHours, nFailures, mttrHours, missionHours
% - Use a MATLAB Function block implementing deployment_metrics
% - Route outputs to Display or Scope blocks for online monitoring
In more advanced robotics courses, these computations can be embedded directly into ROS/ROS2 nodes for online fleet-level health monitoring. Here, they serve to connect abstract reliability formulas with concrete deployment code.
8. Problems and Solutions
Problem 1 (Mission Reliability): A mobile robot has an estimated failure rate \( \hat{\lambda} = 2 \times 10^{-4} \) failures per hour. (a) What is the estimated probability the robot completes a continuous 10-hour mission without catastrophic failure? (b) If 5 identical robots run the same 10-hour mission in parallel and their failures are independent, what is the probability that all 5 succeed?
Solution:
(a) Using the exponential model,
\[ \hat{R}(10) = \exp(-\hat{\lambda} \cdot 10) = \exp\big(-2 \times 10^{-4} \cdot 10\big) = \exp(-2 \times 10^{-3}). \]
Since \( 2 \times 10^{-3} \) is small, we can compute numerically: \( \exp(-0.002) \approx 0.9980 \).
(b) For 5 independent robots with identical reliability, the probability all succeed is
\[ \hat{R}_{\text{all}}(10) = \big[\hat{R}(10)\big]^5 \approx 0.9980^5 \approx 0.990. \]
Thus we still have about a 1% chance that at least one robot fails over the 10-hour mission.
Problem 2 (Availability with Repair): A manipulation robot in a warehouse has an estimated MTBF of 500 hours and mean time to repair (MTTR) of 4 hours. Compute its steady availability. How many hours of downtime per year would you expect on average (assuming 24/7 operation)?
Solution:
Availability is
\[ A = \frac{\text{MTBF}}{\text{MTBF} + \text{MTTR}} = \frac{500}{500 + 4} = \frac{500}{504} \approx 0.9921. \]
Over a year, total hours are \( 24 \times 365 = 8760 \). Expected downtime is
\[ (1 - A) \times 8760 \approx (1 - 0.9921) \times 8760 \approx 0.0079 \times 8760 \approx 69.2 \text{ hours per year}. \]
So we expect roughly three days of downtime per year on average.
Problem 3 (Effect of Delay on Gain Margin): Consider the discrete-time integrator with proportional control from Section 4. (a) Show that without delay the stability condition is \( 0 < K < 2 \). (b) With one-step delay, verify that the stability condition from Jury's test is \( 0 < K < 1 \). Interpret this result.
Solution:
(a) Without delay we have \( x_{k+1} = (1-K)x_k \). The eigenvalue is \( \lambda = 1 - K \). Stability requires \( \lvert \lambda \rvert < 1 \), i.e.
\[ \lvert 1 - K \rvert < 1 \quad \Longleftrightarrow \quad -1 < 1 - K < 1. \]
Rearranging inequalities: \( -1 < 1 - K \) gives \( K < 2 \), and \( 1 - K < 1 \) gives \( K > 0 \). Thus \( 0 < K < 2 \).
(b) With one-step delay, the closed-loop characteristic polynomial is \( p(z) = z^2 - z + K \). Using Jury's conditions for \( z^2 + a_1 z + a_0 \) with \( a_1 = -1 \), \( a_0 = K \):
\[ 1 + a_1 + a_0 = K > 0, \quad 1 - a_1 + a_0 = 2 + K > 0, \quad 1 - a_0 = 1 - K > 0. \]
The second condition holds automatically if \( K > 0 \). The first gives \( K > 0 \) and the third gives \( K < 1 \), so we obtain \( 0 < K < 1 \). Interpretation: delay has reduced the admissible gain range by a factor of 2, demonstrating that gains tuned near the upper bound in simulation may be unstable when realistic delays are present.
Problem 4 (Least-Squares Parameter Identification): Suppose we observe three samples of the linear model \( y_k = a y_{k-1} + b u_{k-1} \) with noise-free data:
\[ (y_0, u_0) = (1, 0),\quad (y_1, u_1) = (1, 1),\quad (y_2, u_2) = (2, 1),\quad y_1 = 1,\; y_2 = 2,\; y_3 = 4. \]
Construct \( \mathbf{\Phi} \) and \( \mathbf{y} \), and compute the least-squares estimate \( \hat{\boldsymbol{\theta}} = [\hat{a}, \hat{b}]' \).
Solution:
We have three equations:
\[ \begin{aligned} y_1 &= a y_0 + b u_0 = a \cdot 1 + b \cdot 0 = a, \\ y_2 &= a y_1 + b u_1 = a \cdot 1 + b \cdot 1 = a + b, \\ y_3 &= a y_2 + b u_2 = a \cdot 2 + b \cdot 1 = 2a + b. \end{aligned} \]
Numerically \( y_1 = 1 \), \( y_2 = 2 \), \( y_3 = 4 \). From the first equation \( a = 1 \). Then the second gives \( 2 = 1 + b \Rightarrow b = 1 \). The third is then satisfied automatically: \( 4 = 2 \cdot 1 + 1 = 3 \) would be inconsistent, but our data says \( y_3 = 4 \), so actually we detect that the simple model cannot fit all three points exactly. We therefore use least squares.
The regressor matrix and output are:
\[ \mathbf{\Phi} = \begin{bmatrix} y_0 & u_0 \\ y_1 & u_1 \\ y_2 & u_2 \end{bmatrix} = \begin{bmatrix} 1 & 0 \\ 1 & 1 \\ 2 & 1 \end{bmatrix},\quad \mathbf{y} = \begin{bmatrix} y_1 \\ y_2 \\ y_3 \end{bmatrix} = \begin{bmatrix} 1 \\ 2 \\ 4 \end{bmatrix}. \]
Compute \( \mathbf{\Phi}'\mathbf{\Phi} \) and \( \mathbf{\Phi}'\mathbf{y} \):
\[ \mathbf{\Phi}'\mathbf{\Phi} = \begin{bmatrix} 1 & 1 & 2 \\ 0 & 1 & 1 \end{bmatrix} \begin{bmatrix} 1 & 0 \\ 1 & 1 \\ 2 & 1 \end{bmatrix} = \begin{bmatrix} 6 & 3 \\ 3 & 2 \end{bmatrix}, \] \[ \mathbf{\Phi}'\mathbf{y} = \begin{bmatrix} 1 & 1 & 2 \\ 0 & 1 & 1 \end{bmatrix} \begin{bmatrix} 1 \\ 2 \\ 4 \end{bmatrix} = \begin{bmatrix} 11 \\ 6 \end{bmatrix}. \]
The inverse of \( \mathbf{\Phi}'\mathbf{\Phi} \) is
\[ (\mathbf{\Phi}'\mathbf{\Phi})^{-1} = \frac{1}{(6)(2) - (3)(3)} \begin{bmatrix} 2 & -3 \\ -3 & 6 \end{bmatrix} = \frac{1}{3} \begin{bmatrix} 2 & -3 \\ -3 & 6 \end{bmatrix}. \]
Therefore
\[ \hat{\boldsymbol{\theta}} = (\mathbf{\Phi}'\mathbf{\Phi})^{-1} \mathbf{\Phi}'\mathbf{y} = \frac{1}{3} \begin{bmatrix} 2 & -3 \\ -3 & 6 \end{bmatrix} \begin{bmatrix} 11 \\ 6 \end{bmatrix} = \frac{1}{3} \begin{bmatrix} 22 - 18 \\ -33 + 36 \end{bmatrix} = \frac{1}{3} \begin{bmatrix} 4 \\ 3 \end{bmatrix} = \begin{bmatrix} 4/3 \\ 1 \end{bmatrix}. \]
Thus the least-squares estimate is \( \hat{a} = 4/3 \), \( \hat{b} = 1 \), slightly different from the exact values implied by pairs of equations due to inconsistency in the data. This illustrates how identification reconciles noisy deployment data with simple models.
9. Summary
In this lesson we formalized several recurring lessons from real robot deployments: the exponential reliability model and its relationship to mission success probability, the distinction between reliability and availability, and the quantitative impact of delays on discrete-time control stability. We also showed how deployment logs can be used to identify simple linear models and refine controllers, and how fault-handling patterns enforce safe states. Multi-language code snippets connected these abstract concepts to actual deployment pipelines. These ideas provide a theoretical backbone for interpreting case studies from manufacturing, healthcare, and other domains in this chapter.
10. References
- Trivedi, K. S. (2002). Probability and Statistics with Reliability, Queuing, and Computer Science Applications. Wiley.
- Laprie, J.-C. (1995). Dependable computing and fault-tolerance: Concepts and terminology. 25th International Symposium on Fault-Tolerant Computing, 2–11.
- Ogata, K. (2010). Modern Control Engineering (5th ed.). Prentice Hall. (Chapters on discrete-time control and stability.)
- Ljung, L. (1999). System Identification: Theory for the User (2nd ed.). Prentice Hall.
- Brooks, R. A. (1986). A robust layered control system for a mobile robot. IEEE Journal on Robotics and Automation, 2(1), 14–23.
- Alur, R., Courcoubetis, C., Henzinger, T. A., & Ho, P.-H. (1993). Hybrid automata: An algorithmic approach to the specification and verification of hybrid systems. Hybrid Systems, LNCS 736, 209–229.
- Blanke, M., Kinnaert, M., Lunze, J., & Staroswiecki, M. (2006). Diagnosis and Fault-Tolerant Control. Springer.
- Thrun, S., Burgard, W., & Fox, D. (2005). Probabilistic Robotics. MIT Press. (Sections on modeling uncertainty in robot systems.)
- Verma, V., Gordon, G., Simmons, R., & Thrun, S. (2004). Real-time fault diagnosis. IEEE Robotics & Automation Magazine, 11(2), 56–66.
- Vasseur, C., & Delmotte, F. (1997). Availability analysis and requested safety level in automatized systems. Reliability Engineering & System Safety, 55(1), 79–85.