Chapter 20: Capstone / Mini-Project
Lesson 3: Implementation Milestones
This lesson formalizes implementation milestones for a robotics capstone project as mathematical objects tied to task graphs and discrete-time progress dynamics. We show how to represent tasks, dependencies, and milestones using inequalities and state-space style models (leveraging your linear control background), and we illustrate simple multi-language code skeletons (Python, C++, Java, MATLAB/Simulink) for tracking milestone completion in a robot project.
1. Conceptual Role of Implementation Milestones
In a robotics capstone, an implementation milestone is a discrete event where a meaningful system capability becomes available. Examples include:
- “Base can be teleoperated safely in the lab corridor.”
- “All proprioceptive sensors stream data at nominal rates.”
- “Low-level PID stabilization closed-loop is tested on the real robot.”
A milestone is not a single task, but the completion of a subset of tasks. Formally, let the set of implementation tasks be \( \mathcal{T} = \{1,\dots,N\} \). Each task contributes to some milestone(s). Milestones allow you to:
- Structure the integration of mechanical, electrical, and software subsystems.
- Expose intermediate demos for feedback (e.g., internal review, mid-term check).
- Control technical risk by front-loading high-uncertainty components (e.g., perception).
A typical high-level flow from project idea to milestone tracking is:
flowchart TD
G["Robot goal / use-case"] --> D["Decompose into subsystems (mechanical, sensing, control, software)"]
D --> T["Enumerate implementation tasks (T1,...,TN)"]
T --> M["Group tasks into milestones (M1,...,Mk)"]
M --> S["Build schedule with dependencies"]
S --> EX["Execute sprints / lab sessions"]
EX --> TR["Track progress vs milestone criteria"]
TR --> ADJ["Adjust plan and resources if needed"]
The rest of the lesson makes this diagram precise: we express tasks and milestones as variables, constraints, and sets, which you can compute and simulate.
2. Mathematical Model of Tasks and Milestones
Let \( \mathcal{T} = \{1,\dots,N\} \) be the index set of tasks. For each task \( i \in \mathcal{T} \) we define:
- \( d_i \gt 0 \): estimated duration (e.g., in hours or days).
- \( s_i \ge 0 \): planned start time.
- \( f_i = s_i + d_i \): planned finish time.
Precedence constraints are written as a partial order \( i \prec j \) meaning “task \( i \) must finish before task \( j \) starts”. For each such pair we require
\[ s_j \ge f_i \quad \text{for all pairs } (i,j) \text{ with } i \prec j. \]
A milestone \( m \) is defined by a subset \( S_m \subseteq \mathcal{T} \) of tasks that must be finished. The completion time of milestone \( m \) is
\[ T_m = \max_{i \in S_m} f_i. \]
Intuitively, \( T_m \) is the time when the last task required for milestone \( m \) finishes. The project makespan is the completion time of the final milestone whose task set is the entire project:
\[ S_{\text{final}} = \mathcal{T}, \qquad T_{\text{final}} = \max_{i \in \mathcal{T}} f_i. \]
A basic monotonicity property is immediate and useful when reasoning about schedule risk.
Proposition (Monotonicity of milestone time).
Suppose two sets of task durations \( \{d_i\} \) and \( \{d_i'\} \) satisfy \( d_i' \ge d_i \) for all \( i \), with the same precedence constraints and start times chosen to respect those constraints. Then for every milestone \( m \),
\[ T_m' \ge T_m. \]
Sketch of proof. For each task we have
\[ f_i' = s_i' + d_i' \ge s_i + d_i = f_i, \]
because respecting the same precedence constraints cannot make start times earlier when durations are larger. Taking the maximum over \( i \in S_m \) yields \( T_m' = \max_{i \in S_m} f_i' \ge \max_{i \in S_m} f_i = T_m \).
This captures the obvious engineering insight: if any task you depend on takes longer, a milestone cannot move earlier in time.
3. Discrete-Time Progress Dynamics for Milestones
Your linear control background lets us treat project progress as a discrete-time system. Let \( \mathbf{x}_k \in [0,1]^n \) be a vector that encodes the completion fraction of \( n \) subsystems (e.g., mechanics, low-level control, perception, safety, autonomy) at iteration or week \( k \). A simple linear model is
\[ \mathbf{x}_{k+1} = \mathbf{x}_k + \mathbf{B}\mathbf{u}_k + \mathbf{w}_k, \]
where:
- \( \mathbf{u}_k \) is an “effort allocation” vector (who works on what).
- \( \mathbf{B} \) maps effort to expected progress (productivity matrix).
- \( \mathbf{w}_k \) models disturbances (unexpected delays or accelerations).
A milestone corresponds to a region in state space defined by linear inequalities:
\[ \mathcal{M} = \{ \mathbf{x} \in [0,1]^n : \mathbf{C}\mathbf{x} \ge \mathbf{d} \}, \]
where, for example, \( \mathbf{C} \) can select subsystem components and \( \mathbf{d} \) encodes thresholds such as “low-level control completeness >= 0.9 and mechanical integration completeness >= 0.8”.
The first hitting time of milestone \( \mathcal{M} \) is
\[ k_m = \min \{ k \in \mathbb{N} : \mathbf{x}_k \in \mathcal{M} \}. \]
A simple, but important, property holds if the project dynamics are monotone.
Theorem (Milestone invariance under monotone progress).
Assume all components of \( \mathbf{B}\mathbf{u}_k \) and \( \mathbf{w}_k \) are nonnegative for all \( k \). Then the sequence \( \{\mathbf{x}_k\} \) is componentwise nondecreasing, and once the milestone is reached, it remains reached:
\[ \mathbf{x}_k \in \mathcal{M} \;\Rightarrow\; \mathbf{x}_{k'} \in \mathcal{M} \quad \text{for all } k' \ge k. \]
Proof. Nonnegativity implies
\[ \mathbf{x}_{k+1} - \mathbf{x}_k = \mathbf{B}\mathbf{u}_k + \mathbf{w}_k \ge \mathbf{0} \quad \Rightarrow \quad \mathbf{x}_{k+1} \ge \mathbf{x}_k \]
componentwise. Thus \( \mathbf{x}_{k'} \ge \mathbf{x}_k \) for all \( k' \ge k \). Since \( \mathbf{C} \) and \( \mathbf{d} \) are fixed, if \( \mathbf{C}\mathbf{x}_k \ge \mathbf{d} \), then
\[ \mathbf{C}\mathbf{x}_{k'} \ge \mathbf{C}\mathbf{x}_k \ge \mathbf{d} \quad \Rightarrow \quad \mathbf{x}_{k'} \in \mathcal{M}. \]
This matches the intuitive requirement that a milestone definition should not “undo itself” as you continue working.
A control-style view of milestone tracking is visualized below:
flowchart TD REF["Milestone target (C x >= d)"] --> PLAN["Choose weekly effort u_k"] PLAN --> EXEC["Execute sprint / lab work"] EXEC --> MEAS["Measure progress x_k"] MEAS --> CMP["Compare C x_k vs d"] CMP -->|"x_k not in milestone region"| ADJ["Adjust allocation / plan"] CMP -->|"x_k in milestone region"| NEXT["Lock milestone and define next one"]
4. Example Milestone Structure for an Intro Robot Project
Consider a differential-drive mobile robot with a simple sensor suite (encoders, IMU, depth sensor) and a rule-based autonomy layer. One possible milestone decomposition is:
-
Milestone M1 — Hardware bring-up.
- Chassis assembled, motors mounted.
- Motor drivers wired and tested at low voltage.
- MCU or single-board computer boots and can toggle GPIOs.
-
Milestone M2 — Sensing and telemetry.
- Encoders produce counts with correct direction and scaling.
- IMU data streamed and basic calibration applied.
- Robot pose estimate logged at fixed sampling rate.
-
Milestone M3 — Closed-loop low-level control.
- Wheel-speed loops closed using linear PID controllers.
- Step responses meet settling-time and overshoot specs from the control course.
-
Milestone M4 — Teleoperation and safety.
- Human teleop via joystick or keyboard.
- Emergency-stop (E-stop) chain tested.
-
Milestone M5 — Basic autonomy demo.
- Simple rule-based obstacle avoidance with depth sensing.
- Repeatable path following over a short corridor.
Each milestone \( M_k \) is defined by a task subset \( S_k \), and the time axis is organized so that high-risk components (e.g., sensing, low-level control) appear in earlier milestones.
5. Python Implementation — Computing Milestone Completion Times
The following Python snippet computes earliest finish times for tasks in
a directed acyclic graph (DAG) and then evaluates the completion time of
a milestone. This is a minimal from-scratch implementation; in a
robotics stack you might integrate this logic into a ROS2
rclpy node that publishes milestone progress.
from dataclasses import dataclass
from collections import deque, defaultdict
from typing import Dict, List, Set
@dataclass
class Task:
name: str
duration: float # hours or days
predecessors: List[str] # task names that must finish before this starts
def topological_order(tasks: Dict[str, Task]) -> List[str]:
"""Kahn's algorithm for DAG topological ordering."""
successors = defaultdict(list)
indegree = defaultdict(int)
for t in tasks.values():
indegree[t.name] = indegree[t.name] # ensure key exists
for p in t.predecessors:
successors[p].append(t.name)
indegree[t.name] += 1
q = deque([name for name, deg in indegree.items() if deg == 0])
order = []
while q:
v = q.popleft()
order.append(v)
for w in successors[v]:
indegree[w] -= 1
if indegree[w] == 0:
q.append(w)
if len(order) != len(tasks):
raise ValueError("Graph has a cycle or missing tasks")
return order
def earliest_times(tasks: Dict[str, Task]) -> Dict[str, float]:
"""Compute earliest finish time for each task."""
order = topological_order(tasks)
finish: Dict[str, float] = {}
start: Dict[str, float] = {}
for name in order:
t = tasks[name]
if not t.predecessors:
start[name] = 0.0
else:
start[name] = max(finish[p] for p in t.predecessors)
finish[name] = start[name] + t.duration
return finish
def milestone_time(tasks: Dict[str, Task], milestone_tasks: Set[str]) -> float:
finish = earliest_times(tasks)
return max(finish[name] for name in milestone_tasks)
# Example: hardware bring-up milestone
tasks = {
"chassis": Task("chassis", 8.0, []),
"motor_wiring": Task("motor_wiring", 6.0, ["chassis"]),
"mcu_bringup": Task("mcu_bringup", 5.0, []),
"low_voltage_test": Task("low_voltage_test", 4.0,
["motor_wiring", "mcu_bringup"]),
}
M1 = {"chassis", "motor_wiring", "mcu_bringup", "low_voltage_test"}
T_M1 = milestone_time(tasks, M1)
print("Milestone M1 earliest completion time:", T_M1)
You can enrich this model with uncertainty (e.g., random durations) and repeatedly sample completion times to empirically approximate distributions for \( T_m \).
6. C++ Implementation — Minimal Milestone Tracker Skeleton
In C++ you might track milestones inside a robotics middleware node
(e.g., a ROS2
rclcpp node). Below is a self-contained C++17-style data
structure for tasks and a simple earliest-finish calculation assuming
tasks are already topologically ordered.
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
struct Task {
std::string name;
double duration;
std::vector<std::string> predecessors;
};
using TaskMap = std::unordered_map<std::string, Task>;
using TimeMap = std::unordered_map<std::string, double>;
TimeMap earliest_finish_times(const TaskMap& tasks,
const std::vector<std::string>& topo_order)
{
TimeMap finish, start;
for (const auto& name : topo_order) {
const auto& t = tasks.at(name);
if (t.predecessors.empty()) {
start[name] = 0.0;
} else {
double s = 0.0;
for (const auto& p : t.predecessors) {
s = std::max(s, finish[p]);
}
start[name] = s;
}
finish[name] = start[name] + t.duration;
}
return finish;
}
double milestone_time(const TimeMap& finish,
const std::unordered_set<std::string>& milestone_tasks)
{
double T = 0.0;
for (const auto& name : milestone_tasks) {
auto it = finish.find(name);
if (it == finish.end()) {
throw std::runtime_error("Unknown task in milestone");
}
if (it->second > T) {
T = it->second;
}
}
return T;
}
int main() {
TaskMap tasks;
tasks["chassis"] = {"chassis", 8.0, {}};
tasks["motor_wiring"] = {"motor_wiring", 6.0, {"chassis"}};
tasks["mcu_bringup"] = {"mcu_bringup", 5.0, {}};
tasks["low_voltage_test"] =
{"low_voltage_test", 4.0, {"motor_wiring", "mcu_bringup"}};
std::vector<std::string> topo_order = {
"chassis", "mcu_bringup", "motor_wiring", "low_voltage_test"
};
TimeMap finish = earliest_finish_times(tasks, topo_order);
std::unordered_set<std::string> M1 = {
"chassis", "motor_wiring", "mcu_bringup", "low_voltage_test"
};
double T = milestone_time(finish, M1);
std::cout << "M1 earliest completion time: " << T << std::endl;
return 0;
}
In ROS2 you could extend main to initialize a node,
periodically recompute \( T_m \) from current estimates
of remaining durations, and publish a “time-to-milestone” diagnostic
topic.
7. Java Implementation — Milestone Progress in a Robot Controller
Java is commonly used in educational robotics frameworks (e.g., high-school competition robots). Below is a simple Java class that tracks whether a milestone is reached based on a normalized progress vector \( \mathbf{x}_k \) and linear inequality \( \mathbf{C}\mathbf{x}_k \ge \mathbf{d} \).
import java.util.Arrays;
public class MilestoneCondition {
// C is m x n, x is length n, d is length m
private final double[][] C;
private final double[] d;
public MilestoneCondition(double[][] C, double[] d) {
this.C = C;
this.d = d;
}
public boolean isReached(double[] x) {
for (int i = 0; i != C.length; ++i) {
double lhs = 0.0;
for (int j = 0; j != x.length; ++j) {
lhs += C[i][j] * x[j];
}
if (lhs < d[i]) {
return false;
}
}
return true;
}
public static void main(String[] args) {
// Example: two subsystems, milestone when both >= 0.8
double[][] C = {
{1.0, 0.0},
{0.0, 1.0}
};
double[] d = {0.8, 0.8};
MilestoneCondition M = new MilestoneCondition(C, d);
double[] x1 = {0.6, 0.9};
double[] x2 = {0.85, 0.9};
System.out.println("Reached at x1? " + M.isReached(x1));
System.out.println("Reached at x2? " + M.isReached(x2));
}
}
In a real robot controller loop, x would be filled using
measured coverage of test cases, percent of closed-loop behaviors
validated, and so forth, rather than arbitrary numbers.
8. MATLAB/Simulink Implementation — Progress as a Discrete-Time System
MATLAB and Simulink are natural environments for modeling the discrete-time progress dynamics introduced earlier. The following MATLAB script simulates \( \mathbf{x}_{k+1} = \mathbf{x}_k + \mathbf{B}\mathbf{u}_k \) without disturbances, and checks when a milestone region is entered.
% Number of subsystems (e.g., mechanics, control, perception)
n = 3;
Nsteps = 10;
% Initial progress x_0
x = zeros(n, 1);
% Effort-to-progress matrix B (n x n) and constant weekly effort u_k
B = [0.2 0.0 0.0;
0.1 0.15 0.0;
0.0 0.1 0.2];
u = [1.0; 1.0; 1.0]; % normalized effort per week
% Milestone region C x_k >= d
C = eye(n);
d = 0.8 * ones(n, 1);
km = NaN; % first hitting time
for k = 0:(Nsteps - 1)
% Check milestone at current state
if all(C * x >= d) && isnan(km)
km = k;
end
% Update progress
x = x + B * u;
x = min(x, 1.0); % clip at 1.0 (fully complete)
end
if isnan(km)
fprintf("Milestone not reached in %d steps.\n", Nsteps);
else
fprintf("Milestone reached at step k = %d.\n", km);
end
In Simulink you can implement the same model using a
Discrete-Time Integrator block for each subsystem, feeding it
with B * u_k. A simple comparator block checks
\( \mathbf{C}\mathbf{x}_k \ge \mathbf{d} \) and drives
a Boolean signal indicating milestone achievement.
9. Problems and Solutions
Problem 1 (Milestone completion time in a small DAG). Consider four tasks with durations \( d_1 = 2 \), \( d_2 = 3 \), \( d_3 = 4 \), \( d_4 = 1 \), and precedence constraints \( 1 \prec 3 \), \( 2 \prec 3 \), \( 3 \prec 4 \). Assume tasks 1 and 2 may start at time 0. Define milestone \( M \) as “all tasks finished”, i.e. \( S_M = \{1,2,3,4\} \). Compute \( T_M \).
Solution.
Tasks 1 and 2 start at 0, so \( f_1 = 2 \) and \( f_2 = 3 \). Task 3 must start after both 1 and 2 finish, so
\[ s_3 = \max(f_1, f_2) = \max(2, 3) = 3, \qquad f_3 = s_3 + d_3 = 3 + 4 = 7. \]
Task 4 must start after 3 finishes, so \( s_4 = f_3 = 7 \) and \( f_4 = s_4 + d_4 = 8 \). Therefore the milestone completion time is
\[ T_M = \max(f_1, f_2, f_3, f_4) = 8. \]
Problem 2 (Effect of duration perturbations). In the setting of Problem 1, suppose duration \( d_2 \) increases from 3 to 6, while all others remain unchanged. Compute the new milestone time \( T_M' \) and verify the monotonicity inequality \( T_M' \ge T_M \).
Solution.
We now have \( f_2' = 6 \), while \( f_1' = 2 \) is unchanged. The earliest start for task 3 is
\[ s_3' = \max(f_1', f_2') = \max(2, 6) = 6, \qquad f_3' = 6 + 4 = 10. \]
Task 4 then starts at \( s_4' = 10 \) and finishes at \( f_4' = 11 \). Thus
\[ T_M' = \max(f_1', f_2', f_3', f_4') = 11. \]
As predicted by the monotonicity proposition, \( T_M' = 11 \ge 8 = T_M \).
Problem 3 (Discrete-time progress and milestone region). Let \( \mathbf{x}_k \in [0,1]^2 \) evolve by \( \mathbf{x}_{k+1} = \mathbf{x}_k + \mathbf{u} \) with constant \( \mathbf{u} = (0.2, 0.3)^\top \) and \( \mathbf{x}_0 = (0,0)^\top \). Define a milestone region
\[ \mathcal{M} = \left\{ \mathbf{x} \in [0,1]^2 : x_1 \ge 0.6,\; x_2 \ge 0.9 \right\}. \]
Compute the first hitting time \( k_m \).
Solution.
We have \( \mathbf{x}_k = (0.2k,\, 0.3k)^\top \). We require \( 0.2k \ge 0.6 \) and \( 0.3k \ge 0.9 \), i.e.
\[ k \ge \frac{0.6}{0.2} = 3, \qquad k \ge \frac{0.9}{0.3} = 3. \]
The smallest integer satisfying both is \( k_m = 3 \). Indeed, \( \mathbf{x}_3 = (0.6, 0.9)^\top \in \mathcal{M} \).
Problem 4 (Invariance of milestone region). Using the same system as in Problem 3, show explicitly that for all \( k \ge k_m \), the state remains in the milestone region \( \mathcal{M} \).
Solution.
For \( k \ge 3 \), we have \( x_1(k) = 0.2k \ge 0.2 \cdot 3 = 0.6 \) and \( x_2(k) = 0.3k \ge 0.3 \cdot 3 = 0.9 \). Thus
\[ \mathbf{x}_k \in \mathcal{M} \quad \text{for all } k \ge 3, \]
which is a concrete instance of the invariance theorem from Section 3.
Problem 5 (Defining robotics-specific milestone sets). Consider the milestone list in Section 4 for the mobile robot (M1 to M5). Propose a vector \( \mathbf{x}_k \in [0,1]^5 \) and linear inequalities \( \mathbf{C}\mathbf{x}_k \ge \mathbf{d} \) that encode the requirement “M3 reached” and “M5 reached.” Just specify \( \mathbf{C} \) and \( \mathbf{d} \) conceptually.
Solution.
Let each component of \( \mathbf{x}_k \) represent the completeness fraction of the corresponding milestone task group: \( x_1 \) for M1-related tasks, \( x_2 \) for M2-related tasks, etc. Then:
- “M3 reached” means \( x_1 \), \( x_2 \), \( x_3 \) are sufficiently close to 1. One simple encoding is \( \mathbf{C}_3 = \operatorname{diag}(1,1,1,0,0) \), \( \mathbf{d}_3 = (0.9, 0.9, 0.9, 0, 0)^\top \).
- “M5 reached” requires all five groups to be nearly complete, e.g. \( \mathbf{C}_5 = \mathbf{I}_5 \) and \( \mathbf{d}_5 = (0.9, 0.9, 0.9, 0.9, 0.9)^\top \).
These choices make the milestone regions \( \mathcal{M}_3 \) and \( \mathcal{M}_5 \) explicit and compatible with the state-space view of Section 3.
10. Summary
In this lesson we treated implementation milestones for a robotics capstone as mathematical objects tied to task graphs and discrete-time project dynamics. We:
- Modeled tasks using durations, start and finish times, and precedence constraints in a DAG, and defined milestone completion times \( T_m \) as maxima over task finish times.
- Used a discrete-time, linear update \( \mathbf{x}_{k+1} = \mathbf{x}_k + \mathbf{B}\mathbf{u}_k + \mathbf{w}_k \) to capture project progress, and defined milestone regions via linear inequalities \( \mathbf{C}\mathbf{x}_k \ge \mathbf{d} \).
- Proved a monotonicity/invariance result showing that, under nonnegative progress, once a milestone is reached it remains reached.
- Implemented basic milestone computations and checks in Python, C++, Java, and MATLAB/Simulink, with an eye toward embedding these into robotics software stacks.
These concepts help you plan and reason about the implementation path from idea to robot demo, and they prepare you for the next lesson on validation and performance metrics, where we will quantify how good each milestone outcome is.
11. References
- Kelley, J.E. Jr. (1961). Critical-path planning and scheduling. Proceedings of the Eastern Joint Computer Conference, 16, 160–173.
- Pritsker, A.A.B. (1966). GERT: Graphical Evaluation and Review Technique. RAND Corporation Research Memorandum.
- De Meyer, A., Loch, C.H., & Pich, M.T. (2002). Managing project uncertainty: From variation to chaos. Sloan Management Review, 43(2), 60–67.
- Shenhar, A.J., & Dvir, D. (1996). Toward a typological theory of project management. Research Policy, 25(4), 607–632.
- Blanke, M., Kinnaert, M., Lunze, J., & Staroswiecki, M. (2006). Diagnosis and Fault-Tolerant Control. Springer. (Chapters on system-level design and fault handling provide theoretical grounding for safety-related milestones.)
- Siciliano, B., Sciavicco, L., Villani, L., & Oriolo, G. (2009). Robotics: Modelling, Planning and Control. Springer. (Introductory chapters on robot system architectures and planning provide a theoretical backdrop for project structuring.)