Tasks/Transport & Logistics/Powertrain Control

Switching a traction inverter

Switch a traction inverter above base speed

pmsm_fcs_control Transport & Logistics Powertrain Control
instruction.mdthis is what the agent is given

A permanent-magnet synchronous machine is spinning on a dynamometer at a fixed speed, fed from a two-level six-transistor inverter. Once every switching period you choose which of the eight switching states the inverter holds for that period — one bit per leg, upper device or lower device — and your job is to make the machine's current follow the command it is given while switching as little as you can get away with.

Hard Constraints

  • You are called one period at a time and never see a reference before it is commanded. The episode is not handed to you in advance and there is no way to ask for it; the grader holds it and streams it. A controller that tries to plan the whole episode up front has nothing to plan against.
  • You are not told the machine's true parameters. The one on the dynamometer has warmed up and aged away from the nameplate, and the resistance, the magnet flux and both inductances are all somewhere else.
  • You are not told when the command will next change.
  • Exceeding the current limit destroys the inverter: if |i_dq| ever exceeds i_lim the episode stops there and is charged a flat 5.0.
  • Budget: 600 s of wall clock per episode, covering make_controller and every call to act. That is generous on purpose: there is room to do real work per period, and room to precompute whatever you like before the episode starts.
  • There is no way to break a rule. A returned value that is not one of the eight switching states is read as state 0, and the machine does what the physics says. Time is the only currency.

What You Have

methods/main/solver.py     a working controller -- a real one, not a stub;
                           read it before you replace it
selfcheck.py               a handful of episodes, prints your cost
core.py, model.py          the machine model and the episode loop
episodes.py                how command trajectories and parameter drift are drawn
operating_points.json      the speed / command operating points
python selfcheck.py
python selfcheck.py --solver methods/main/solver.py

selfcheck.py draws its own command trajectories and its own parameter drift every time you run it, from the same distribution the graded episodes come from. The number it prints will therefore move a little from run to run. That is the point: there is no fixed set of episodes to tune against, and a controller that only works on the ones you happened to see is not worth having.

make_controller is called once, before the episode starts, with a dictionary describing the setup:

key meaning
u_sup DC link voltage, V
omega shaft speed, rad/s, constant for the episode
periods how many switching decisions the episode lasts
tau simulation step, s
k_sub simulation steps per switching period
i_lim current magnitude that destroys the inverter, A
i_nom rated current, A
w_track, w_sw the two weights in the cost below
nameplate p, l_d, l_q, r_s, psi_p — the catalogue machine

act is then called once per switching period with what a drive can actually measure at that instant:

key meaning
k period index
i_sd, i_sq measured current in the rotor frame, A
epsilon rotor angle, rad
omega, u_sup as above
i_sd_ref, i_sq_ref the current commanded for this period
last_action the state applied in the previous period, or −1

The speeds are above the machine's base speed, in field weakening. i_sd_ref comes from an outer loop that has already chosen how hard to weaken the field: it is set so the voltage needed to hold the commanded current in the steady state is between 0.86 and 1.16 times the radius of the largest circle that fits inside the voltage hexagon. Some commands are therefore comfortably reachable, and some are not reachable at all — the best you can do there is get as close as the hexagon allows and stay there.

What You Submit

One function:

def make_controller(spec):
    ...
    return act        # act(obs) -> int in 0..7

How It Is Judged

Per episode:

cost = w_track * mean over simulation steps of |i_dq − i_dq_ref| / i_lim
     + w_sw    * (total leg transitions) / periods

The first term is charged at every simulation step, not once per switching period, so the ripple inside a period is counted. The second counts how many of the three legs change state from one period to the next — that is what heats the transistors. A destroyed inverter is charged a flat 5.0 and ends the episode.

Your score is the mean cost over the graded episodes, and lower is better. The graded episodes are drawn from the same distribution selfcheck.py samples from, but they are not the ones you can see.

