Project: the delivery robot

A small project taken through analysis, design, implementation, testing and evaluation, the way the NEA is written up.

A14.9Software development, law and ethicsA level60 min

Do this lesson in the simulator

Every A level in computer science ends with a programming project, the non-exam assessment (NEA), worth 20% of the qualification. You choose a problem, usually for a real user, and take it through analysis, design, implementation, testing and evaluation, writing up each stage. This lesson works a small robot project through those stages the way a project write-up does, then you build it.

The robot here is small on purpose. A real NEA is much larger and needs more complex techniques; what carries over is the shape: every stage follows from the one before, and the success criteria written in analysis are what testing and evaluation check.

1. Analysis

The problem. Staff carry parcels from the office to a delivery bay beside the charging wall. The client, the site manager, wants the robot to do it.

Stakeholders and research. The site manager (safety, charging), office staff (sending), and teachers (collecting). An interview with the site manager established that the robot must never touch the wall, must park where the collection shelf can reach it, and must make its arrival obvious to someone who is not looking at it.

Success criteria, numbered and measurable:

No. Criterion Evidence
SC1 Stops with a gap of 17 to 23 cm to the wall before moving across to the bay the gap read by distance()
SC2 Is inside the delivery bay within 30 s of starting the time from clock()
SC3 On arrival, LED green and an 880 Hz note observed, and the task's check
SC4 Never touches the wall observed

Limitations agreed with the client: one fixed route, no people in the way (the obstacle behaviour of lesson A14.8 is for a later iteration).

2. Design

Decomposition, as a structure chart written as an outline:

delivery
    run_unit_tests      tests gap_ok before the robot moves
    approach(target)    returns the gap once parked
    sidestep(cm)
    signal
    evaluate(gap, elapsed)
        gap_ok(gap)     returns True if the gap meets SC1

Algorithms. The approach must be fast but must not overshoot. Driving in large steps until the wall is near, then small ones, gives both:

SUBROUTINE approach(target)
    WHILE distance() > target + 10
        drive forward 5 cm at speed 60
    ENDWHILE
    WHILE distance() > target + 1
        drive forward 1 cm at speed 30
    ENDWHILE
    RETURN distance()
ENDSUBROUTINE

Test plan. Designed now, from the criteria. gap_ok is a pure function, so it can be unit tested with boundary data before the robot ever moves:

Test Data Kind Expected
gap_ok below the band 16.9 boundary False
gap_ok lowest in band 17 boundary True
gap_ok middle 20 normal True
gap_ok highest in band 23 boundary True
gap_ok above the band 23.1 boundary False
full run robot from the start system SC1 to SC4 all met

3. Implementation

Built iteratively, one module at a time, each tested before the next was started, and each working step committed to version control with a message:

  1. gap_ok and its unit tests. Commit: "gap check with boundary tests".
  2. approach, run on its own and the gap printed. The first version used 5 cm steps all the way and stopped at 16 cm: a failed test, fixed by adding the slow final stage. Commit: "two-speed approach".
  3. sidestep and signal. Commit: "move to the bay and signal".
  4. evaluate, which prints each criterion as met or not met. Commit: "self-evaluation".

In a write-up, each step is evidenced with the code, the test output, and a note of what went wrong and how it was fixed. That record of problems solved is valued, not hidden.

Good implementation also means the things examiners look for in code: meaningful names, constants for the limits (GAP_LOW, GAP_HIGH), subroutines with clear interfaces, comments where the reason is not obvious, and validation where data comes in.

4. Testing

Iterative testing happened in step 3 above. Post-development testing runs the test plan on the finished program:

# unit tests for gap_ok, the way the finished program runs them
GAP_LOW, GAP_HIGH = 17, 23

def gap_ok(gap):
    return GAP_LOW <= gap <= GAP_HIGH

tests = [(16.9, False), (17, True), (20, True), (23, True), (23.1, False)]
passed = 0
for value, expected in tests:
    ok = gap_ok(value) == expected
    passed += ok
    print(f"test gap_ok({value}): {'pass' if ok else 'FAIL'}")
print(f"unit tests: {passed} of {len(tests)} passed")

Run this in the simulator

For the NEA, final testing should also include the user: the site manager watching the robot deliver and confirming the criteria, a form of acceptance testing.

5. Evaluation

The evaluation goes back to each success criterion, says whether it was met, and points to the evidence. Where one was not fully met, it says why and what would fix it. It also considers user feedback, and how the system could be maintained and extended:

SC1 was met: the robot parked with a gap of 21 cm (test output, run 3). SC2 was met, arriving after 20 s. SC3 and SC4 were met. The site manager asked for the robot to wait if a person is in the corridor; this was out of scope for version 1 and is planned for version 2, using the decision rules of lesson A14.8. Because the approach and the evaluation are separate subroutines, that change will not affect the criteria checks. A limitation is that the route is fixed: a new bay would need code changes rather than a setting, so a future version should read the route from a file.

That paragraph is what top-band evaluation looks like: every claim tied to a criterion and to evidence, honest about limits, and forward-looking.

Law and ethics in the project

A real project should consider the issues of this module too. If the robot logged who collected each parcel, the analysis would note the data protection principles (lesson A14.7), and the design would include retention and pseudonymisation. If it used a camera near students, the evaluation should discuss consent and bias (lesson A14.8).

Task: the delivery robot

Build the designed program. The robot starts facing the charging wall, 71 cm away. The delivery bay is to the right of where it parks.

Write these subroutines, and use them in this order:

  • gap_ok(gap): gap is a number of cm; returns True if it is from GAP_LOW to GAP_HIGH inclusive, otherwise False.
  • run_unit_tests(): tests gap_ok with exactly these five values, in this order: 16.9 (expected False), 17 (True), 20 (True), 23 (True) and 23.1 (False). For each, print test gap_ok(<value>): pass or test gap_ok(<value>): FAIL, then print unit tests: <passed> of 5 passed.
  • approach(target): target is the gap in cm to park at; drive towards the wall and return the gap read with distance() once stopped. Call it as approach(20).
  • sidestep(cm): move right cm centimetres. Call it as sidestep(30).
  • signal(): LED green and an 880 Hz note.
  • evaluate(gap, elapsed): gap is the value approach returned and elapsed is clock() read after signal(). Print SC1 gap <gap> cm: <result> using gap_ok, SC2 in the bay after <elapsed> s: <result> (met if elapsed is at most TIME_LIMIT), and SC3 signal: met, where each <result> is met or not met. Then print criteria met: <n> of 3, counting the criteria met.

The whole program prints exactly ten lines. SC4 is checked by watching the run, so the program does not print it: do not touch the wall.

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

GAP_LOW, GAP_HIGH = 17, 23     # SC1, in cm
TIME_LIMIT = 30                # SC2, in seconds

def gap_ok(gap):
    pass

Challenges

  1. Add SC5, "never closer than 15 cm to the wall at any time", and a way for the program to measure it while it drives.
  2. Write the evaluation paragraph for your own run, using your printed output as evidence.
  3. Plan version 2 with the site manager's request. Which methodology from lesson A14.2 suits adding it, and why?