@inventure71/paddockjs 0.3.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -0
- package/INSTALL_AND_UPDATE.md +5 -2
- package/README.md +328 -13
- package/docs/architecture.md +150 -41
- package/docs/concepts.md +22 -3
- package/docs/custom_model_controller.md +329 -0
- package/docs/data_contract.md +474 -34
- package/docs/data_model.md +1 -1
- package/docs/index.md +1 -0
- package/docs/learnings.md +23 -1
- package/docs/rules.md +169 -21
- package/docs/system_specs.md +56 -20
- package/docs/training.md +369 -25
- package/examples/python/README.md +62 -0
- package/examples/python/base_policy_server.py +191 -0
- package/examples/python/requirements.txt +2 -0
- package/examples/train-basic-policy.mjs +5 -6
- package/examples/trainingData.mjs +133 -0
- package/package.json +15 -4
- package/src/api/PaddockSimulatorController.js +36 -2
- package/src/app/BrowserExpertAdapter.js +367 -9
- package/src/app/F1SimulatorApp.js +615 -940
- package/src/app/banners/bannerFormatters.js +55 -0
- package/src/app/banners/raceDataBannerController.js +141 -0
- package/src/app/banners/stewardMessageController.js +99 -0
- package/src/app/camera/cameraConstants.js +21 -0
- package/src/app/camera/cameraController.js +265 -0
- package/src/app/camera/cameraGeometry.js +176 -0
- package/src/app/domBindings.js +14 -1
- package/src/app/readouts/carOverviewRenderer.js +109 -0
- package/src/app/readouts/controlStateRenderer.js +37 -0
- package/src/app/readouts/raceStatusRenderer.js +133 -0
- package/src/app/readouts/readoutFormatters.js +58 -0
- package/src/app/readouts/telemetryRenderer.js +113 -0
- package/src/app/readouts/timingTowerRenderer.js +177 -0
- package/src/app/rendering/appAssets.js +38 -0
- package/src/app/rendering/carRenderer.js +286 -0
- package/src/app/rendering/displayUtils.js +22 -0
- package/src/app/rendering/drsTrailRenderer.js +90 -0
- package/src/app/rendering/expertSensorRenderer.js +126 -0
- package/src/app/rendering/pitLaneStatusRenderer.js +50 -0
- package/src/app/rendering/replayGhostRenderer.js +124 -0
- package/src/app/rendering/trackRenderer.js +25 -0
- package/src/app/runtime/frameLoop.js +66 -0
- package/src/app/runtime/runtimeTiming.js +34 -0
- package/src/app/runtime/runtimeVisibility.js +45 -0
- package/src/config/defaultOptions.js +24 -4
- package/src/environment/actions.js +38 -7
- package/src/environment/controllerLoop.js +246 -0
- package/src/environment/episode.js +86 -2
- package/src/environment/evaluation.js +206 -0
- package/src/environment/index.d.ts +638 -6
- package/src/environment/index.js +14 -0
- package/src/environment/metrics.js +159 -0
- package/src/environment/observationOptions.js +33 -0
- package/src/environment/observations.js +500 -43
- package/src/environment/options.js +41 -2
- package/src/environment/recorder.js +28 -0
- package/src/environment/runtime.js +260 -24
- package/src/environment/scenarios.js +249 -0
- package/src/environment/sensors/bodySenses.js +23 -0
- package/src/environment/sensors/boundarySenses.js +17 -0
- package/src/environment/sensors/carRays.js +75 -0
- package/src/environment/sensors/contactSenses.js +32 -0
- package/src/environment/sensors/index.js +157 -0
- package/src/environment/sensors/indexedRayBands.js +103 -0
- package/src/environment/sensors/nearbyCars.js +52 -0
- package/src/environment/sensors/opponentRadar.js +65 -0
- package/src/environment/sensors/rayConfig.js +110 -0
- package/src/environment/sensors/rayDefaults.js +6 -0
- package/src/environment/sensors/rayGeometry.js +60 -0
- package/src/environment/sensors/rayGuards.js +19 -0
- package/src/environment/sensors/sensorTargets.js +60 -0
- package/src/environment/sensors/surfaceRays.js +296 -0
- package/src/environment/sensors/trackRays.js +261 -0
- package/src/environment/sensors.js +1 -257
- package/src/environment/specs.js +188 -9
- package/src/environment/workerProtocol.js +64 -0
- package/src/index.d.ts +533 -7
- package/src/index.js +26 -0
- package/src/rendering/proceduralTrackAsset.js +20 -510
- package/src/rendering/renderSnapshot.js +38 -12
- package/src/rendering/track/finishLineRenderer.js +34 -0
- package/src/rendering/track/gridRenderer.js +47 -0
- package/src/rendering/track/offsetStrokeSafety.js +136 -0
- package/src/rendering/track/pitLaneRenderer.js +248 -0
- package/src/rendering/track/trackMaterialRenderer.js +326 -0
- package/src/rendering/track/trackRenderConstants.js +38 -0
- package/src/rendering/track/trackRenderGeometry.js +94 -0
- package/src/simulation/collisionGeometry.js +214 -0
- package/src/simulation/driver/driverControlConstants.js +28 -0
- package/src/simulation/driver/driverController.js +47 -0
- package/src/simulation/driver/driverInput.js +28 -0
- package/src/simulation/driver/driverMath.js +6 -0
- package/src/simulation/driver/driverPersonality.js +25 -0
- package/src/simulation/driver/edgeRecovery.js +48 -0
- package/src/simulation/driver/racingControls.js +399 -0
- package/src/simulation/driver/racingLinePlan.js +166 -0
- package/src/simulation/driver/recoveryDynamics.js +38 -0
- package/src/simulation/driver/rejoinControls.js +213 -0
- package/src/simulation/driver/safetyCarControls.js +35 -0
- package/src/simulation/driver/trafficScan.js +51 -0
- package/src/simulation/driverController.js +4 -253
- package/src/simulation/participants/participantInteractions.js +120 -0
- package/src/simulation/pit/pitFlow.js +234 -0
- package/src/simulation/pit/pitIntent.js +92 -0
- package/src/simulation/pit/pitOccupancy.js +47 -0
- package/src/simulation/pit/pitPenaltyService.js +71 -0
- package/src/simulation/pit/pitQueue.js +55 -0
- package/src/simulation/pit/pitRouteMovement.js +160 -0
- package/src/simulation/pit/pitRouting.js +319 -0
- package/src/simulation/pit/pitService.js +41 -0
- package/src/simulation/pit/pitServiceConstants.js +21 -0
- package/src/simulation/pit/pitServiceProfile.js +45 -0
- package/src/simulation/pit/pitSnapshots.js +61 -0
- package/src/simulation/pit/pitState.js +113 -0
- package/src/simulation/pit/pitTireService.js +92 -0
- package/src/simulation/race/classification.js +104 -0
- package/src/simulation/race/gridPenalties.js +37 -0
- package/src/simulation/race/raceControlState.js +38 -0
- package/src/simulation/race/raceDistance.js +32 -0
- package/src/simulation/race/raceFinish.js +65 -0
- package/src/simulation/race/raceLifecycle.js +71 -0
- package/src/simulation/race/raceOrder.js +113 -0
- package/src/simulation/race/raceProgress.js +124 -0
- package/src/simulation/race/raceSetup.js +93 -0
- package/src/simulation/race/raceStep.js +80 -0
- package/src/simulation/race/redFlag.js +15 -0
- package/src/simulation/race/retirements.js +44 -0
- package/src/simulation/race/safetyCar.js +86 -0
- package/src/simulation/race/stalledDnf.js +86 -0
- package/src/simulation/raceSimulation.js +386 -1012
- package/src/simulation/replay/replayGhosts.js +128 -0
- package/src/simulation/rules/collisionSteward.js +69 -0
- package/src/simulation/rules/moduleConfig.js +72 -0
- package/src/simulation/rules/penaltyConfig.js +99 -0
- package/src/simulation/rules/penaltyEvents.js +18 -0
- package/src/simulation/rules/penaltyLedger.js +153 -0
- package/src/simulation/rules/penaltyStats.js +26 -0
- package/src/simulation/rules/pitLaneSpeedingSteward.js +71 -0
- package/src/simulation/rules/ruleConfigMerge.js +41 -0
- package/src/simulation/rules/ruleDefaults.js +85 -0
- package/src/simulation/rules/rulesReview.js +141 -0
- package/src/simulation/rules/rulesetPresets.js +47 -0
- package/src/simulation/rules/tireRequirementSteward.js +29 -0
- package/src/simulation/rules/trackLimitsSteward.js +85 -0
- package/src/simulation/rulesConfig.js +34 -0
- package/src/simulation/snapshots/raceSnapshots.js +192 -0
- package/src/simulation/timing/drsTiming.js +87 -0
- package/src/simulation/timing/gapEstimation.js +38 -0
- package/src/simulation/timing/lapTelemetry.js +182 -0
- package/src/simulation/timing/raceTiming.js +15 -0
- package/src/simulation/timing/sectorPerformance.js +60 -0
- package/src/simulation/timing/timingConstants.js +5 -0
- package/src/simulation/timing/timingHistory.js +46 -0
- package/src/simulation/timing/timingLines.js +79 -0
- package/src/simulation/track/centerlineSpline.js +59 -0
- package/src/simulation/track/drsZones.js +67 -0
- package/src/simulation/track/pitLaneAccess.js +103 -0
- package/src/simulation/track/pitLaneLayout.js +395 -0
- package/src/simulation/track/pitLaneState.js +160 -0
- package/src/simulation/track/proceduralCenterline.js +237 -0
- package/src/simulation/track/sampleGeometry.js +49 -0
- package/src/simulation/track/spatialQueries.js +154 -0
- package/src/simulation/track/startStraight.js +141 -0
- package/src/simulation/track/trackConstants.js +109 -0
- package/src/simulation/track/trackGenerationOptions.js +283 -0
- package/src/simulation/track/trackMath.js +241 -0
- package/src/simulation/track/trackModel.js +240 -0
- package/src/simulation/track/trackQueryIndex.js +945 -0
- package/src/simulation/track/trackSectors.js +19 -0
- package/src/simulation/track/trackStatePolicy.js +24 -0
- package/src/simulation/track/trackValidation.js +98 -0
- package/src/simulation/trackModel.js +1 -628
- package/src/simulation/units.js +6 -4
- package/src/simulation/vehicle/contactResolution.js +116 -0
- package/src/simulation/vehicle/mainTrackWheelSurface.js +82 -0
- package/src/simulation/vehicle/pitWheelSurface.js +69 -0
- package/src/simulation/vehicle/runoffResponse.js +88 -0
- package/src/simulation/vehicle/surfacePriority.js +28 -0
- package/src/simulation/vehicle/vehicleGeometry.js +247 -0
- package/src/simulation/vehicle/vehiclePhysics.js +363 -0
- package/src/simulation/vehicle/vehicleSnapshots.js +316 -0
- package/src/simulation/vehicle/vehicleState.js +186 -0
- package/src/simulation/vehicle/wheelSurface.js +150 -0
- package/src/simulation/vehicleGeometry.js +1 -0
- package/src/simulation/vehiclePhysics.js +1 -104
- package/src/simulation/warmup/runtimeWarmup.js +224 -0
- package/src/simulation/wheelSurface.js +1 -0
- package/src/styles.css +342 -42
- package/src/ui/bannerTemplates.js +69 -0
- package/src/ui/cameraControlsTemplate.js +20 -0
- package/src/ui/carOverviewTemplate.js +33 -0
- package/src/ui/componentTemplates.js +20 -426
- package/src/ui/raceCanvasTemplate.js +69 -0
- package/src/ui/raceControlStatusBanner.js +39 -0
- package/src/ui/raceControlsTemplate.js +36 -0
- package/src/ui/shellTemplate.js +6 -5
- package/src/ui/telemetryDrawerTemplate.js +55 -0
- package/src/ui/telemetryTemplates.js +136 -0
- package/src/ui/templateUtils.js +23 -0
- package/src/ui/timingTowerTemplate.js +34 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# Custom Model Controller Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to connect a trained driver model to PaddockJS without adding model-specific code to PaddockJS itself.
|
|
4
|
+
|
|
5
|
+
PaddockJS does not load weights, run neural-network layers, choose rewards, store checkpoints, or own a training algorithm. Your integration module owns those pieces. PaddockJS only provides the simulator runtime, observations, cached specs, action validation, stable driver ordering, and normal physics stepping.
|
|
6
|
+
|
|
7
|
+
## What You Build
|
|
8
|
+
|
|
9
|
+
Build a small controller module around your model:
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
export function createMyDriverController({ modelUrl }) {
|
|
13
|
+
return {
|
|
14
|
+
async init(ctx) {
|
|
15
|
+
this.model = await loadMyModel(modelUrl, ctx.observationSpec);
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
async reset(ctx) {
|
|
19
|
+
this.hiddenState = createHiddenState(ctx.controlledDrivers.length);
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
async decideBatch(ctx) {
|
|
23
|
+
const vectors = ctx.orderedObservations.map((item) => item.vector);
|
|
24
|
+
const outputs = await this.model.predictBatch(vectors, this.hiddenState);
|
|
25
|
+
|
|
26
|
+
return Object.fromEntries(ctx.controlledDrivers.map((driverId, index) => [
|
|
27
|
+
driverId,
|
|
28
|
+
actionFromModelOutput(outputs[index]),
|
|
29
|
+
]));
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
onStep(ctx) {
|
|
33
|
+
recordMyStats(ctx.metrics, ctx.actions);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The required method is `decideBatch(ctx)`. The other methods are optional:
|
|
40
|
+
|
|
41
|
+
- `init(ctx)`: load model files, allocate inference runtime, inspect specs.
|
|
42
|
+
- `reset(ctx)`: reset hidden state, memory, per-driver buffers, or episode stats.
|
|
43
|
+
- `decideBatch(ctx)`: run one batched inference call and return actions for all controlled drivers.
|
|
44
|
+
- `onStep(ctx)`: log metrics, rewards, traces, memory activity, or debugging data.
|
|
45
|
+
|
|
46
|
+
## Controller Context
|
|
47
|
+
|
|
48
|
+
`decideBatch(ctx)` receives the same shape in browser visualization and JavaScript training-style loops:
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
{
|
|
52
|
+
controlledDrivers,
|
|
53
|
+
orderedObservations,
|
|
54
|
+
observation,
|
|
55
|
+
metrics,
|
|
56
|
+
events,
|
|
57
|
+
previousActions,
|
|
58
|
+
orderedPreviousActions,
|
|
59
|
+
actionSpec,
|
|
60
|
+
observationSpec,
|
|
61
|
+
policyStep,
|
|
62
|
+
runtimeStep,
|
|
63
|
+
actionRepeat,
|
|
64
|
+
resetDriverIds,
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use `ctx.controlledDrivers` and `ctx.orderedObservations` for batching. Their order is stable for the runtime, so index `0` in your tensor batch belongs to `ctx.controlledDrivers[0]`.
|
|
69
|
+
|
|
70
|
+
Specs are cached by the controller loop. Do not call `getObservationSpec()` or `getActionSpec()` every frame.
|
|
71
|
+
|
|
72
|
+
## Actions
|
|
73
|
+
|
|
74
|
+
Return normalized physical controls:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
{
|
|
78
|
+
[driverId]: {
|
|
79
|
+
steering: -1, // max left
|
|
80
|
+
throttle: 1,
|
|
81
|
+
brake: 0,
|
|
82
|
+
pitIntent: 0,
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`steering` is an absolute steering-wheel target:
|
|
88
|
+
|
|
89
|
+
- `-1`: maximum left
|
|
90
|
+
- `0`: centered
|
|
91
|
+
- `1`: maximum right
|
|
92
|
+
|
|
93
|
+
The vehicle physics still rate-limits the wheel motion. The controller cannot snap car position, heading, speed, or steering state.
|
|
94
|
+
|
|
95
|
+
If your model outputs `accel` in `[-1, 1]`, map it like this:
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
function actionFromModelOutput(output) {
|
|
99
|
+
const steering = clamp(output.steering, -1, 1);
|
|
100
|
+
const accel = clamp(output.accel, -1, 1);
|
|
101
|
+
return {
|
|
102
|
+
steering,
|
|
103
|
+
throttle: Math.max(accel, 0),
|
|
104
|
+
brake: Math.max(-accel, 0),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Efficient Observation Setup
|
|
110
|
+
|
|
111
|
+
For high-throughput model inference, prefer compact vectors:
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
observation: {
|
|
115
|
+
profile: 'physical-driver',
|
|
116
|
+
output: 'vector',
|
|
117
|
+
includeSchema: false,
|
|
118
|
+
vectorType: 'float32',
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This gives each observation a `Float32Array` vector and avoids per-frame object/schema allocation.
|
|
123
|
+
|
|
124
|
+
For visual debugging, use `output: 'full'` or `output: 'object'` so you can display human-readable senses. Policy Runner uses the active observation values; it should not recompute sharper or different senses than the model receives.
|
|
125
|
+
|
|
126
|
+
## Headless Runtime
|
|
127
|
+
|
|
128
|
+
Use the browser-free environment when training or evaluating outside a mounted page:
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
import {
|
|
132
|
+
createPaddockDriverControllerLoop,
|
|
133
|
+
createPaddockEnvironment,
|
|
134
|
+
} from '@inventure71/paddockjs/environment';
|
|
135
|
+
|
|
136
|
+
const env = createPaddockEnvironment({
|
|
137
|
+
drivers,
|
|
138
|
+
entries,
|
|
139
|
+
controlledDrivers: ['agent-1', 'agent-2'],
|
|
140
|
+
physicsMode: 'simulator',
|
|
141
|
+
participantInteractions: { defaultProfile: 'batch-training' },
|
|
142
|
+
observation: {
|
|
143
|
+
profile: 'physical-driver',
|
|
144
|
+
output: 'vector',
|
|
145
|
+
includeSchema: false,
|
|
146
|
+
vectorType: 'float32',
|
|
147
|
+
},
|
|
148
|
+
result: {
|
|
149
|
+
stateOutput: 'none',
|
|
150
|
+
resetDriversObservationScope: 'reset',
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const loop = createPaddockDriverControllerLoop({
|
|
155
|
+
runtime: env,
|
|
156
|
+
controller: createMyDriverController({ modelUrl: './model.onnx' }),
|
|
157
|
+
actionRepeat: 4,
|
|
158
|
+
mode: 'headless-eval',
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
let result = await loop.reset();
|
|
162
|
+
while (!result.done) {
|
|
163
|
+
result = await loop.step();
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Browser Runtime
|
|
168
|
+
|
|
169
|
+
Use browser expert mode when you want to watch the model drive:
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
import {
|
|
173
|
+
createPaddockDriverControllerLoop,
|
|
174
|
+
mountF1Simulator,
|
|
175
|
+
} from '@inventure71/paddockjs';
|
|
176
|
+
|
|
177
|
+
const simulator = await mountF1Simulator(root, {
|
|
178
|
+
drivers,
|
|
179
|
+
entries,
|
|
180
|
+
physicsMode: 'simulator',
|
|
181
|
+
expert: {
|
|
182
|
+
enabled: true,
|
|
183
|
+
controlledDrivers: ['agent-1'],
|
|
184
|
+
frameSkip: 1,
|
|
185
|
+
visualizeSensors: { rays: true, drivers: 'selected' },
|
|
186
|
+
},
|
|
187
|
+
observation: {
|
|
188
|
+
profile: 'physical-driver',
|
|
189
|
+
output: 'full',
|
|
190
|
+
includeSchema: true,
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const loop = createPaddockDriverControllerLoop({
|
|
195
|
+
runtime: simulator.expert,
|
|
196
|
+
controller: createMyDriverController({ modelUrl: '/models/my-driver.json' }),
|
|
197
|
+
actionRepeat: 4,
|
|
198
|
+
mode: 'browser',
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
await loop.reset();
|
|
202
|
+
loop.start();
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`actionRepeat: 4` means one model decision every four simulator frames. At a 60 FPS visual rate, that is a 15 Hz control cadence.
|
|
206
|
+
|
|
207
|
+
## Runtime Modes (Single Architecture)
|
|
208
|
+
|
|
209
|
+
Use one expert runtime with two explicit modes:
|
|
210
|
+
|
|
211
|
+
1. Local expert mode (default): browser owns stepping through `loop.step()` / `loop.start()`.
|
|
212
|
+
2. External render-only mode: browser does not step physics and only renders authoritative external frames.
|
|
213
|
+
|
|
214
|
+
Switching modes is done by attaching or detaching the external renderer source on `simulator.expert`.
|
|
215
|
+
|
|
216
|
+
## External Render-Only Mode
|
|
217
|
+
|
|
218
|
+
Browser expert runtime can also run in render-only mode from external frames:
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
const source = {
|
|
222
|
+
subscribe(onFrame) {
|
|
223
|
+
const socket = new WebSocket('ws://127.0.0.1:8787/preview');
|
|
224
|
+
socket.addEventListener('message', (event) => {
|
|
225
|
+
const packet = JSON.parse(event.data);
|
|
226
|
+
if (packet?.type !== 'preview:snapshot') return;
|
|
227
|
+
onFrame({
|
|
228
|
+
snapshot: packet.snapshot,
|
|
229
|
+
observation: packet.observation,
|
|
230
|
+
meta: packet.meta,
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
return () => socket.close();
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
simulator.expert.attachExternalRenderer(source);
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Policy Runner live preview mode can also poll an HTTP endpoint that returns the same frame packet shape (for example `http://127.0.0.1:8787/preview/frame`) when push WebSockets are not available in your training process.
|
|
241
|
+
|
|
242
|
+
When external renderer mode is attached:
|
|
243
|
+
|
|
244
|
+
- local expert `step()` is blocked
|
|
245
|
+
- local expert `resetDrivers()` is blocked
|
|
246
|
+
- local expert `reset()` is blocked in strict mode
|
|
247
|
+
|
|
248
|
+
Detach before returning to browser-owned stepping:
|
|
249
|
+
|
|
250
|
+
```js
|
|
251
|
+
simulator.expert.detachExternalRenderer();
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
This keeps exactness for live preview visualization: the browser renders authoritative external frames and does not advance local physics.
|
|
255
|
+
|
|
256
|
+
## Python + JS Environment Bridge (Recommended Flow)
|
|
257
|
+
|
|
258
|
+
When your training orchestration is Python but simulation state comes from the JS environment:
|
|
259
|
+
|
|
260
|
+
1. Keep the simulator loop authoritative in your JS environment bridge.
|
|
261
|
+
2. After each `reset/step/resetDrivers`, publish `{ snapshot, observation, meta }` through your transport.
|
|
262
|
+
3. In browser Policy Runner, use `Live preview stream` and connect that transport URL.
|
|
263
|
+
4. While attached, do not issue local browser step/reset calls.
|
|
264
|
+
|
|
265
|
+
The provided Python base server is transport/inference scaffolding only. It does not own training logic, reward design, checkpoint selection, or simulation stepping.
|
|
266
|
+
|
|
267
|
+
## Per-Driver Resets
|
|
268
|
+
|
|
269
|
+
When the runtime supports `resetDrivers()`, the controller loop forwards selected-driver resets and calls `controller.reset(ctx)` with `ctx.resetDriverIds`.
|
|
270
|
+
|
|
271
|
+
Use this to clear only the affected model state:
|
|
272
|
+
|
|
273
|
+
```js
|
|
274
|
+
reset(ctx) {
|
|
275
|
+
for (const driverId of ctx.resetDriverIds) {
|
|
276
|
+
this.hiddenByDriver.delete(driverId);
|
|
277
|
+
this.memoryByDriver.delete(driverId);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
If `ctx.resetDriverIds` contains every controlled driver, treat it as a full episode reset.
|
|
283
|
+
|
|
284
|
+
## Model Runtime Choices
|
|
285
|
+
|
|
286
|
+
The controller can use any inference backend:
|
|
287
|
+
|
|
288
|
+
- ONNX Runtime Web
|
|
289
|
+
- TensorFlow.js
|
|
290
|
+
- WebGPU
|
|
291
|
+
- Web Worker
|
|
292
|
+
- WebAssembly
|
|
293
|
+
- a remote HTTP inference service
|
|
294
|
+
- plain JavaScript heuristics
|
|
295
|
+
- a custom checkpoint format
|
|
296
|
+
|
|
297
|
+
Keep that code in your app or local experiment. Do not add checkpoint-format parsing or model-runtime dependencies to PaddockJS core unless the package explicitly decides to support that runtime as a generic simulator feature.
|
|
298
|
+
|
|
299
|
+
## Safety Checklist
|
|
300
|
+
|
|
301
|
+
Before trusting a controller:
|
|
302
|
+
|
|
303
|
+
- Confirm it returns one action per controlled driver.
|
|
304
|
+
- Confirm it uses `ctx.orderedObservations` instead of relying on object key order.
|
|
305
|
+
- Confirm it never mutates `ctx.result`, simulator state, car position, heading, speed, or hidden physics fields.
|
|
306
|
+
- Confirm `throttle` and `brake` stay in `[0, 1]`.
|
|
307
|
+
- Confirm `steering` stays in `[-1, 1]`.
|
|
308
|
+
- Confirm hidden state resets for `ctx.resetDriverIds`.
|
|
309
|
+
- Confirm browser visualization and headless evaluation use the same controller code.
|
|
310
|
+
- Confirm any reward/stat logic lives in `onStep()` or external training code, not in PaddockJS core.
|
|
311
|
+
|
|
312
|
+
## Minimal Debug Controller
|
|
313
|
+
|
|
314
|
+
Use this controller to verify your runtime wiring before connecting a real model:
|
|
315
|
+
|
|
316
|
+
```js
|
|
317
|
+
export function createStraightLineController() {
|
|
318
|
+
return {
|
|
319
|
+
decideBatch(ctx) {
|
|
320
|
+
return Object.fromEntries(ctx.controlledDrivers.map((driverId) => [
|
|
321
|
+
driverId,
|
|
322
|
+
{ steering: 0, throttle: 0.35, brake: 0 },
|
|
323
|
+
]));
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
If this controller drives physically and your model does not, the integration loop is probably correct and the issue is in model inputs, normalization, hidden state, or action decoding.
|