implemented controls on bang bang simulator
parent
6f84d1070a
commit
2bf27c264d
|
|
@ -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;
|
||||
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 {
|
||||
//this.box.dir = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
43
index.html
43
index.html
|
|
@ -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"
|
||||
>
|
||||
Reset Robot Position
|
||||
</button>
|
||||
</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>
|
||||
|
||||
</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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue