Chapter 20: Capstone Project (Advanced Autonomy)

Lesson 2: System Architecture and Method Choice

This lesson develops a rigorous view of autonomy system architectures for capstone projects in advanced robotics. We formalize the autonomy stack as a composition of modules, define multi-objective design criteria, and treat the selection of planning, perception, learning, and control methods as a constrained optimization problem under timing, computation, and safety requirements. The lesson concludes with implementation blueprints in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica.

1. Role of System Architecture in the Capstone

In the capstone, you must integrate methods studied in previous chapters: sampling-based and optimization planners, task and motion planning, advanced perception, learning from demonstration, reinforcement learning, and multi-robot coordination. Architecture is the discipline of deciding how these modules interact, at what rates they execute, and how they share information and guarantees.

Let \( x_t \) denote the robot state at time step \( t \), \( z_t \) the raw sensor data, and \( u_t \) the control input. A generic autonomy stack induces a policy

\[ u_t = \pi_{\theta}\bigl(h_{\theta}(z_{0:t})\bigr), \]

where \( h_{\theta} \) summarizes perception, state estimation, task reasoning, and planning, and \( \pi_{\theta} \) is the low-level controller or learned policy. A more structured factorization separates modules as

\[ \begin{aligned} o_t &= P_{\theta_P}(z_t), \\ \hat{x}_t &= E_{\theta_E}(o_{0:t}), \\ \xi_{t:t+H} &= G_{\theta_G}(\hat{x}_t, \mathcal{M}, \varphi), \\ u_t &= C_{\theta_C}(\hat{x}_t, \xi_{t:t+H}), \end{aligned} \]

where \( P_{\theta_P} \) is perception, \( E_{\theta_E} \) is state estimation, \( G_{\theta_G} \) is a planner producing a reference trajectory \( \xi_{t:t+H} \) over horizon \( H \), \( \mathcal{M} \) is a map or model, and \( \varphi \) is a task specification (e.g., a logic formula). The controller \( C_{\theta_C} \) tracks the plan.

The architecture problem for your capstone project is to choose: (i) which module decomposition to use; (ii) which method family to implement for each module (e.g., RRT* vs. trajectory optimization); (iii) how to couple modules in time and information; and (iv) how to guarantee safety and performance margins end-to-end.

2. Formal Module Graph and Data Flow

A system architecture can be modeled as a directed acyclic (or weakly cyclic) graph of modules \( \{M_i\}_{i=1}^N \). Each module \( M_i \) is characterized by

\[ M_i : \mathcal{X}_i \times \Theta_i \times \mathbb{T}_i \rightrightarrows \mathcal{Y}_i, \]

where \( \mathcal{X}_i \) is the input space, \( \mathcal{Y}_i \) the output space, \( \Theta_i \) its parameter space, and \( \mathbb{T}_i \) encodes timing information (update period, deadlines, jitter). The multivalued arrow indicates that modules can be stochastic or non-deterministic (e.g., randomized planners).

For an architecture with perception \( M_P \), estimation \( M_E \), planning \( M_G \), control \( M_C \), and safety monitoring \( M_S \), a typical data flow is summarized below.

flowchart TD
  Z["Sensors z_t"] --> P["Perception M_P"]
  P --> E["Estimator M_E"]
  E --> G["Planner M_G"]
  G --> C["Controller M_C"]
  C --> A["Actuators u_t"]
  E --> S["Safety monitor M_S"]
  G --> S
  S --> C
        

The semantics of this graph can be expressed as a set of update equations. For instance, if perception and estimation operate at period \( T_{\text{percept}} \) and planning at period \( T_{\text{plan}} \), the control input at time \( t \) may depend on the most recent planner output \( \xi_{k:t+H} \) computed at \( k \le t \), which induces delay and potentially stale information. The architecture must therefore explicitly manage: (i) buffer semantics (which version of data is used); (ii) synchronization between modules; and (iii) worst-case timing.

3. Design Objectives and Metrics

Let \( m \) denote a discrete choice of methods, such as a tuple

\[ m = (m_P, m_E, m_G, m_C, m_S) \in \mathcal{M}_P \times \mathcal{M}_E \times \mathcal{M}_G \times \mathcal{M}_C \times \mathcal{M}_S, \]

