added sonar filtering page

master
Jake 2026-04-30 23:11:49 +08:00
parent 5f0ff276e9
commit e24b957fa0
4 changed files with 262 additions and 16 deletions

BIN
images/sonar_noise.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -28,6 +28,7 @@
<button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson6">OLED Display</button>
<button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson7">RGB LED(Neopixel)</button>
<button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson8">Sonar</button>
<button class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson8b">Sonar Filtering</button>
<button class="tab-btn text-gray-600 hover:text-blue-600 hidden" data-target="lesson9">Motor
Encoders</button>
<button hidden class="tab-btn text-gray-600 hover:text-blue-600" data-target="lesson10">Reciever</button>
@ -48,13 +49,16 @@
<!-- Step 1 -->
<div class="prose">
<h2>Step 1: Setting up the IDE</h2>
<p>There are many IDEs that we can use to program MicroPython robot, we'll be using one called "Thonny". So let's begin by downloading it.</p>
<p>There are many IDEs that we can use to program MicroPython robot, we'll be using one called
"Thonny". So let's begin by downloading it.</p>
<h3>Download</h3>
<ul class="ml-6 list-disc">
<li><a href="https://github.com/thonny/thonny/releases/download/v4.1.7/thonny-4.1.7.exe" class="text-blue-600 hover:underline">Windows</a>
<li><a href="https://github.com/thonny/thonny/releases/download/v4.1.7/thonny-4.1.7.exe"
class="text-blue-600 hover:underline">Windows</a>
</li>
<li><a href="https://github.com/thonny/thonny/releases/download/v4.1.7/thonny-4.1.7.pkg" class="text-blue-600 hover:underline">Mac</a></li>
<li><a href="https://github.com/thonny/thonny/releases/download/v4.1.7/thonny-4.1.7.pkg"
class="text-blue-600 hover:underline">Mac</a></li>
</ul>
<p>You can also get it from the Thonny website, <a
href="https://thonny.org/">https://thonny.org/</a></p>
@ -69,13 +73,16 @@
<ol class="list-decimal ml-6">
<li>In Thonny, click on the Run->Configure Interpreter buttons at the top of the window.</li>
<li>Choose "MicroPython (Raspberry Pi Pico) from the first dropdown menu.</li>
<li>Click the "<u>Install or update MicroPython</u>" button down the bottom-right of the window.</li>
<li>You need to put your device in FLASH mode, hold down the BOOT button, press and release the RESET button.</li>
<li>Click the "<u>Install or update MicroPython</u>" button down the bottom-right of the window.
</li>
<li>You need to put your device in FLASH mode, hold down the BOOT button, press and release the
RESET button.</li>
<li>You should now find a drive called "RPI-RP2". in the Target volume dropdown, shoose it.</li>
<li>Choose the variant "Raspberry Pi Pico - Pico / Pico H"</li>
<li>Press Install button to install the firmware.</li>
<li>Reset the device again with the RESET button.</li>
<li>Press the cancel button to close the firmware update panel, then press OK to officially begin coding.</li>
<li>Press the cancel button to close the firmware update panel, then press OK to officially
begin coding.</li>
</ol>
</div>
<div>
@ -100,8 +107,10 @@
<!-- Step 1 -->
<div class="prose">
<h2>Step 1: Open/Create the main.py file</h2>
<p>When your device is plugged into your computer, you should be able to press the File->Load button, and choose "Raspberry Pi Pico" to load from.</p>
<p>If no such file exists we need to make it, press File->New, and then save it to the Raspberry Pi Pico as "main.py"</p>
<p>When your device is plugged into your computer, you should be able to press the File->Load
button, and choose "Raspberry Pi Pico" to load from.</p>
<p>If no such file exists we need to make it, press File->New, and then save it to the Raspberry Pi
Pico as "main.py"</p>
</div>
<div>
@ -110,7 +119,8 @@
<!-- Step 2: Image -->
<div class="prose">
<h2>Step 2: Open the Serial Monitor</h2>
<p>To get messages back from your device, and to read errors, press the view->shellbutton and make sure
<p>To get messages back from your device, and to read errors, press the view->shellbutton and make
sure
you have the shell window open at the bottom.</p>
</br>
@ -119,7 +129,8 @@
<code>print("Hello World")</code> and then save, yuou should see the message appear in the
Serial monitor.
</p>
<p>Note that the code won't persist on the robot after a reset unless you also SAVE it to the device, RUN just runs it once</p>
<p>Note that the code won't persist on the robot after a reset unless you also SAVE it to the
device, RUN just runs it once</p>
</br>
<h2>Step 4: Code all the things!</h2>
@ -132,7 +143,7 @@
<img src="images/thonny_code.png" alt="Robot turning right"
class="rounded shadow w-full max-w-xs md:max-w-full" />
</div>
</div>
</section>
@ -908,10 +919,13 @@ while True:
<!-- Step 2 -->
<div class="prose">
<h2>Step 2: Coding</h2>
<p>We'll use a library to handle sending and listening for the pulses. Put this module on your device.</p>
<p>We'll use a library to handle sending and listening for the pulses. Put this module on your
device.</p>
</br>
<p>When the "distance()" function is called, it sends a short pulse on the trigger pin, then waits for a response on the echo pin. The time it takes for the echo to return is used to calculate the distance.</p>
<p>When the "distance()" function is called, it sends a short pulse on the trigger pin, then waits
for a response on the echo pin. The time it takes for the echo to return is used to calculate
the distance.</p>
</div>
<div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
@ -955,7 +969,8 @@ class Sonar:
<!-- Step 3 -->
<div class="prose">
<h2>Step 3: Call it in the main.py</h2>
<p>Now all we need to do is create the object and call the distance function in the main.py file.</p>
<p>Now all we need to do is create the object and call the distance function in the main.py file.
</p>
</div>
<div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
@ -1076,6 +1091,237 @@ while True:
</section>
<!-- Lesson 6 (hidden initially) -->
<section id="lesson8b" class="lesson-content hidden">
<h1 class="text-3xl font-bold mb-6">Sonar Filtering</h1>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Step 1 -->
<div class="prose">
<h2>Step 1: Understand the problem</h2>
<p>Like most inputs, the sonar data is noisy, and there are different types of noise.</p>
<p>On the right you can see a fairly fixed output, but with sudden spikes of irregular data.</p>
</br>
<p>So we need code that will filter our spikes like that, the easiest is to just throw them away. To
do this we'll use a simple moving average filter.</p>
</div>
<div>
<img src="images/sonar_noise.png" alt="Robot turning right"
class="rounded shadow w-full max-w-xs md:max-w-full" />
</div>
<!-- Step 2 -->
<div class="prose">
<h2>Step 2: Coding a moving average filter</h2>
<p>Instead of just taking single data points and making decisions, we'll keep an array of the last 8
readings.</p>
</br>
<p>First we need to start keeping a rolling buffer of the last 8 readings.</p>
<p>We'll use a list to store the readings, and we'll use the <code>append()</code> method to add new
readings to the end of the list.</p>
<p>We'll use the <code>pop(0)</code> method to remove the oldest reading from the start of the list.
</p>
<p>We'll use the <code>len()</code> method to check the size of the list.</p>
<p>We'll use the <code>print()</code> method to print the list.</p>
</br>
<p><b>NOTE:</b> We can get smoother results by using a larger buffer size, but this can also start
to introduce lag.</p>
</div>
<div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
BUFFER_SIZE = 8 # The size of the buffer
buffer = [] # Create an empty array to hold
# the readings (this is the rolling buffer)
while True:
dist = sonar.distance() # Get a new reading
buffer.append(dist) # Add it to the buffer
# If the buffer is too big, remove the oldest reading
if len(buffer) > BUFFER_SIZE:
buffer.pop(0)
print(buffer) # Print the buffer
</code></pre>
</div>
<!-- Step 3 -->
<div class="prose">
<h2>Step 3: Take the average</h2>
<p>Now all we need to do is create the object and call the distance function in the main.py file.
</p>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
def average_filter(values):
total = 0
for i in range(len(values)):
total = total + values[i]
return total / len(values)
# Call in the main loop
filtered = average_filter(values)
print(filtered)
</code>
</pre>
</div>
<div>
<img src="images/sonar_noise_averaged.png" alt="Robot turning right"
class="rounded shadow w-full max-w-xs md:max-w-full" />
</div>
<div class="prose">
<h2>Step 4: Take the median</h2>
<p>That's better, but those large peaks are still throwing our average off quite a bit. We can improve this by taking the MEDIAN of the buffer rather than the average.
</p>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
def median_filter(values):
sorted_vals = sorted(values) # sort the values from low->high
mid = int(len(sorted_vals) / 2) # get the middle index
return sorted_vals[mid] # return the value of the median
# Call in the main loop
filtered = median_filter(values)
print(filtered)
</code>
</pre>
</div>
<div>
<img src="images/sonar_noise_median.png" alt="Robot turning right"
class="rounded shadow w-full max-w-xs md:max-w-full" />
</div>
<div class="prose">
<h2>Step 5: Fine tuning</h2>
<p>Before moving on, play with the buffer size to see how it effects the results AND the responsiveness to changes.</p>
<p>Try and find a good balance with the time.sleep() value as well</p>
</div>
</section>
<!-- Lesson 6 (hidden initially) -->
<section id="lesson10" class="lesson-content hidden">
<h1 class="text-3xl font-bold mb-6">Receiver</h1>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Step 2 -->
<div class="prose">
<h2>Step 1: Coding</h2>
<p>Create a new file called <code>remote.py</code> and add the code to the right.</p>
</div>
<div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
# remote.py
import board
import busio
START_BYTE = 0xAA
END_BYTE = 0xBB
PACKET_LENGTH = 8
# Calibration values
LEFT_MIN = 0
LEFT_MID = 131
LEFT_MAX = 203
RIGHT_MIN = 51
RIGHT_MID = 120
RIGHT_MAX = 255
class ThumbInput:
def __init__(self, tx=board.GP0, rx=board.GP1, baudrate=115200):
self.uart = busio.UART(tx=tx, rx=rx, baudrate=baudrate, timeout=0.1)
self.buffer = bytearray()
def read(self):
"""Returns (left_percent, right_percent) in range [-100, 100], or None if packet incomplete."""
data = self.uart.read(1)
if data:
byte = data[0]
self.buffer.append(byte)
if len(self.buffer) > PACKET_LENGTH:
self.buffer = self.buffer[-PACKET_LENGTH:]
if len(self.buffer) == PACKET_LENGTH and self.buffer[0] == START_BYTE and self.buffer[-1] == END_BYTE:
payload = self.buffer[1:7]
self.buffer = bytearray()
left_raw = payload[1]
right_raw = payload[3]
left = self._map_thumbstick(left_raw, LEFT_MIN, LEFT_MID, LEFT_MAX)
right = self._map_thumbstick(right_raw, RIGHT_MIN, RIGHT_MID, RIGHT_MAX)
return (int(left), int(right))
return None
def _map_thumbstick(self, x, min_val, mid_val, max_val):
if x < mid_val:
return (x - mid_val) / (mid_val - min_val) * 100
else:
return (x - mid_val) / (max_val - mid_val) * 100
</code></pre>
</div>
<!-- Step 3 -->
<div class="prose">
<h2>Step 2: Using the recieved signal</h2>
<p>Import just the <code>ThumbInput</code> part of the library.</p>
</br>
<p>Initialize the receiver with <code>receiver = ThumbInput()</code></p>
</br>
<p>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.</p>
</div>
<div>
<pre class="bg-gray-100 p-4 rounded shadow text-sm"><code class="language-python">
from remote import ThumbInput
receiver = ThumbInput()
while True:
result = receiver.read()
if result:
left_motor.move(result[0])
right_motor.move(result[1])
#print("Left:", result[0], "Right:", result[1])
time.sleep(0.001)
</code></pre>
</div>
</section>
<!-- Lesson 6 (hidden initially) -->
<section id="lesson11" class="lesson-content hidden">
<h1 class="text-3xl font-bold mb-6">PID</h1>