From 7e8a2b56668740d88414a83ffa4e57a75a1af2a6 Mon Sep 17 00:00:00 2001 From: Jake Date: Thu, 31 Jul 2025 01:04:30 +0800 Subject: [PATCH] added pid lesson --- LineFollowerSim.js | 89 ++++++++++++++++ index.html | 251 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 326 insertions(+), 14 deletions(-) create mode 100644 LineFollowerSim.js diff --git a/LineFollowerSim.js b/LineFollowerSim.js new file mode 100644 index 0000000..7dd53ee --- /dev/null +++ b/LineFollowerSim.js @@ -0,0 +1,89 @@ +export class LineFollowerSim { + constructor(canvas, updateBoxFn) { + this.canvas = canvas; + this.ctx = canvas.getContext("2d"); + + this.box = { + x: 250, + y: 150, + width: 40, + height: 20, + speed: 0.4, + dir: 0 + }; + + this.lineX = 250; + this.lineTargetX = 250; + this.lineWidth = 12; + + this.updateBox = updateBoxFn || this.defaultUpdateBox.bind(this); + this.loop = this.loop.bind(this); + } + + updateLine() { + if (Math.random() < 0.02) { + this.lineTargetX = 250 + (Math.random() - 0.5) * 200; + } + + this.lineX += (this.lineTargetX - this.lineX) * 0.005; + } + + defaultUpdateBox() { + const leftSensor = this.box.x - this.box.width / 2; + const rightSensor = this.box.x + this.box.width / 2; + + const onLine = x => x >= this.lineX - this.lineWidth / 2 && x <= this.lineX + this.lineWidth / 2; + + 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; + } + + this.box.x += this.box.dir * this.box.speed; + } + + draw() { + const ctx = this.ctx; + const { x, y, width, height } = this.box; + + ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + + // Line + ctx.beginPath(); + ctx.moveTo(this.lineX, 0); + ctx.lineTo(this.lineX, this.canvas.height); + ctx.strokeStyle = "blue"; + ctx.lineWidth = this.lineWidth; + ctx.stroke(); + + // Box + ctx.fillStyle = "red"; + ctx.fillRect(x - width / 2, y - height / 2, width, height); + + // Sensors + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc(x - width / 2, y - height / 2, 3, 0, Math.PI * 2); + ctx.fill(); + ctx.beginPath(); + ctx.arc(x + width / 2, y - height / 2, 3, 0, Math.PI * 2); + ctx.fill(); + } + + loop() { + this.updateLine(); + this.updateBox(); + this.draw(); + requestAnimationFrame(this.loop); + } + + start() { + this.loop(); + } +} diff --git a/index.html b/index.html index d09a663..c8ff19b 100644 --- a/index.html +++ b/index.html @@ -31,6 +31,7 @@ + @@ -909,8 +910,8 @@ while True:

RP2040 GP26 ------ SONAR TRIG

RP2040 GP28 ------ SONAR ECHO


- - + +
Robot turning right

adafruit_hcsr04.py


-

Note the timeout=0.005, this is important as it tells the code how long to wait for an ECHO. If we don't have that, our code could be blocked for quite a lot of time if the only obstacles are a long way away.

+

Note the timeout=0.005, this is important as it tells the code how long to wait for + an ECHO. If we don't have that, our code could be blocked for quite a lot of time if the only + obstacles are a long way away.


-

Even though all we really need is to get the distance with sonar.distance, when no echo is received in time an error will occur that will stop your code from running.

-
-

With our try-except block, any errors will just run whatever is in the except part. In this case, nothing.

+

Even though all we really need is to get the distance with sonar.distance, when no + echo is received in time an error will occur that will stop your code from running.

+
+

With our try-except block, any errors will just run whatever is in the + except part. In this case, nothing. +


@@ -957,9 +963,12 @@ while True:
                 
                 

Step 3: Clean it up a bit

-

To tidy up our main loop, we can move all that to a function so the main loop just needs get_distance().

-
-

If your code is getting quite long, you might even take functions like this and put them in your own module to keep your main code easy to read.

+

To tidy up our main loop, we can move all that to a function so the main loop just needs + get_distance(). +

+
+

If your code is getting quite long, you might even take functions like this and put them in your + own module to keep your main code easy to read.


@@ -993,7 +1002,7 @@ while True:
 
             
- +
@@ -1061,10 +1070,11 @@ class ThumbInput:

Step 2: Using the recieved signal

Import just the ThumbInput part of the library.

-
-

Initialize the receiver with receiver = ThumbInput()

-
-

Create a new loop as this won't work nicely with any i2c, this loop should be placed after the motors are created, and before the i2c is initialized.

+
+

Initialize the receiver with receiver = ThumbInput()

+
+

Create a new loop as this won't work nicely with any i2c, this loop should be placed after the + motors are created, and before the i2c is initialized.


@@ -1085,6 +1095,123 @@ while True:
 
         
 
+
+        
+        
+
     
+