where, for example, \( m_G \) might be “RRT* with kinodynamic extension” or “trajectory optimization with sequential convex programming”. For each choice \( m \) and continuous parameter vector \( \theta \), the induced closed-loop behavior \( \{x_t, u_t\}_{t=0}^T \) is random due to initial conditions, noise, and stochastic methods.

A generic performance objective is

\[ J(m, \theta) = \mathbb{E}\Bigl[ \sum_{t=0}^{T-1} \ell(x_t, u_t; m, \theta) + \Phi(x_T) \Bigr], \]

where \( \ell \) is a stage cost encoding task progress, control effort, risks, and constraint violations, and \( \Phi \) is a terminal cost.

In a capstone project, you rarely optimize a single scalar objective. Instead, you track a vector of metrics

\[ c(m, \theta) = \bigl( c_{\text{task}},\, c_{\text{compute}},\, c_{\text{robust}},\, c_{\text{safety}},\, c_{\text{dev}} \bigr), \]

where, for example, \( c_{\text{task}} \) measures task success (time-to-completion, path length), \( c_{\text{compute}} \) measures CPU and memory usage, \( c_{\text{robust}} \) measures performance under perturbations, \( c_{\text{safety}} \) encodes constraint violations, and \( c_{\text{dev}} \) accounts for implementation complexity. A common scalarization is

\[ J_{\lambda}(m,\theta) = \lambda^{\top} c(m,\theta), \quad \lambda \in \mathbb{R}^5_{\ge 0}, \]

with designer-chosen weights \( \lambda \). Method choice is then treated as a constrained optimization problem:

\[ \min_{m,\theta}\; J_{\lambda}(m,\theta) \quad \text{s.t.}\quad g_j(m,\theta) \le 0,\; j = 1,\dots,J, \]

where constraint functions \( g_j \) encode timing, safety, and resource limits.

4. Timing and Computational Constraints

Consider a control loop with sequential modules \( M_{i_1}, M_{i_2}, \dots, M_{i_K} \). Let \( C_k(m) \) denote the worst-case execution time of module \( M_{i_k} \) under method choice \( m \), and let \( \delta_k \) be the communication delay between \( M_{i_k} \) and \( M_{i_{k+1}} \). The worst-case loop period is

\[ T_{\text{loop}}(m) = \sum_{k=1}^K C_k(m) + \sum_{k=1}^{K-1} \delta_k. \]

If the physical platform requires a maximum allowable control period \( T_{\max} \) for stability or performance, a necessary condition is

\[ T_{\text{loop}}(m) \le T_{\max}. \]

When modules run at different rates, we can use a utilization-based model. Suppose each module \( M_i \) has worst-case execution time \( C_i \) and period \( T_i \). Under a single CPU and rate-monotonic scheduling assumptions, a sufficient condition for schedulability is

\[ U = \sum_{i=1}^N \frac{C_i}{T_i} \le N\bigl(2^{1/N} - 1\bigr), \]

which for large \( N \) approaches \( \ln 2 \approx 0.693 \). Thus, method choices that significantly increase \( C_i \) (e.g., a very expensive planner) may violate this bound and must be run more sparsely or offloaded.

5. Safety Margins and Modular Guarantees

A key advantage of a structured architecture is that we can reason about safety via modular guarantees. Assume the planner ensures a configuration-space distance margin to obstacles \( d_{\text{plan}}(t) \ge d_{\min} > 0 \) for all time steps within its solution, and the low-level tracking error \( e(t) = x_t - \xi_t \) obeys

\[ \lVert e(t) \rVert_2 \le \bar{e} \quad \text{for all } t, \]

where \( \xi_t \) is the planned state at time \( t \). Suppose further that the configuration-space distance to the nearest obstacle is 1-Lipschitz with respect to \( x_t \). Then the actual distance \( d_{\text{real}}(t) \) satisfies

\[ d_{\text{real}}(t) \ge d_{\text{plan}}(t) - \lVert e(t) \rVert_2 \ge d_{\min} - \bar{e}. \]

Therefore, if you design the controller so that \( \bar{e} < d_{\min} \), the closed-loop system is collision free. This yields a modular design rule:

  • The planner must certify a minimum margin \( d_{\min} \).
  • The controller must guarantee a tracking error bound \( \bar{e} \).
  • The architecture must ensure that delays and sampling do not invalidate these guarantees.