Metric

mean weighted episode cost over the hidden episodes · lower is better

w_track * mean |i_dq - i_dq_ref|/i_lim per sim step + w_sw * leg transitions / periods; a limit violation costs 5.0

anchorvisible setheld-outreward
Bshipped one-period FCS-MPC0.1329400.1575630.00
Rhuman reference method0.1066700.1214340.30
Sbest expert round observed0.0817600.0840070.60
Uconvex-relaxation bound0.0275801.00
normalisation
m >= B0
B > m >= R0.3 * (B - m) / (B - R)
R > m >= S0.3 + 0.3 * (R - m) / (R - S)
S > m > U0.6 + 0.4 * (S - m) / (S - U)
m <= U1

m = this run's held-out metric  ·  B = shipped one-period FCS-MPC  ·  R = human reference method  ·  S = best expert round observed  ·  U = convex-relaxation bound

Linear in the cost, no transform. The 12 episode costs are averaged first and the mean is mapped once; a destroyed episode enters it at 5.0.

Rollouts

111 minwall clock
$14.42spend
22.2Mtokens
17versions, 13 kept
0.075 0.150 0.225 0.300 0.375 $0 $3 $6 $9 $12 cumulative spend on the run development mean cost over 40 episodes, lower is better best expert round observed · visible · 0.081760 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17
keptrevertedno scoreturning point
  1. v1Inherited one-period FCS-MPC on the nameplate model, transition price 0.0020.37772 min · $0.33
  2. v2Start the model at the warm/aged population midpoint: r_s 1.15x, psi_p 0.95x0.380063 min · $0.52
  3. v3Bounded online least-squares fit of r_s, psi_p, l_d, l_q from one-period stepsIdentify the machine actually on the dyno; the parameters are hidden and have drifted off the nameplate.0.380495 min · $0.79
  4. v4Exhaustive two-period tree, charging all 20 substeps and both transitionsPick a two-period sequence, not one vector: the two destroyed episodes were myopia, not model error.0.109736 min · $0.96
  5. v5Ablate the online fit; population-midpoint model, same two-period tree0.111316 min · $1.08
  6. v6Exhaustive horizon 2 -> 3 periods, 512 sequences0.101036 min · $1.24
  7. v7Exhaustive horizon 3 -> 4 periods, 4096 sequences0.0931610 min · $1.55
  8. v8Exhaustive horizon 4 -> 5 periods, 32768 sequences0.0878419 min · $2.31
  9. v9Transition penalty raised from the inherited 0.002 to the metric's own 0.045 of 10 points only31 min · $3.42
  10. v10First-action-stratified beam, 4096 states, out to horizon 8Buy depth instead of breadth: keep 4096 states stratified by first action and search eight periods ahead.0.0809240 min · $4.20
  11. v11Start the parameter fit after 2 observed transitions instead of 44-episode probe only44 min · $4.95
  12. v12Shorten the beam near the known episode end, so unscored periods are not paid for4-episode probe only45 min · $5.28
  13. v13Shorten it near the expected end of an old reference segment too4-episode probe only50 min · $5.93
  14. v14Beam horizon 8 -> 12, about half an electrical revolution at these speeds0.080672 min · $8.44
  15. v15Safety filter: drop first actions whose 10 substeps come within 2 A of i_limholdout seeds only90 min · $10.76
  16. v16Docstring and dead-import cleanup; controller path unchangednot re-measured110 min · $13.67
  17. v17Removed the stray bytecode file; main/ holds solver.py onlynot re-measured110 min · $14.18

v1-v8 cost $2.31 in 19 min. v15-v17 cost $5.74 more for a safety filter, a holdout check and two cleanups, with no measured change.

On the hidden set

held-out metricreward
shipped one-period FCS-MPC0.1575630.00
human reference method0.1214340.30
best expert round observed0.0840070.60
convex-relaxation bound0.0275801.00
this run0.0850320.5918