@inventure71/paddockjs 1.0.0 → 2.0.1
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 +58 -0
- package/INSTALL_AND_UPDATE.md +4 -3
- package/README.md +219 -12
- package/docs/architecture.md +126 -45
- package/docs/concepts.md +9 -5
- package/docs/custom_model_controller.md +329 -0
- package/docs/data_contract.md +317 -22
- package/docs/data_model.md +1 -1
- package/docs/index.md +10 -0
- package/docs/learnings.md +22 -0
- package/docs/rules.md +71 -14
- package/docs/sense_contract.md +59 -0
- package/docs/system_specs.md +75 -13
- package/docs/training.md +280 -32
- package/examples/python/README.md +62 -0
- package/examples/python/base_policy_server.py +191 -0
- package/examples/python/requirements.txt +2 -0
- package/package.json +12 -4
- package/src/api/PaddockSimulatorController.js +6 -33
- package/src/app/BrowserExpertAdapter.js +76 -9
- package/src/app/BrowserExpertExternalRenderer.js +303 -0
- package/src/app/F1SimulatorApp.js +291 -1630
- 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 +4 -0
- 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 +20 -0
- package/src/config/restartOptions.js +34 -0
- package/src/environment/actions.js +13 -5
- package/src/environment/controllerLoop.js +261 -0
- package/src/environment/episode.js +98 -2
- package/src/environment/evaluation.js +54 -32
- package/src/environment/index.d.ts +376 -10
- package/src/environment/index.js +1 -0
- package/src/environment/metrics.js +159 -0
- package/src/environment/observationOptions.js +22 -1
- package/src/environment/observationVector.js +200 -0
- package/src/environment/observations.js +199 -141
- package/src/environment/options.js +37 -5
- package/src/environment/recorder.js +13 -6
- package/src/environment/rewards.js +13 -2
- package/src/environment/runtime.js +272 -23
- package/src/environment/scenarios.js +57 -9
- 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 +75 -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 +66 -0
- package/src/environment/sensors/surfaceRays.js +360 -0
- package/src/environment/sensors/trackRays.js +424 -0
- package/src/environment/sensors.js +1 -367
- package/src/environment/specs.js +159 -26
- package/src/environment/workerProtocol.js +13 -1
- package/src/index.d.ts +171 -6
- package/src/index.js +6 -32
- package/src/rendering/proceduralTrackAsset.js +20 -823
- 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 +1 -1
- 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 -619
- package/src/simulation/participants/participantInteractions.js +120 -0
- package/src/simulation/pit/pitFlow.js +239 -0
- package/src/simulation/pit/pitIntent.js +92 -0
- package/src/simulation/pit/pitKinematics.js +24 -0
- package/src/simulation/pit/pitOccupancy.js +47 -0
- package/src/simulation/pit/pitPenaltyService.js +71 -0
- package/src/simulation/pit/pitQueue.js +60 -0
- package/src/simulation/pit/pitRouteMovement.js +167 -0
- package/src/simulation/pit/pitRouting.js +319 -0
- package/src/simulation/pit/pitService.js +43 -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 +94 -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 +67 -0
- package/src/simulation/race/raceOrder.js +113 -0
- package/src/simulation/race/raceProgress.js +129 -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 +92 -0
- package/src/simulation/race/stalledDnf.js +77 -0
- package/src/simulation/raceSimulation.js +231 -2817
- package/src/simulation/replay/replayGhosts.js +128 -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/penaltyStats.js +26 -0
- package/src/simulation/rules/ruleConfigMerge.js +41 -0
- package/src/simulation/rules/ruleDefaults.js +85 -0
- package/src/simulation/rules/rulesReview.js +144 -0
- package/src/simulation/rules/rulesetPresets.js +47 -0
- package/src/simulation/rules/trackLimitsSteward.js +1 -1
- package/src/simulation/rulesConfig.js +5 -323
- package/src/simulation/snapshots/raceSnapshots.js +193 -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/trackQueryGrid.js +94 -0
- package/src/simulation/track/trackQueryIndex.js +659 -0
- package/src/simulation/track/trackQueryPitIndex.js +140 -0
- package/src/simulation/track/trackQueryScratch.js +46 -0
- package/src/simulation/track/trackQueryStats.js +28 -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 -1706
- package/src/simulation/vehicle/contactResolution.js +181 -0
- package/src/simulation/vehicle/mainTrackWheelSurface.js +82 -0
- package/src/simulation/vehicle/pitWheelSurface.js +69 -0
- package/src/simulation/vehicle/runoffResponse.js +87 -0
- package/src/simulation/vehicle/surfacePriority.js +28 -0
- package/src/simulation/vehicle/vehicleGeometry.js +247 -0
- package/src/simulation/vehicle/vehicleKinematics.js +37 -0
- package/src/simulation/vehicle/vehiclePhysics.js +364 -0
- package/src/simulation/vehicle/vehicleSnapshots.js +327 -0
- package/src/simulation/vehicle/vehicleState.js +220 -0
- package/src/simulation/vehicle/wheelSurface.js +152 -0
- package/src/simulation/vehicleGeometry.js +1 -247
- package/src/simulation/vehiclePhysics.js +1 -136
- package/src/simulation/warmup/runtimeWarmup.js +224 -0
- package/src/simulation/wheelSurface.js +1 -338
- package/src/styles.css +42 -0
- package/src/ui/cameraControlsTemplate.js +20 -0
- package/src/ui/carOverviewTemplate.js +33 -0
- package/src/ui/componentTemplates.js +14 -360
- package/src/ui/raceCanvasTemplate.js +69 -0
- package/src/ui/raceControlsTemplate.js +36 -0
- package/src/ui/shellTemplate.js +5 -4
- package/src/ui/telemetryDrawerTemplate.js +55 -0
- package/src/ui/telemetryTemplates.js +136 -0
- package/src/ui/timingTowerTemplate.js +34 -0
|
@@ -1,367 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { metersToSimUnits, simUnitsToMeters } from '../simulation/units.js';
|
|
3
|
-
import { VEHICLE_GEOMETRY } from '../simulation/vehicleGeometry.js';
|
|
4
|
-
|
|
5
|
-
const TRACK_RAY_STEP_METERS = 1;
|
|
6
|
-
const TRACK_RAY_REFINE_STEPS = 12;
|
|
7
|
-
const ANALYTIC_TRACK_RAY_MAX_CURVATURE = 0.00005;
|
|
8
|
-
const PIT_CONNECTOR_RAY_FALLBACK_METERS = 55;
|
|
9
|
-
export const DEFAULT_RAY_ANGLES_DEGREES = [-135, -60, -20, 0, 20, 60, 135, 180];
|
|
10
|
-
|
|
11
|
-
export function buildNearbyCars(car, snapshot, { maxCars = 6, radiusMeters = 150 } = {}) {
|
|
12
|
-
const limit = Math.max(0, Math.floor(maxCars));
|
|
13
|
-
if (limit === 0) return [];
|
|
14
|
-
const radius = metersToSimUnits(radiusMeters);
|
|
15
|
-
const radiusSquared = radius * radius;
|
|
16
|
-
const forwardX = Math.cos(car.heading);
|
|
17
|
-
const forwardY = Math.sin(car.heading);
|
|
18
|
-
const rightX = -Math.sin(car.heading);
|
|
19
|
-
const rightY = Math.cos(car.heading);
|
|
20
|
-
const nearest = [];
|
|
21
|
-
|
|
22
|
-
snapshot.cars.forEach((other, order) => {
|
|
23
|
-
if (other.id === car.id) return;
|
|
24
|
-
const dx = other.x - car.x;
|
|
25
|
-
const dy = other.y - car.y;
|
|
26
|
-
const distanceSquared = dx * dx + dy * dy;
|
|
27
|
-
if (distanceSquared > radiusSquared) return;
|
|
28
|
-
|
|
29
|
-
const forward = forwardX * dx + forwardY * dy;
|
|
30
|
-
const right = rightX * dx + rightY * dy;
|
|
31
|
-
const entry = {
|
|
32
|
-
id: other.id,
|
|
33
|
-
relativeForwardMeters: simUnitsToMeters(forward),
|
|
34
|
-
relativeRightMeters: simUnitsToMeters(right),
|
|
35
|
-
relativeDistanceMeters: simUnitsToMeters(Math.sqrt(distanceSquared)),
|
|
36
|
-
relativeSpeedKph: other.speedKph - car.speedKph,
|
|
37
|
-
relativeHeadingRadians: normalizeRelativeHeading(other.heading - car.heading),
|
|
38
|
-
ahead: forward > 0,
|
|
39
|
-
sameLap: other.lap === car.lap,
|
|
40
|
-
distanceSquared,
|
|
41
|
-
order,
|
|
42
|
-
};
|
|
43
|
-
insertNearestCar(nearest, entry, limit);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
return nearest.map(({ distanceSquared, order, ...entry }) => entry);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function buildRaySensors(car, snapshot, rayOptions = {}) {
|
|
50
|
-
const angles = rayOptions.anglesDegrees ?? DEFAULT_RAY_ANGLES_DEGREES;
|
|
51
|
-
const lengthMeters = rayOptions.lengthMeters ?? 120;
|
|
52
|
-
const origin = getCarRayOrigin(car);
|
|
53
|
-
const trackContext = rayOptions.detectTrack === false
|
|
54
|
-
? null
|
|
55
|
-
: createTrackRayContext(car, snapshot, origin);
|
|
56
|
-
|
|
57
|
-
return angles.map((angleDegrees) => ({
|
|
58
|
-
angleDegrees,
|
|
59
|
-
angleRadians: degreesToRadians(angleDegrees),
|
|
60
|
-
lengthMeters,
|
|
61
|
-
track: rayOptions.detectTrack === false
|
|
62
|
-
? createTrackMiss(lengthMeters)
|
|
63
|
-
: estimateTrackHit(car, snapshot, angleDegrees, lengthMeters, trackContext),
|
|
64
|
-
car: rayOptions.detectCars === false
|
|
65
|
-
? { hit: false, distanceMeters: lengthMeters, driverId: null, relativeSpeedKph: 0 }
|
|
66
|
-
: estimateCarHit(car, snapshot, angleDegrees, lengthMeters, origin),
|
|
67
|
-
}));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function createTrackRayContext(car, snapshot, origin) {
|
|
71
|
-
if (!Array.isArray(snapshot.track?.samples) || snapshot.track.samples.length === 0) {
|
|
72
|
-
return { origin, originState: null };
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
origin,
|
|
76
|
-
originState: nearestTrackState(snapshot.track, origin, car.progress),
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function estimateTrackHit(car, snapshot, angleDegrees, lengthMeters, context = null) {
|
|
81
|
-
if (!Array.isArray(snapshot.track?.samples) || snapshot.track.samples.length === 0) {
|
|
82
|
-
return estimateLocalTrackHit(car, snapshot, angleDegrees, lengthMeters);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const origin = context?.origin ?? getCarRayOrigin(car);
|
|
86
|
-
const ray = getCarRayVector(car, angleDegrees);
|
|
87
|
-
const maxDistance = metersToSimUnits(lengthMeters);
|
|
88
|
-
const step = metersToSimUnits(TRACK_RAY_STEP_METERS);
|
|
89
|
-
const originState = context?.originState ?? nearestTrackState(snapshot.track, origin, car.progress);
|
|
90
|
-
const includePitLane = Boolean(car.inPitLane || car.pitLanePart || originState.inPitLane);
|
|
91
|
-
const analyticHit = estimateAnalyticMainTrackHit({
|
|
92
|
-
car,
|
|
93
|
-
track: snapshot.track,
|
|
94
|
-
originState,
|
|
95
|
-
ray,
|
|
96
|
-
lengthMeters,
|
|
97
|
-
includePitLane,
|
|
98
|
-
});
|
|
99
|
-
if (analyticHit) return analyticHit;
|
|
100
|
-
let previousDistance = 0;
|
|
101
|
-
let previousInside = null;
|
|
102
|
-
|
|
103
|
-
for (let distance = 0; distance <= maxDistance; distance += step) {
|
|
104
|
-
const state = nearestTrackState(snapshot.track, pointOnRay(origin, ray, distance), car.progress);
|
|
105
|
-
const inside = isInsideTrackBorder(state, snapshot.track, includePitLane);
|
|
106
|
-
if (previousInside == null) {
|
|
107
|
-
previousInside = inside;
|
|
108
|
-
previousDistance = distance;
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (inside !== previousInside) {
|
|
113
|
-
const kind = previousInside ? 'exit' : 'entry';
|
|
114
|
-
const hitDistance = refineTrackTransitionDistance(
|
|
115
|
-
snapshot.track,
|
|
116
|
-
origin,
|
|
117
|
-
ray,
|
|
118
|
-
car.progress,
|
|
119
|
-
previousDistance,
|
|
120
|
-
distance,
|
|
121
|
-
kind,
|
|
122
|
-
includePitLane,
|
|
123
|
-
);
|
|
124
|
-
return {
|
|
125
|
-
hit: true,
|
|
126
|
-
distanceMeters: simUnitsToMeters(hitDistance),
|
|
127
|
-
kind,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
previousDistance = distance;
|
|
131
|
-
previousInside = inside;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return createTrackMiss(lengthMeters);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function insertNearestCar(nearest, entry, limit) {
|
|
138
|
-
let insertAt = nearest.findIndex((candidate) => (
|
|
139
|
-
entry.distanceSquared < candidate.distanceSquared ||
|
|
140
|
-
(entry.distanceSquared === candidate.distanceSquared && entry.order < candidate.order)
|
|
141
|
-
));
|
|
142
|
-
if (insertAt < 0) insertAt = nearest.length;
|
|
143
|
-
if (insertAt >= limit) return;
|
|
144
|
-
nearest.splice(insertAt, 0, entry);
|
|
145
|
-
if (nearest.length > limit) nearest.pop();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function estimateAnalyticMainTrackHit({ car, track, originState, ray, lengthMeters, includePitLane }) {
|
|
149
|
-
if (includePitLane || isNearPitConnector(track, originState)) return null;
|
|
150
|
-
if (Math.abs(originState.curvature ?? 0) > ANALYTIC_TRACK_RAY_MAX_CURVATURE) return null;
|
|
151
|
-
|
|
152
|
-
const lateral = ray.x * originState.normalX + ray.y * originState.normalY;
|
|
153
|
-
if (Math.abs(lateral) < 0.08) return createTrackMiss(lengthMeters);
|
|
154
|
-
|
|
155
|
-
const trackHalfWidth = track.width / 2;
|
|
156
|
-
const offset = originState.signedOffset ?? car.signedOffset ?? 0;
|
|
157
|
-
const inside = Math.abs(offset) <= trackHalfWidth;
|
|
158
|
-
const targetEdge = getLocalTrackTransitionTarget({
|
|
159
|
-
inside,
|
|
160
|
-
offsetMeters: offset,
|
|
161
|
-
lateral,
|
|
162
|
-
trackHalfWidthMeters: trackHalfWidth,
|
|
163
|
-
});
|
|
164
|
-
if (targetEdge == null) return createTrackMiss(lengthMeters);
|
|
165
|
-
|
|
166
|
-
const distance = (targetEdge - offset) / lateral;
|
|
167
|
-
const maxDistance = metersToSimUnits(lengthMeters);
|
|
168
|
-
if (distance < 0 || distance > maxDistance) return createTrackMiss(lengthMeters);
|
|
169
|
-
|
|
170
|
-
return {
|
|
171
|
-
hit: true,
|
|
172
|
-
distanceMeters: simUnitsToMeters(distance),
|
|
173
|
-
kind: inside ? 'exit' : 'entry',
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function isNearPitConnector(track, state) {
|
|
178
|
-
const pitLane = track.pitLane;
|
|
179
|
-
if (!pitLane?.enabled || !Number.isFinite(state?.distance)) return false;
|
|
180
|
-
const window = metersToSimUnits(PIT_CONNECTOR_RAY_FALLBACK_METERS);
|
|
181
|
-
const entryDistance = pitLane.entry?.trackDistance ?? pitLane.entry?.distanceFromStart;
|
|
182
|
-
const exitDistance = pitLane.exit?.trackDistance ?? pitLane.exit?.distanceFromStart;
|
|
183
|
-
return wrappedTrackDistance(state.distance, entryDistance, track.length) <= window ||
|
|
184
|
-
wrappedTrackDistance(state.distance, exitDistance, track.length) <= window;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function wrappedTrackDistance(first, second, totalLength) {
|
|
188
|
-
if (!Number.isFinite(first) || !Number.isFinite(second) || !Number.isFinite(totalLength) || totalLength <= 0) {
|
|
189
|
-
return Infinity;
|
|
190
|
-
}
|
|
191
|
-
const delta = Math.abs(first - second);
|
|
192
|
-
return Math.min(delta, totalLength - delta);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function estimateLocalTrackHit(car, snapshot, angleDegrees, lengthMeters) {
|
|
196
|
-
const trackHalfWidthMeters = simUnitsToMeters(snapshot.track.width / 2);
|
|
197
|
-
const offsetMeters = simUnitsToMeters(car.signedOffset ?? 0);
|
|
198
|
-
const lateral = Math.sin(degreesToRadians(angleDegrees));
|
|
199
|
-
if (Math.abs(lateral) < 0.08) {
|
|
200
|
-
return createTrackMiss(lengthMeters);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const inside = Math.abs(offsetMeters) <= trackHalfWidthMeters;
|
|
204
|
-
const targetEdge = getLocalTrackTransitionTarget({ inside, offsetMeters, lateral, trackHalfWidthMeters });
|
|
205
|
-
if (targetEdge == null) return createTrackMiss(lengthMeters);
|
|
206
|
-
|
|
207
|
-
const distanceMeters = (targetEdge - offsetMeters) / lateral;
|
|
208
|
-
if (distanceMeters < 0 || distanceMeters > lengthMeters) return createTrackMiss(lengthMeters);
|
|
209
|
-
|
|
210
|
-
return {
|
|
211
|
-
hit: true,
|
|
212
|
-
distanceMeters,
|
|
213
|
-
kind: inside ? 'exit' : 'entry',
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function getLocalTrackTransitionTarget({ inside, offsetMeters, lateral, trackHalfWidthMeters }) {
|
|
218
|
-
if (inside) return lateral > 0 ? trackHalfWidthMeters : -trackHalfWidthMeters;
|
|
219
|
-
if (offsetMeters > trackHalfWidthMeters) return lateral < 0 ? trackHalfWidthMeters : null;
|
|
220
|
-
if (offsetMeters < -trackHalfWidthMeters) return lateral > 0 ? -trackHalfWidthMeters : null;
|
|
221
|
-
return null;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function refineTrackTransitionDistance(track, origin, ray, progressHint, lowDistance, highDistance, kind, includePitLane = true) {
|
|
225
|
-
let low = lowDistance;
|
|
226
|
-
let high = highDistance;
|
|
227
|
-
for (let index = 0; index < TRACK_RAY_REFINE_STEPS; index += 1) {
|
|
228
|
-
const middle = (low + high) / 2;
|
|
229
|
-
const state = nearestTrackState(track, pointOnRay(origin, ray, middle), progressHint);
|
|
230
|
-
const inside = isInsideTrackBorder(state, track, includePitLane);
|
|
231
|
-
if (kind === 'entry') {
|
|
232
|
-
if (inside) high = middle;
|
|
233
|
-
else low = middle;
|
|
234
|
-
} else if (inside) {
|
|
235
|
-
low = middle;
|
|
236
|
-
} else {
|
|
237
|
-
high = middle;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return high;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function isInsideTrackBorder(state, track, includePitLane = true) {
|
|
244
|
-
if (includePitLane && state.inPitLane) return true;
|
|
245
|
-
return state.crossTrackError <= track.width / 2;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function createTrackMiss(lengthMeters) {
|
|
249
|
-
return {
|
|
250
|
-
hit: false,
|
|
251
|
-
distanceMeters: lengthMeters,
|
|
252
|
-
kind: null,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function estimateCarHit(car, snapshot, angleDegrees, lengthMeters, origin = getCarRayOrigin(car)) {
|
|
257
|
-
const ray = getCarRayVector(car, angleDegrees);
|
|
258
|
-
const maxDistance = metersToSimUnits(lengthMeters);
|
|
259
|
-
let closest = null;
|
|
260
|
-
snapshot.cars.forEach((other) => {
|
|
261
|
-
if (other.id === car.id) return;
|
|
262
|
-
if (!carRayBroadphaseHit(origin, ray, maxDistance, other)) return;
|
|
263
|
-
const hitDistance = intersectCarFootprint(origin, ray, other);
|
|
264
|
-
if (hitDistance == null || hitDistance > maxDistance) return;
|
|
265
|
-
const distanceMeters = simUnitsToMeters(hitDistance);
|
|
266
|
-
if (!closest || hitDistance < closest.distanceSimUnits) {
|
|
267
|
-
closest = {
|
|
268
|
-
hit: true,
|
|
269
|
-
distanceSimUnits: hitDistance,
|
|
270
|
-
distanceMeters,
|
|
271
|
-
driverId: other.id,
|
|
272
|
-
relativeSpeedKph: other.speedKph - car.speedKph,
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
if (!closest) return { hit: false, distanceMeters: lengthMeters, driverId: null, relativeSpeedKph: 0 };
|
|
277
|
-
const { distanceSimUnits, ...publicHit } = closest;
|
|
278
|
-
return publicHit;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function carRayBroadphaseHit(origin, ray, maxDistance, other) {
|
|
282
|
-
const dx = other.x - origin.x;
|
|
283
|
-
const dy = other.y - origin.y;
|
|
284
|
-
const projection = dx * ray.x + dy * ray.y;
|
|
285
|
-
const radius = Math.hypot(VEHICLE_GEOMETRY.bodyLength, VEHICLE_GEOMETRY.bodyWidth) / 2;
|
|
286
|
-
if (projection < -radius || projection > maxDistance + radius) return false;
|
|
287
|
-
const perpendicularSquared = Math.max(0, dx * dx + dy * dy - projection * projection);
|
|
288
|
-
return perpendicularSquared <= radius * radius;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
export function getCarRayOrigin(car) {
|
|
292
|
-
return {
|
|
293
|
-
x: car.x,
|
|
294
|
-
y: car.y,
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
export function getCarRayVector(car, angleDegrees) {
|
|
299
|
-
const angle = car.heading + degreesToRadians(angleDegrees);
|
|
300
|
-
return {
|
|
301
|
-
x: Math.cos(angle),
|
|
302
|
-
y: Math.sin(angle),
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function pointOnRay(origin, ray, distance) {
|
|
307
|
-
return {
|
|
308
|
-
x: origin.x + ray.x * distance,
|
|
309
|
-
y: origin.y + ray.y * distance,
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function intersectCarFootprint(origin, ray, other) {
|
|
314
|
-
const forward = { x: Math.cos(other.heading), y: Math.sin(other.heading) };
|
|
315
|
-
const right = { x: -Math.sin(other.heading), y: Math.cos(other.heading) };
|
|
316
|
-
const delta = { x: origin.x - other.x, y: origin.y - other.y };
|
|
317
|
-
const localOrigin = {
|
|
318
|
-
x: dot(delta, forward),
|
|
319
|
-
y: dot(delta, right),
|
|
320
|
-
};
|
|
321
|
-
const localRay = {
|
|
322
|
-
x: dot(ray, forward),
|
|
323
|
-
y: dot(ray, right),
|
|
324
|
-
};
|
|
325
|
-
const halfLength = VEHICLE_GEOMETRY.bodyLength / 2;
|
|
326
|
-
const halfWidth = VEHICLE_GEOMETRY.bodyWidth / 2;
|
|
327
|
-
return intersectAxisAlignedBoxRay(localOrigin, localRay, halfLength, halfWidth);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function intersectAxisAlignedBoxRay(origin, ray, halfLength, halfWidth) {
|
|
331
|
-
let tMin = -Infinity;
|
|
332
|
-
let tMax = Infinity;
|
|
333
|
-
const xRange = intersectSlab(origin.x, ray.x, -halfLength, halfLength);
|
|
334
|
-
const yRange = intersectSlab(origin.y, ray.y, -halfWidth, halfWidth);
|
|
335
|
-
if (!xRange || !yRange) return null;
|
|
336
|
-
tMin = Math.max(tMin, xRange.min, yRange.min);
|
|
337
|
-
tMax = Math.min(tMax, xRange.max, yRange.max);
|
|
338
|
-
if (tMax < 0 || tMin > tMax) return null;
|
|
339
|
-
return Math.max(0, tMin);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function intersectSlab(origin, direction, min, max) {
|
|
343
|
-
if (Math.abs(direction) < 1e-9) {
|
|
344
|
-
return origin >= min && origin <= max ? { min: -Infinity, max: Infinity } : null;
|
|
345
|
-
}
|
|
346
|
-
const first = (min - origin) / direction;
|
|
347
|
-
const second = (max - origin) / direction;
|
|
348
|
-
return {
|
|
349
|
-
min: Math.min(first, second),
|
|
350
|
-
max: Math.max(first, second),
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function dot(a, b) {
|
|
355
|
-
return a.x * b.x + a.y * b.y;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
function degreesToRadians(degrees) {
|
|
359
|
-
return (degrees * Math.PI) / 180;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function normalizeRelativeHeading(angle) {
|
|
363
|
-
let value = angle;
|
|
364
|
-
while (value > Math.PI) value -= Math.PI * 2;
|
|
365
|
-
while (value < -Math.PI) value += Math.PI * 2;
|
|
366
|
-
return value;
|
|
367
|
-
}
|
|
1
|
+
export * from './sensors/index.js';
|
package/src/environment/specs.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { normalizeLookaheadMeters } from './observationOptions.js';
|
|
2
|
+
import { effectiveSensorOptions } from './observations.js';
|
|
3
|
+
import { normalizeRayOptions } from './sensors.js';
|
|
4
|
+
|
|
5
|
+
const MODEL_RAY_SURFACE_CHANNELS = Object.freeze(['kerb', 'illegalSurface']);
|
|
2
6
|
|
|
3
7
|
export function buildActionSpec(options) {
|
|
4
8
|
const compounds = Array.isArray(options.rules?.modules?.tireStrategy?.compounds) &&
|
|
@@ -22,21 +26,35 @@ export function buildActionSpec(options) {
|
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
export function buildObservationSpec(options) {
|
|
25
|
-
const rayOptions = options.sensors.rays;
|
|
29
|
+
const rayOptions = normalizeRayOptions(options.sensors.rays);
|
|
26
30
|
const nearbyOptions = options.sensors.nearbyCars;
|
|
27
|
-
const lookaheadMeters =
|
|
31
|
+
const lookaheadMeters = Array.isArray(options.observation?.lookaheadMeters)
|
|
32
|
+
? options.observation.lookaheadMeters
|
|
33
|
+
: normalizeLookaheadMeters(options.observation?.lookaheadMeters);
|
|
34
|
+
const profile = options.observation?.profile ?? 'default';
|
|
28
35
|
return {
|
|
29
|
-
version:
|
|
36
|
+
version: profile === 'physical-driver' ? 6 : 3,
|
|
30
37
|
controlledDrivers: [...options.controlledDrivers],
|
|
31
38
|
object: {
|
|
39
|
+
profile,
|
|
32
40
|
self: [
|
|
33
41
|
{ name: 'id', unit: 'id' },
|
|
34
42
|
{ name: 'speedKph', unit: 'kph' },
|
|
35
43
|
{ name: 'speedMetersPerSecond', unit: 'm/s' },
|
|
36
44
|
{ name: 'headingRadians', unit: 'rad' },
|
|
45
|
+
{ name: 'yawRateRadiansPerSecond', unit: 'rad/s' },
|
|
37
46
|
{ name: 'steeringAngleRadians', unit: 'rad' },
|
|
38
47
|
{ name: 'throttle', unit: 'normalized' },
|
|
39
48
|
{ name: 'brake', unit: 'normalized' },
|
|
49
|
+
{ name: 'appliedControls', unit: 'nullable:normalized-controls' },
|
|
50
|
+
{ name: 'lateralG', unit: 'g' },
|
|
51
|
+
{ name: 'longitudinalG', unit: 'g' },
|
|
52
|
+
{ name: 'gripUsage', unit: 'ratio' },
|
|
53
|
+
{ name: 'slipAngleRadians', unit: 'rad' },
|
|
54
|
+
{ name: 'tractionLimited', unit: 'boolean' },
|
|
55
|
+
{ name: 'stabilityState', unit: 'label' },
|
|
56
|
+
{ name: 'destroyed', unit: 'boolean' },
|
|
57
|
+
{ name: 'destroyReason', unit: 'nullable:label' },
|
|
40
58
|
{ name: 'lap', unit: 'count' },
|
|
41
59
|
{ name: 'completedLaps', unit: 'count' },
|
|
42
60
|
{ name: 'lapProgressMeters', unit: 'm' },
|
|
@@ -56,6 +74,27 @@ export function buildObservationSpec(options) {
|
|
|
56
74
|
{ name: 'pitStopPenaltyServiceRemainingSeconds', unit: 'nullable:seconds' },
|
|
57
75
|
{ name: 'pitStopsCompleted', unit: 'count' },
|
|
58
76
|
],
|
|
77
|
+
trackRelation: [
|
|
78
|
+
{ name: 'lateralOffsetMeters', unit: 'm' },
|
|
79
|
+
{ name: 'headingErrorRadians', unit: 'rad' },
|
|
80
|
+
{ name: 'legalWidthMeters', unit: 'm' },
|
|
81
|
+
{ name: 'leftBoundaryMeters', unit: 'm' },
|
|
82
|
+
{ name: 'rightBoundaryMeters', unit: 'm' },
|
|
83
|
+
{ name: 'onLegalSurface', unit: 'boolean' },
|
|
84
|
+
{ name: 'surface', unit: 'label' },
|
|
85
|
+
],
|
|
86
|
+
contactPatches: {
|
|
87
|
+
ids: ['front-left', 'front-right', 'rear-left', 'rear-right'],
|
|
88
|
+
fields: [
|
|
89
|
+
{ name: 'present', unit: 'boolean' },
|
|
90
|
+
{ name: 'signedOffsetMeters', unit: 'm' },
|
|
91
|
+
{ name: 'crossTrackErrorMeters', unit: 'm' },
|
|
92
|
+
{ name: 'surface', unit: 'label' },
|
|
93
|
+
{ name: 'surfaceCode', unit: 'number' },
|
|
94
|
+
{ name: 'onLegalSurface', unit: 'boolean' },
|
|
95
|
+
{ name: 'inPitLane', unit: 'boolean' },
|
|
96
|
+
],
|
|
97
|
+
},
|
|
59
98
|
race: [
|
|
60
99
|
{ name: 'position', unit: 'rank' },
|
|
61
100
|
{ name: 'totalCars', unit: 'count' },
|
|
@@ -64,27 +103,8 @@ export function buildObservationSpec(options) {
|
|
|
64
103
|
{ name: 'redFlag', unit: 'boolean' },
|
|
65
104
|
{ name: 'totalLaps', unit: 'count' },
|
|
66
105
|
],
|
|
67
|
-
rays:
|
|
68
|
-
|
|
69
|
-
anglesDegrees: [...rayOptions.anglesDegrees],
|
|
70
|
-
lengthMeters: rayOptions.lengthMeters,
|
|
71
|
-
track: {
|
|
72
|
-
distanceMeters: { unit: 'm', noHitValue: rayOptions.lengthMeters },
|
|
73
|
-
hit: { unit: 'boolean' },
|
|
74
|
-
kind: { values: ['exit', 'entry', null] },
|
|
75
|
-
},
|
|
76
|
-
car: {
|
|
77
|
-
distanceMeters: { unit: 'm', noHitValue: rayOptions.lengthMeters },
|
|
78
|
-
hit: { unit: 'boolean' },
|
|
79
|
-
driverId: { nullable: true },
|
|
80
|
-
relativeSpeedKph: { unit: 'kph' },
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
nearbyCars: {
|
|
84
|
-
enabled: Boolean(nearbyOptions.enabled),
|
|
85
|
-
maxCars: nearbyOptions.maxCars,
|
|
86
|
-
radiusMeters: nearbyOptions.radiusMeters,
|
|
87
|
-
},
|
|
106
|
+
rays: buildRayObjectSpec(rayOptions),
|
|
107
|
+
nearbyCars: buildNearbyObjectSpec(nearbyOptions),
|
|
88
108
|
track: {
|
|
89
109
|
lengthMeters: { unit: 'm' },
|
|
90
110
|
widthMeters: { unit: 'm' },
|
|
@@ -99,18 +119,87 @@ export function buildObservationSpec(options) {
|
|
|
99
119
|
events: { type: 'array' },
|
|
100
120
|
},
|
|
101
121
|
vector: {
|
|
102
|
-
schema: buildVectorSchema({ rayOptions, nearbyOptions, lookaheadMeters }),
|
|
122
|
+
schema: buildVectorSchema({ rayOptions, nearbyOptions, lookaheadMeters, profile }),
|
|
103
123
|
},
|
|
124
|
+
perDriver: buildPerDriverObservationSpecs(options, { rayOptions, nearbyOptions, lookaheadMeters, profile }),
|
|
104
125
|
};
|
|
105
126
|
}
|
|
106
127
|
|
|
107
|
-
function
|
|
128
|
+
function buildPerDriverObservationSpecs(options, { rayOptions, nearbyOptions, lookaheadMeters, profile }) {
|
|
129
|
+
if (Object.keys(options.sensorsByDriver ?? {}).length === 0) return {};
|
|
130
|
+
const defaultSensors = { rays: rayOptions, nearbyCars: nearbyOptions };
|
|
131
|
+
return Object.fromEntries(options.controlledDrivers.map((driverId) => {
|
|
132
|
+
const sensors = effectiveSensorOptions(options, driverId, defaultSensors, true);
|
|
133
|
+
return [driverId, {
|
|
134
|
+
object: {
|
|
135
|
+
rays: buildRayObjectSpec(sensors.rays),
|
|
136
|
+
nearbyCars: buildNearbyObjectSpec(sensors.nearbyCars),
|
|
137
|
+
},
|
|
138
|
+
vector: {
|
|
139
|
+
schema: buildVectorSchema({
|
|
140
|
+
rayOptions: sensors.rays,
|
|
141
|
+
nearbyOptions: sensors.nearbyCars,
|
|
142
|
+
lookaheadMeters,
|
|
143
|
+
profile,
|
|
144
|
+
}),
|
|
145
|
+
},
|
|
146
|
+
}];
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function buildRayObjectSpec(rayOptions) {
|
|
151
|
+
return {
|
|
152
|
+
enabled: Boolean(rayOptions.enabled),
|
|
153
|
+
anglesDegrees: [...rayOptions.anglesDegrees],
|
|
154
|
+
lengthMeters: rayOptions.lengthMeters,
|
|
155
|
+
defaultLengthMeters: rayOptions.defaultLengthMeters,
|
|
156
|
+
rays: rayOptions.rays.map((ray) => ({ ...ray })),
|
|
157
|
+
channels: [...rayOptions.channels],
|
|
158
|
+
precision: rayOptions.precision,
|
|
159
|
+
track: {
|
|
160
|
+
distanceMeters: { unit: 'm', noHitValue: rayOptions.lengthMeters },
|
|
161
|
+
hit: { unit: 'boolean' },
|
|
162
|
+
kind: { values: ['exit', 'entry', null] },
|
|
163
|
+
},
|
|
164
|
+
roadEdge: {
|
|
165
|
+
distanceMeters: { unit: 'm', noHitValue: rayOptions.lengthMeters },
|
|
166
|
+
hit: { unit: 'boolean' },
|
|
167
|
+
kind: { values: ['exit', 'entry', null] },
|
|
168
|
+
},
|
|
169
|
+
kerb: surfaceRaySpec(rayOptions.lengthMeters),
|
|
170
|
+
illegalSurface: surfaceRaySpec(rayOptions.lengthMeters),
|
|
171
|
+
car: {
|
|
172
|
+
distanceMeters: { unit: 'm', noHitValue: rayOptions.lengthMeters },
|
|
173
|
+
hit: { unit: 'boolean' },
|
|
174
|
+
driverId: { nullable: true },
|
|
175
|
+
targetId: { nullable: true },
|
|
176
|
+
targetType: { values: ['car', 'replayGhost', null] },
|
|
177
|
+
relativeSpeedKph: { unit: 'kph' },
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function buildNearbyObjectSpec(nearbyOptions) {
|
|
183
|
+
return {
|
|
184
|
+
enabled: Boolean(nearbyOptions.enabled),
|
|
185
|
+
maxCars: nearbyOptions.maxCars,
|
|
186
|
+
radiusMeters: nearbyOptions.radiusMeters,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function buildVectorSchema({ rayOptions, nearbyOptions, lookaheadMeters, profile = 'default' }) {
|
|
191
|
+
const includePhysicalDriverSenses = profile === 'physical-driver';
|
|
108
192
|
const schema = [
|
|
109
193
|
{ name: 'self.speedKph', unit: 'kph', scale: 'fixed:400' },
|
|
110
194
|
{ name: 'self.speedMetersPerSecond', unit: 'm/s', scale: 'fixed:120' },
|
|
111
195
|
{ name: 'self.steeringAngleRadians', unit: 'rad', scale: 'fixed:pi' },
|
|
112
196
|
{ name: 'self.throttle', scale: '0..1' },
|
|
113
197
|
{ name: 'self.brake', scale: '0..1' },
|
|
198
|
+
{ name: 'self.lateralG', scale: 'fixed:8g' },
|
|
199
|
+
{ name: 'self.longitudinalG', scale: 'fixed:6g' },
|
|
200
|
+
{ name: 'self.gripUsage', scale: '0..2' },
|
|
201
|
+
{ name: 'self.slipAngleRadians', unit: 'rad', scale: 'fixed:pi' },
|
|
202
|
+
{ name: 'self.tractionLimited', scale: 'boolean' },
|
|
114
203
|
{ name: 'self.lapProgressRatio', scale: '0..1' },
|
|
115
204
|
{ name: 'self.trackOffsetMeters', unit: 'm', scale: 'fixed:meters' },
|
|
116
205
|
{ name: 'self.trackHeadingErrorRadians', unit: 'rad', scale: 'fixed:pi' },
|
|
@@ -126,6 +215,23 @@ function buildVectorSchema({ rayOptions, nearbyOptions, lookaheadMeters }) {
|
|
|
126
215
|
{ name: 'race.pitLaneOpen', scale: 'boolean' },
|
|
127
216
|
{ name: 'track.curvature', scale: 'track-curvature' },
|
|
128
217
|
];
|
|
218
|
+
if (includePhysicalDriverSenses) {
|
|
219
|
+
schema.push(
|
|
220
|
+
{ name: 'self.yawRateRadiansPerSecond', unit: 'rad/s', scale: 'fixed:pi' },
|
|
221
|
+
{ name: 'trackRelation.leftBoundaryMeters', unit: 'm', scale: 'fixed:meters' },
|
|
222
|
+
{ name: 'trackRelation.rightBoundaryMeters', unit: 'm', scale: 'fixed:meters' },
|
|
223
|
+
{ name: 'trackRelation.legalWidthMeters', unit: 'm', scale: 'fixed:meters' },
|
|
224
|
+
);
|
|
225
|
+
for (let index = 0; index < 4; index += 1) {
|
|
226
|
+
schema.push(
|
|
227
|
+
{ name: `contactPatches[${index}].present`, scale: 'boolean' },
|
|
228
|
+
{ name: `contactPatches[${index}].surfaceCode`, scale: 'surface-code' },
|
|
229
|
+
{ name: `contactPatches[${index}].onLegalSurface`, scale: 'boolean' },
|
|
230
|
+
{ name: `contactPatches[${index}].signedOffsetMeters`, unit: 'm', scale: 'fixed:meters' },
|
|
231
|
+
{ name: `contactPatches[${index}].crossTrackErrorMeters`, unit: 'm', scale: 'fixed:meters' },
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
129
235
|
lookaheadMeters.forEach((_, index) => {
|
|
130
236
|
schema.push(
|
|
131
237
|
{ name: `track.lookahead[${index}].curvature`, scale: 'track-curvature' },
|
|
@@ -142,7 +248,16 @@ function buildVectorSchema({ rayOptions, nearbyOptions, lookaheadMeters }) {
|
|
|
142
248
|
{ name: `rays[${index}].car.distanceRatio`, scale: '0..1' },
|
|
143
249
|
{ name: `rays[${index}].car.hit`, scale: 'boolean' },
|
|
144
250
|
{ name: `rays[${index}].car.relativeSpeedKph`, unit: 'kph', scale: 'fixed:200' },
|
|
251
|
+
{ name: `rays[${index}].car.targetTypeReplayGhost`, scale: 'boolean' },
|
|
145
252
|
);
|
|
253
|
+
MODEL_RAY_SURFACE_CHANNELS.forEach((channel) => {
|
|
254
|
+
if (rayOptions.channels?.includes?.(channel)) {
|
|
255
|
+
schema.push(
|
|
256
|
+
{ name: `rays[${index}].${channel}.distanceRatio`, scale: '0..1' },
|
|
257
|
+
{ name: `rays[${index}].${channel}.hit`, scale: 'boolean' },
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
});
|
|
146
261
|
});
|
|
147
262
|
}
|
|
148
263
|
if (nearbyOptions.enabled) {
|
|
@@ -156,8 +271,26 @@ function buildVectorSchema({ rayOptions, nearbyOptions, lookaheadMeters }) {
|
|
|
156
271
|
{ name: `nearbyCars[${index}].relativeHeadingRadians`, unit: 'rad', scale: 'fixed:pi' },
|
|
157
272
|
{ name: `nearbyCars[${index}].ahead`, scale: 'boolean' },
|
|
158
273
|
{ name: `nearbyCars[${index}].sameLap`, scale: 'boolean' },
|
|
274
|
+
{ name: `nearbyCars[${index}].entityTypeReplayGhost`, scale: 'boolean' },
|
|
159
275
|
);
|
|
276
|
+
if (includePhysicalDriverSenses) {
|
|
277
|
+
schema.push(
|
|
278
|
+
{ name: `nearbyCars[${index}].behind`, scale: 'boolean' },
|
|
279
|
+
{ name: `nearbyCars[${index}].closingRateMetersPerSecond`, unit: 'm/s', scale: 'fixed:100' },
|
|
280
|
+
{ name: `nearbyCars[${index}].timeToContactSeconds`, unit: 's', scale: 'fixed:10' },
|
|
281
|
+
{ name: `nearbyCars[${index}].leftOverlap`, scale: 'boolean' },
|
|
282
|
+
{ name: `nearbyCars[${index}].rightOverlap`, scale: 'boolean' },
|
|
283
|
+
);
|
|
284
|
+
}
|
|
160
285
|
}
|
|
161
286
|
}
|
|
162
287
|
return schema;
|
|
163
288
|
}
|
|
289
|
+
|
|
290
|
+
function surfaceRaySpec(lengthMeters) {
|
|
291
|
+
return {
|
|
292
|
+
distanceMeters: { unit: 'm', noHitValue: lengthMeters },
|
|
293
|
+
hit: { unit: 'boolean' },
|
|
294
|
+
surface: { values: ['kerb', 'grass', 'gravel', null] },
|
|
295
|
+
};
|
|
296
|
+
}
|
|
@@ -15,6 +15,8 @@ export function handleEnvironmentMessage(env, message = {}) {
|
|
|
15
15
|
switch (message.type) {
|
|
16
16
|
case 'reset':
|
|
17
17
|
return createSuccessResponse(id, 'reset:result', env.reset(message.options ?? {}));
|
|
18
|
+
case 'resetDrivers':
|
|
19
|
+
return createSuccessResponse(id, 'resetDrivers:result', env.resetDrivers(message.placements ?? {}, message.resultOptions ?? {}));
|
|
18
20
|
case 'step':
|
|
19
21
|
return createSuccessResponse(id, 'step:result', env.step(message.actions ?? {}));
|
|
20
22
|
case 'getActionSpec':
|
|
@@ -24,7 +26,7 @@ export function handleEnvironmentMessage(env, message = {}) {
|
|
|
24
26
|
case 'getObservation':
|
|
25
27
|
return createSuccessResponse(id, 'getObservation:result', env.getObservation());
|
|
26
28
|
case 'getState':
|
|
27
|
-
return createSuccessResponse(id, 'getState:result', env.getState());
|
|
29
|
+
return createSuccessResponse(id, 'getState:result', env.getState(resolveStateOptions(message)));
|
|
28
30
|
case 'destroy':
|
|
29
31
|
env.destroy();
|
|
30
32
|
return createSuccessResponse(id, 'destroy:result', null);
|
|
@@ -33,6 +35,16 @@ export function handleEnvironmentMessage(env, message = {}) {
|
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
function resolveStateOptions(message) {
|
|
39
|
+
if (message.stateOptions && typeof message.stateOptions === 'object') {
|
|
40
|
+
return message.stateOptions;
|
|
41
|
+
}
|
|
42
|
+
if (message.resultOptions?.stateOutput) {
|
|
43
|
+
return { output: message.resultOptions.stateOutput };
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
36
48
|
function createSuccessResponse(id, type, result) {
|
|
37
49
|
return {
|
|
38
50
|
id,
|