This type of reasoning is essential when choosing, for example, between a more aggressive kinodynamic planner and a conservative lattice planner, or between a model-based controller and a learned policy.

Reliability can be modeled similarly. Assume modules fail independently with probabilities \( q_i \) per cycle. Then the per-cycle success probability is

\[ p_{\text{succ}} = \prod_{i=1}^N (1 - q_i), \]

and the probability that the system completes \( T \) cycles without failure is approximately

\[ p_{\text{sys}}(T) = p_{\text{succ}}^{\,T}. \]

Introducing redundancy (e.g., two perception methods with a voting scheme) effectively modifies the \( q_i \) and may be necessary for safety-critical capstone projects.

6. Python Blueprint: ROS 2 Style Architecture Skeleton

In Python, a typical capstone architecture is implemented on top of ROS 2 and motion planning libraries (e.g., OMPL or MoveIt). Below is a minimal skeleton illustrating how to factor modules into nodes and how to express method choices via configuration.


import dataclasses
from enum import Enum

class PlannerKind(Enum):
    RRT_STAR = "rrt_star"
    TRAJ_OPT = "traj_opt"

class EstimatorKind(Enum):
    EKF = "ekf"
    PARTICLE = "particle"

@dataclasses.dataclass
class MethodChoice:
    planner: PlannerKind
    estimator: EstimatorKind
    use_learned_policy: bool

@dataclasses.dataclass
class TimingConfig:
    control_dt: float
    plan_dt: float
    perception_dt: float

@dataclasses.dataclass
class ArchitectureConfig:
    methods: MethodChoice
    timing: TimingConfig

class PerceptionModule:
    def __init__(self, config: ArchitectureConfig):
        self.config = config

    def process_sensors(self, raw_msg):
        # Return processed features or intermediate representation
        return {"features": raw_msg.data}

class EstimatorModule:
    def __init__(self, config: ArchitectureConfig):
        self.config = config

    def update_state(self, features, prev_state):
        # Use EKF or particle filter depending on config.methods.estimator
        return prev_state  # placeholder

class PlannerModule:
    def __init__(self, config: ArchitectureConfig):
        self.config = config

    def compute_plan(self, state, goal):
        if self.config.methods.planner is PlannerKind.RRT_STAR:
            # Call OMPL-based planner
            plan = []
        else:
            # Call trajectory optimization method
            plan = []
        return plan

class ControllerModule:
    def __init__(self, config: ArchitectureConfig):
        self.config = config

    def compute_control(self, state, plan):
        # Compute u_t based on current state and plan prefix
        return [0.0, 0.0, 0.0]  # placeholder

class SafetyMonitorModule:
    def __init__(self, config: ArchitectureConfig):
        self.config = config

    def check(self, state, plan, u):
        # Example: clamp velocity or stop if constraints violated
        return u

class AutonomyStack:
    def __init__(self, config: ArchitectureConfig):
        self.config = config
        self.perception = PerceptionModule(config)
        self.estimator = EstimatorModule(config)
        self.planner = PlannerModule(config)
        self.controller = ControllerModule(config)
        self.monitor = SafetyMonitorModule(config)
        self.state = None
        self.plan = None
        self.goal = None

    def step(self, sensor_msg):
        features = self.perception.process_sensors(sensor_msg)
        self.state = self.estimator.update_state(features, self.state)
        if self.plan is None or self._need_replan(self.state, self.plan):
            self.plan = self.planner.compute_plan(self.state, self.goal)
        u = self.controller.compute_control(self.state, self.plan)
        u_safe = self.monitor.check(self.state, self.plan, u)
        return u_safe

    def _need_replan(self, state, plan):
        return False  # placeholder
      

In a real ROS 2 project you would subclass rclpy.node.Node and wire the modules via topics, services, or actions. The configuration ArchitectureConfig can be loaded from YAML files to encode method choices for different experimental conditions.

7. C++ and Java Implementation Patterns

7.1 C++ Node-Based Architecture (ROS 2 / OMPL)

In C++, methods are typically encapsulated in classes that are wired as ROS 2 nodes. Libraries such as OMPL (for planning) and Eigen (for linear algebra) are commonly used.


#include <memory>
#include <vector>
#include "rclcpp/rclcpp.hpp"

