Now you are the cruise control. A hidden car drives a hilly 10-minute route, and every half second the simulator calls your function
controller(t, speed, v_set, grade) -> force
with the time t in seconds (0, 0.5, 1, ...), the speedometer reading in m/s, the current speed limit v_set in m/s, and the road's grade from an inclinometer (rise per metre; 0.05 is a 5 % climb). Return the force you want at the wheels, in newtons: positive drives, negative brakes.
What you are up against:
- the engine delivers at most 2200 N and the brakes at most 4000 N: any force outside
-4000..2200is limited to that range; - the car has a mass between 1100 and 1700 kg (passengers and luggage vary; you are not told), air drag of about
0.35to0.5times the square of the air speed, and a rolling resistance of about 1.2 % of its weight; - a steady wind of up to 5 m/s blows with or against the car;
- hills of up to ±8 % pull the car back or push it on: the slope's force is
mass * 9.81 * grade; - the speed limit changes every 50 to 140 seconds, between 14 and 31 m/s;
- the speedometer is noisy (a random error of about 0.1 m/s, rounded to 0.01) and the inclinometer slightly so.
The car starts at the first speed limit. You are judged against a reference speed v_ref that starts at the first limit and, after every half-second step, moves towards the current limit by at most 0.5 m/s going up (a comfortable 1 m/s²) and at most 0.75 m/s going down:
v_ref = min(v_set, v_ref + 0.5) if v_ref < v_set else max(v_set, v_ref - 0.75)
Each test calls run_drive(controller, seed) (it is defined for you, so you can try it with Run), which returns a summary of the drive, for example
{"rms_error": 1.006781, "mean_force_change": 92.971, "worst_over_limit": 0.8672,
"worst_error": 2.2233, "distance_km": 12.53}
for the plain proportional controller lambda t, speed, v_set, grade: 600 * (v_set - speed) on seed 1. Your function may keep state (an integral, a filtered speed) in global variables; t == 0 marks the start of a new drive, so reset your state then.
How this problem is scored
- A test passes when the car is never more than 2 m/s over the speed limit (from 10 s after each change of the limit, so there is time to brake) and the RMS error is at most 2 m/s.
- Its quality is
100 - 35 * rms_error - 0.12 * mean_force_change(between 0 and 100).rms_erroris the root-mean-square ofspeed - v_refover all 1200 steps;mean_force_changeis the average ofabs(F - previous F)in newtons, the jerking of the throttle and brakes that passengers feel and that wastes fuel. Every 0.1 m/s of RMS error costs 3.5 points, every 10 N of average change 1.2 points. - The P controller above scores 54 on seed 1: it never quite reaches the limit uphill, and it passes all the speedometer's noise straight to the engine.
Constraints
- return a number; the result must not depend on the clock, and each call should take well under a millisecond
Goals
- Write a speed controller that a simulator calls once per time step
- Hold a reference speed over hills and wind with a limited engine force
- Balance tracking against how much the force is jerked about