Public
Edited
Mar 10
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function onTick() {
// Keep total time elapsed up to date
mutable totalMsElapsed = mutable totalMsElapsed + msPerTick;

const oldGasPedal = mutable gasPedal;
const oldX = mutable x;
const oldV = mutable v;

// 1. Update pos as defined by current v
mutable x = oldX + actualV * sPerTick;

// 2. Update v as defined by a
mutable v = Math.max(0, oldV + a * sPerTick);

// 3. Update gas pedal values
mutable gasPedal = desiredGasPedal;

console.log(mutable gasPedal, oldGasPedal);

// 4. Cruise control
// 4.1 Track how long velocity has been retained
if (roundV(oldV) === roundV(v)) {
mutable vRetainedFor = mutable vRetainedFor + msPerTick;
} else {
mutable vRetainedFor = 0;
}

// 4.2
// Evaluate if cruise control should be turned on
if (
!mutable cruiseControlIsActive &&
!brakePedal && // Never activate when break is used
actualV >= minCruiseControlV &&
vRetainedFor > minCruiseControlActivationT
) {
mutable cruiseControlV = actualV;
mutable cruiseControlIsActive = true;
}

// 4.3
// Turn cruise control off when breaking
if (mutable cruiseControlIsActive && brakePedal) {
mutable cruiseControlIsActive = false;
}

// 4.4
// Turn cruise control off when user accelerates
// We could compare V, but we're specifically interested in user interaction (V could increase when going downhill)
if (mutable cruiseControlIsActive && mutable gasPedal > oldGasPedal) {
// We must reset this counter, or CC will just be turned on in the next cycle
mutable vRetainedFor = 0;

// Turn CC off
mutable cruiseControlIsActive = false;
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
minCruiseControlV = cruiseControlDisabled
? 10000 // hack to implement disabling
: (minCruiseControlVKmh / 3.6) // km/h -> m/s
Insert cell
minCruiseControlActivationT = minCruiseControlActivationTSec * 1000 // ms
Insert cell
mutable cruiseControlV = 0
Insert cell
// Cruise control is turned on, but user might still have foot on gas pedal
mutable cruiseControlIsActive = false
Insert cell
// FIXME: This should measure/calculate the actual force to keep constant speed
// Now it naively accelerates or breaks intermittently without this constant base force
cruiseControlF = {
// Maximum force for CC to use
const maxF = 7500;

// deltaV = actualV - cruiseControlV // Difference in target vs current speed
// F = ma
// v = s/t
// currentA = a
// calculate time it would take to reach a

// First iteration: increase force linearly based on V
// 180 / 3.6 / 50 = 1 -> full throttle at 180 km/h
const multiplier = actualV / 50;
const F = multiplier * maxF;

// Only break when exceeding speed limit significantly
if (actualV > cruiseControlV && toKmh(actualV - cruiseControlV) < 5) {
return 0;
}

// Accelerate or decelerate
return actualV < cruiseControlV ? F : -F;
}
Insert cell
Insert cell
Insert cell
mutable gasPedal = 0
Insert cell
maxGasForce = 1000 * 7.5 // N
Insert cell
maxBreakForce = -(15 * 1000) // N
Insert cell
// Force produced by engine
// FIXME: it would be more accurate to model a separate force for brakes
engineF = {
// Breaking (takes precedence over everything else)
if (brakePedal) {
return maxBreakForce * brakePedal;
}

// Cruise control looks at target speed and
if (cruiseControlIsActive) {
return cruiseControlF;
}

return maxGasForce * gasPedal;
}
Insert cell
Insert cell
Insert cell
g = 9.81
Insert cell
m = 800 // kg
Insert cell
mutable x = 0.0 // m
Insert cell
mutable v = 0.0 // m/s
Insert cell
mutable vRetainedFor = 0 // ms, how long current speed has been retained
Insert cell
a = totalF / m // m/s²
Insert cell
actualV = Math.max(0, v) // Braking, friction and air cannot push the car backwards
Insert cell
Insert cell
breakF = 0.0
Insert cell
frictionF = {
const f = 0.075 * g * m;
return v > 0 ? -1 * f : 0; // N
}
Insert cell
airF = -1 * 1 * Math.pow(actualV, 2.3) // N
Insert cell
totalF = engineF + breakF + frictionF + airF // N
Insert cell
E = 0.5 * m * Math.pow(actualV, 2) // J
Insert cell
Insert cell
Insert cell
msPerTick = 1000 / 40 // frames per second
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function onUpdate(msSinceLastFrame) {
const isRunning = loop.isRunning();
if (mutable simulationIsRunning != isRunning) {
mutable simulationIsRunning = isRunning;
}

// Simulate n number of ticks (the actual simulation happens here)
const ms = leftoverMs + msSinceLastFrame;
const tickCount = Math.floor(ms / msPerTick);
for (let i = 0; i < tickCount; ++i) {
onTick(msPerTick);
}

// Store number of leftover ms that was not enough to get its own tick
mutable leftoverMs = ms - tickCount * msPerTick;

// Finally, update plots and analysis
const maxAnalysisCount = 20 / sPerTick;
mutable analysis = mutable analysis.concat([getAnalysis()]);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more