struct MethodChoice {
  enum class PlannerKind { RRT_STAR, TRAJ_OPT };
  enum class EstimatorKind { EKF, PARTICLE };
  PlannerKind planner;
  EstimatorKind estimator;
  bool use_learned_policy;
};

class AutonomyNode : public rclcpp::Node {
public:
  explicit AutonomyNode(const MethodChoice & choice)
  : Node("autonomy_node"), choice_(choice)
  {
    // Initialize publishers, subscribers, timers
  }

private:
  MethodChoice choice_;
  // Perception, estimator, planner, controller modules as members

  void sensorCallback(const std::shared_ptr<const SensorMsg> msg)
  {
    // Process perception, estimation, planning, control, monitoring
  }
};
      

7.2 Java Service-Oriented Architecture

For Java-based robotics (e.g., industrial manipulator controllers or ROSJava setups), an interface-based design allows swapping methods via dependency injection.


public interface Planner {
    Plan computePlan(State x, Goal g);
}

public class RRTStarPlanner implements Planner {
    @Override
    public Plan computePlan(State x, Goal g) {
        // RRT* planning logic
        return new Plan();
    }
}

public class TrajOptPlanner implements Planner {
    @Override
    public Plan computePlan(State x, Goal g) {
        // Trajectory optimization logic
        return new Plan();
    }
}

public class AutonomyStack {
    private final Planner planner;
    private final Controller controller;
    private final Estimator estimator;

    public AutonomyStack(Planner planner,
                         Controller controller,
                         Estimator estimator) {
        this.planner = planner;
        this.controller = controller;
        this.estimator = estimator;
    }

    public ControlOutput step(SensorMsg msg, Goal goal) {
        State xhat = estimator.update(msg);
        Plan plan = planner.computePlan(xhat, goal);
        ControlOutput u = controller.compute(xhat, plan);
        return u;
    }
}
      

Alternative planners and estimators are selected simply by passing different implementations to the AutonomyStack constructor, reflecting different architecture and method choices in your experiments.

8. MATLAB/Simulink and Mathematica Prototyping

8.1 MATLAB/Simulink Architecture Diagram

Simulink is well-suited for representing the autonomy stack as a block diagram with discrete-time subsystems. The example below programmatically creates a simple loop with perception, planner, and controller blocks.


modelName = "capstone_architecture";
new_system(modelName);
open_system(modelName);

add_block("simulink/Commonly Used Blocks/In1", ...
          modelName + "/Sensors");
add_block("simulink/Commonly Used Blocks/Out1", ...
          modelName + "/Actuators");

add_block("simulink/User-Defined Functions/Subsystem", ...
          modelName + "/Perception");
add_block("simulink/User-Defined Functions/Subsystem", ...
          modelName + "/Estimator");
add_block("simulink/User-Defined Functions/Subsystem", ...
          modelName + "/Planner");
add_block("simulink/User-Defined Functions/Subsystem", ...
          modelName + "/Controller");

add_line(modelName, "Sensors/1", "Perception/1");
add_line(modelName, "Perception/1", "Estimator/1");
add_line(modelName, "Estimator/1", "Planner/1");
add_line(modelName, "Planner/1", "Controller/1");
add_line(modelName, "Controller/1", "Actuators/1");

save_system(modelName);
      

Each subsystem can be parameterized with specific methods, such as an RRT*-based planner or a collocation-based trajectory optimizer, and scheduled using discrete-time sample times.

8.2 Wolfram Mathematica Functional Composition

In Mathematica, the architecture is naturally expressed as a composition of pure functions, enabling rapid exploration of method combinations.


perception[raw_] := Module[{features},
  (* Extract features from raw sensor data *)
  features = raw;
  features
];

estimator[state_, features_] := Module[{xhat},
  (* EKF or particle filter logic *)
  xhat = state;
  xhat
];

planner[xhat_, goal_, method_] := Module[{plan},
  Which[
    method === "RRTStar",
      plan = {},

    method === "TrajOpt",
      plan = {}
  ];
  plan
];

controller[xhat_, plan_] := Module[{u},
  (* Compute control command *)
  u = {0., 0., 0.};
  u
];

autonomyStep[raw_, goal_, method_, state_] := Module[
  {features, xhat, plan, u},
  features = perception[raw];
  xhat = estimator[state, features];
  plan = planner[xhat, goal, method];
  u = controller[xhat, plan];
  {u, xhat, plan}
];
      

You can wrap autonomyStep in a simulation loop and vary the method parameter to compare the impact of different planners or estimators on closed-loop performance metrics.

9. Problems and Solutions

Problem 1 (Combinatorial Method Choice Space): Suppose you have three perception methods, two estimation methods, three planning methods, two controllers, and two safety monitors. How many distinct method configurations \( m = (m_P, m_E, m_G, m_C, m_S) \) are possible? If each configuration takes 20 minutes to implement and test, what is the total time required to test all configurations?

Solution: The number of combinations is \( 3 \times 2 \times 3 \times 2 \times 2 = 72 \). At 20 minutes per configuration, the total time is \( 72 \times 20 = 1440 \) minutes, or 24 hours. This demonstrates why you must prune the design space using architectural insight instead of exhaustive search.

Problem 2 (Timing Feasibility): A control loop consists of four sequential modules with worst-case execution times (in milliseconds) depending on method choice: perception \( C_P = 6 \), estimation \( C_E = 3 \), planning \( C_G = 20 \), control \( C_C = 1 \). Communication delays between these modules are negligible. The platform requires a loop period at most \( T_{\max} = 30 \) ms. Is this method choice feasible? What is the largest additional monitoring module time \( C_S \) you can afford while remaining feasible?

Solution: The loop period is

\[ T_{\text{loop}} = C_P + C_E + C_G + C_C = 6 + 3 + 20 + 1 = 30\ \text{ms}, \]

which exactly meets the requirement \( T_{\text{loop}} \le T_{\max} \), so it is feasible. Adding a monitoring module with time \( C_S \) yields \( T_{\text{loop}}^{\prime} = 30 + C_S \). Feasibility requires \( 30 + C_S \le 30 \), hence \( C_S \le 0 \). In other words, no additional computation can be added in the sequential loop without violating the timing requirement; you would need to parallelize monitoring or speed up other modules.

Problem 3 (Safety Margin Proof): Assume the planner provides a minimum configuration-space distance to obstacles \( d_{\text{plan}}(t) \ge d_{\min} > 0 \) for all time steps along the planned trajectory \( \xi_t \). The tracking error satisfies \( \lVert e(t) \rVert_2 \le \bar{e} \) for all \( t \), and the distance to obstacles is 1-Lipschitz in the configuration: \( \lvert d(x) - d(y) \rvert \le \lVert x - y \rVert_2 \). Prove that if \( \bar{e} < d_{\min} \) then the realized trajectory \( x_t \) is collision free.

Solution: Let \( d_{\text{real}}(t) = d(x_t) \) denote the distance from the realized state to the nearest obstacle. Then

\[ \begin{aligned} d_{\text{real}}(t) &\ge d_{\text{plan}}(t) - \lvert d(x_t) - d(\xi_t) \rvert \\ &\ge d_{\text{plan}}(t) - \lVert x_t - \xi_t \rVert_2 \\ &\ge d_{\min} - \bar{e}. \end{aligned} \]

If \( \bar{e} < d_{\min} \), then \( d_{\text{real}}(t) > 0 \) for all \( t \), which implies that the robot remains strictly outside all obstacles and therefore the trajectory is collision free.

Problem 4 (Reliability with Redundant Perception): Suppose the overall architecture consists of four modules, each with per-cycle failure probability \( q_i \). Let \( q_1 = 0.01 \) (perception), \( q_2 = 0.005 \) (estimation), \( q_3 = 0.002 \) (planning), \( q_4 = 0.001 \) (control). Compute the per-cycle success probability \( p_{\text{succ}} \). Now suppose you introduce a redundant perception module such that perception fails only if both modules fail, each having failure probability 0.01 and acting independently. What is the new per-cycle success probability?

Solution: Without redundancy, the per-cycle success probability is

\[ \begin{aligned} p_{\text{succ}} &= (1 - q_1)(1 - q_2)(1 - q_3)(1 - q_4) \\ &= 0.99 \times 0.995 \times 0.998 \times 0.999. \end{aligned} \]

