implemented controls on bang bang simulator

master
Jake 2025-08-03 14:15:59 +08:00
parent 6f84d1070a
commit 2bf27c264d
2 changed files with 129 additions and 16 deletions

View File

@ -16,34 +16,124 @@ export class LineFollowerSim {
this.lineTargetX = 250;
this.lineWidth = 12;
const bbThresholdSlider = document.getElementById("bbThresholdSlider");
const bbTurnSpeedSlider = document.getElementById("bbTurnSpeedSlider");
const bbThresholdValue = document.getElementById("bbThresholdValue");
const bbTurnSpeedValue = document.getElementById("bbTurnSpeedValue");
this.threshold = parseFloat(bbThresholdSlider.value);
const resetBtn = document.getElementById("resetBangBangBtn");
resetBtn.addEventListener("click", () => {
// Put the box instantly on the line
this.box.x = this.lineX;
});
this.lastUpdateTime = 0;
this.updateInterval = 50; // ms
this.updateBox = updateBoxFn || this.defaultUpdateBox.bind(this);
this.loop = this.loop.bind(this);
}
updateCodeBlock() {
this.code = `
if left_color < ${this.threshold}:
motor(-${this.turnAmount}, ${this.turnAmount}) # Turn left
elif right_color < ${this.threshold}:
motor(${this.turnAmount}, -${this.turnAmount}) # Turn right
else:
motor(50, 50) # Go straight
`
document.getElementById('code-block').textContent = this.code;
}
updateLine() {
if (Math.random() < 0.02) {
this.lineTargetX = 250 + (Math.random() - 0.5) * 200;
}
this.lineX += (this.lineTargetX - this.lineX) * 0.005;
this.lineX += (this.lineTargetX - this.lineX) * 0.0025;
}
lineProximity(x, lineX) {
const distance = Math.abs(x - lineX);
const maxDistance = 20;
const maxValue = 1024;
if (distance > maxDistance) return 0;
// Reverse the falloff: closer = lower, farther = higher
const value = 100 + (distance / maxDistance) * (maxValue - 100);
return Math.round(value);
}
defaultUpdateBox() {
const now = performance.now();
if (now - this.lastUpdateTime < this.updateInterval) return;
this.lastUpdateTime = now;
const bbLeftColorValue = document.getElementById("leftColor");
const bbRightColorValue = document.getElementById("rightColor");
const bbThresh = parseFloat(bbThresholdSlider.value);
const bbTurnSpeed = parseFloat(bbTurnSpeedSlider.value);
bbThresholdValue.textContent = bbThresh.toFixed(0);
bbTurnSpeedValue.textContent = bbTurnSpeed.toFixed(2);
this.threshold = bbThresh.toFixed(0);
this.turnAmount = bbTurnSpeed.toFixed(0);
const leftSensor = this.box.x - this.box.width / 2;
const rightSensor = this.box.x + this.box.width / 2;
this.updateCodeBlock();
const onLine = x => x >= this.lineX - this.lineWidth / 2 && x <= this.lineX + this.lineWidth / 2;
let leftColor = this.lineProximity(leftSensor, this.lineX);
let rightColor = this.lineProximity(rightSensor, this.lineX);
if (leftColor == 0) {
leftColor = 1024; // No line detected
}
if (rightColor == 0) {
rightColor = 1024; // No line detected
}
bbLeftColorValue.textContent = leftColor;
bbRightColorValue.textContent = rightColor;
const leftOnLine = onLine(leftSensor);
const rightOnLine = onLine(rightSensor);
if (leftOnLine && !rightOnLine) {
this.box.dir = -1;
} else if (rightOnLine && !leftOnLine) {
this.box.dir = 1;
} else {
//this.box.dir = 0;
if (leftColor < bbThresh) {
this.box.dir -= (bbTurnSpeed * 0.01);
if (this.box.dir < -5) {
this.box.dir = -5; // Limit the turn speed
}
} else if (rightColor < bbThresh) {
this.box.dir += bbTurnSpeed * 0.01;
if (this.box.dir > 5) {
this.box.dir = 5; // Limit the turn speed
}
} else {
if (this.box.dir > 1) {
this.box.dir -= 0.1;
} else if (this.box.dir < -1) {
this.box.dir += 0.1;
} else {
this.box.dir = 0;
}
}
console.log(this.box.dir);
this.box.x += this.box.dir * this.box.speed;
}

View File

@ -1119,6 +1119,24 @@ while True:
<div>
<h2>Bang-Bang Algorithm</h2>
<canvas id="canvasBangBang" width="500" height="300"></canvas>
<div style="max-width: 500px; margin: 0 auto; font-family: sans-serif;">
<h3>Bang-Bang Controller Parameters</h3>
<label>Threshold: <input type="range" id="bbThresholdSlider" min="0" max="1024" step="0.001"
value="500"> <span id="bbThresholdValue">500</span></label><br>
<label>Turn Speed: <input type="range" id="bbTurnSpeedSlider" min="0" max="100" step="1"
value="50">
<span id="bbTurnSpeedValue">50</span></label><br>
<p><strong>left_color:</strong> <span id="leftColor">0</span></p>
<strong>right_color:</strong> <span id="rightColor">0</span>
<pre class="bg-gray-100 p-4 rounded shadow text-sm">
<code id="code-block" class="language-python"></code>
</pre>
</br> <button id="resetBangBangBtn"
class="px-2 py-1 bg-red-600 text-white text-sm font-semibold rounded hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-400">
Reset Robot Position
</button>
</div>
</br>
</div>
@ -1129,30 +1147,32 @@ while True:
<p>A PID algorithm on the other hand tries to always keep the robot exactly on the line.</p>
</br>
<p>First we have to change our two colour values, <code>left_color</code> and
<code>right_color</code> into a single <code>error</code> value.</p>
<code>right_color</code> into a single <code>error</code> value.
</p>
<p><code>right_color - left_color = error</code></p>
</br>
<p>Now if we are on the line our error should be 0, with ot becoming positive if we move one way,
and negative if we move the other.</p>
</br>
<p>Try adjusting the <code>Kp</code> value to control how aggressively the robot will attempt to
drive towards the line.</p>
</div>
<div>
<h2>PID Algorithm</h2>
<canvas id="canvasPID" width="500" height="300"></canvas>
<div style="max-width: 500px; margin: 0 auto; font-family: sans-serif;">
<h3>PID Controller Parameters</h3>
<label>Kp: <input type="range" id="kpSlider" min="0" max="1" step="0.001" value="0.01"> <span
id="kpVal">0.01</span></label><br>
<label>Kp: <input type="range" id="kpSlider" min="0" max="1" step="0.001" value="0.074"> <span
id="kpVal">0.074</span></label><br>
<label>Ki: <input type="range" id="kiSlider" min="0" max="0.5" step="0.0001" value="0.0000">
<span id="kiVal">0.0</span></label><br>
<label>Kd: <input type="range" id="kdSlider" min="0" max="0.5" step="0.001" value="0.000">
<span id="kdVal">0.0</span></label><br><br>
<strong>Error (right_color-left_color):</strong> <span id="errorLabel">0</span>
</br> <button
id="resetBtn"
class="px-2 py-1 bg-red-600 text-white text-sm font-semibold rounded hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-400"
>
</br> <button id="resetBtn"
class="px-2 py-1 bg-red-600 text-white text-sm font-semibold rounded hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-400">
Reset Robot Position
</button>
</button>
</div>
</div>
@ -1161,7 +1181,8 @@ while True:
<div class="prose">
<h2>Step 3: The Code</h2>
<p>The pid.py library is simple, we initialize it with a <code>P</code>, <code>I</code>, and
<code>D</code>.</p>
<code>D</code>.
</p>
</br>
<p>These stand for <code>Proportional</code>, <code>Integral</code>, and <code>Derivative</code></p>
</br>
@ -1243,6 +1264,8 @@ while True:
import { LineFollowerSim } from './LineFollowerSim.js';
// Bang-Bang controller (default)
const canvas1 = document.getElementById('canvasBangBang');
const sim1 = new LineFollowerSim(canvas1);