Numerically, this is approximately \( 0.99 \times 0.995 \approx 0.98505 \), \( 0.98505 \times 0.998 \approx 0.98308 \), \( 0.98308 \times 0.999 \approx 0.98210 \). With redundant perception modules having failures \( q_1^{(1)} = q_1^{(2)} = 0.01 \) and independence, the perception subsystem fails with probability

\[ q_1^{\text{red}} = 0.01^2 = 10^{-4}, \]

hence perception succeeds with probability \( 1 - q_1^{\text{red}} = 0.9999 \). The new per-cycle success probability is

\[ p_{\text{succ}}^{\text{red}} = 0.9999 \times 0.995 \times 0.998 \times 0.999 \approx 0.9929, \]

showing a substantial improvement in reliability from roughly 98.2% to 99.3% per cycle.

Problem 5 (Method Choice Flow): You must choose between a sampling-based planner (S) and a trajectory optimization planner (T) for a manipulator capstone project. You know: (i) the environment is cluttered; (ii) dynamics are non-trivial but moderately smooth; (iii) real-time re-planning is desirable but not strictly required. Sketch a decision flow that captures how these criteria influence the final method choice.

Solution (decision flow): One possible decision flow is:

flowchart TD
  ST["Start"] --> CL["Environment very cluttered?"]
  CL -->|"yes"| SB["Prefer sampling-based \nplanner S"]
  CL -->|"no"| DYN["Dynamics strongly nonlinear \nbut smooth?"]
  DYN -->|"yes"| TO["Prefer trajectory \noptimization T"]
  DYN -->|"no"| RT["Hard real-time \nreplanning required?"]
  RT -->|"yes"| SB_RT["Use fast sampling-based \nS with pruning"]
  RT -->|"no"| HYB["Use hybrid: coarse S, \nrefine with T"]
        

This flow can be refined into a formal multi-objective optimization over metrics such as success rate, computation time, and path quality, but it already illustrates how architectural reasoning guides method selection.

10. Summary

In this lesson we treated system architecture and method choice as a structured design problem. We modeled the autonomy stack as a graph of modules with timing and reliability properties, defined multi-objective cost functions over method choices, and derived simple yet powerful safety and schedulability conditions. Implementation blueprints in Python, C++, Java, MATLAB/Simulink, and Mathematica illustrated how to encode these architectural decisions in code. These concepts are crucial as you enter the implementation, validation, and reporting phases of your capstone project.

11. References

  1. Brooks, R.A. (1986). A robust layered control system for a mobile robot. IEEE Journal on Robotics and Automation, 2(1), 14–23.
  2. Gat, E. (1998). Three-layer architectures. In D. Kortenkamp, R.P. Bonasso, & R. Murphy (Eds.), Artificial Intelligence and Mobile Robots. MIT Press.
  3. Alami, R., Chatila, R., Fleury, S., Ghallab, M., & Ingrand, F. (1998). An architecture for autonomy. International Journal of Robotics Research, 17(4), 315–337.
  4. Kaelbling, L.P., Littman, M.L., & Cassandra, A.R. (1998). Planning and acting in partially observable stochastic domains. Artificial Intelligence, 101(1–2), 99–134.
  5. LaValle, S.M. (2006). Planning Algorithms. Cambridge University Press. (Chapters on system-level planning and integration.)
  6. Beetz, M., Jain, D., Mosenlechner, L., Tenorth, M., Kunze, L., Blodow, N., & Kruijff, G.-J.M. (2010). Cognition-enabled autonomous robot control for the realization of home chore task intelligence. Proceedings of the IEEE, 100(8), 2454–2471.
  7. Huang, H., Kwiatkowska, M., Norman, G., & Parker, D. (2017). Probabilistic model checking for intelligent autonomous systems. International Symposium on Automated Technology for Verification and Analysis.
  8. Schwarting, W., Alonso-Mora, J., & Rus, D. (2018). Planning and decision-making for autonomous vehicles. Annual Review of Control, Robotics, and Autonomous Systems, 1, 187–210.
  9. Censi, A. (2015). A mathematical theory of co-design. arXiv preprint arXiv:1512.08055.
  10. Kress-Gazit, H., Wongpiromsarn, T., & Belta, C. (2009). Correct-by-construction control synthesis for robots with partial information and temporal logic specifications. IEEE Transactions on Robotics, 26(6), 1139–1154.