## Videos

Watch the associated videos here:

## Ultrasonic Radar - how it works

We can build a simple, radar like scanning system by attaching an Ultrasonic Range Finder a Servo, and rotate the servo about whilst taking readings.

Specifically, we will rotate the servo 1 degree at a time, take a distance reading, output the reading to the radar display, and then move to the next angle until the entire sweep is complete.

Later, in another part of this series we’ll send the set of readings to a trained ML model and see if it can recognise any objects within the scan.

## Radar display

### Drawing the Radar

SOHCAHTOA - It’s all about triangles!

We want to create a radar-like display. The scan will sweep round a 180° arc, and any objects in front of the range finder will display on the scan, proportionate to the display.

The display will be housed on the back of the robot (we’ll add this in a later part).

### PicoGraphics

We’ll use the Pimoroni MicroPython as it includes their PicoGraphics library, which is great for drawing vector graphics.

PicoGraphics has a `line` primitive takes X1, Y1, X2, Y2 coordinates. We can use this to draw our radar sweep.

### The Display

The display I’ve chosen for this project is a 240x240 colour display - you can grab one from here: https://shop.pimoroni.com/products/1-3-spi-colour-lcd-240x240-breakout.

The display coordinates `X, Y` `0, 0` are at the top left of the display.

This display uses an ST7789V display driver which also happens to be built into the Pimoroni Pico Explorer Base, which I used to prototype this project.

Other specifications for this display:

• It has 240 x 240 pixels
• Square 1.3” IPS LCD display
• Uses the SPI bus

I’m looking at putting the breakout version of this display on the robot, in a later part of the series.

### Drawing the sweep

We will draw a series of lines, one for each of the 180° angles of the sweep.

To draw the line we need to solve a triangle to find the `x1` and `y1` start positions of the line We can then use PicoGraphics function:

``````display.line(x1, y1, x2, y2)
``````

We need to solve the triangle to find the position of `x1, y1`.

We know what `x2, y2`is:

• `y2` is the bottom of the screen (`height`)
• `x2` = its the middle of the screen (`width /2`)
• We know the length of side c of the triangle, angle `A` as well as angle `C`
• We need to find the length of side a (`y1`), and length of side b (`x1`, or more accurately `middle - b`)

### AAS Triangle

Angle, Angle, Side

• We can solve Angle B by subtracting 180 from A+C (which we already know)
• We can solve sides a and b using the AAS formula:
• side a = `a/sin A = c/sin C`
• side b = `b/sin B = c/sin C`

## 3D Design

### Chassis

This robot uses the Explora base.

The Explora base is a simple, quick to print and easy to reproduce Chassis for building robots. It’s 3mm thick, very quick to print, Solid, doesn’t bend, and easy to attach motors and wheels.

### Explora Blueprint

The Explora base starts with a 90 x 70mm rectangle, has four ‘tabs’; one for each the wheel. There are also front and rear sections.

You will want to add the holes and mounting points depending on your own design.

### Servo holder

The Servo holder sits on top of the chassis and is held in place by 3x `M3` captive nut and screws.

### Servo

Servo screws in from underneath. You can use any commonly available servo, including:

• SG90
• MG90
• DS929MG
• TowerPro MG92B

Use the two larger screws included with the Servo to secure the servo to the servo holder.

### Range Finder Holder

The Range Finder holder attaches the Servo Horn to the Servo.

Ensure you center the Servo and face range finder straight ahead before screwing it in.

Secure the servo horn to the servo spindle using the small screw included with the servo.

## Ultrasonic Range Finder

Add Ultrasonic Range Finder to the back of the Range Finder holder; it should just push-fit; no glue or screws required.

Connect 4 Dupont cables to:

• GND
• VCC
• Trigger
• Echo

## MicroPython code

Download the latest version of the code from GitHub: https://github.com/kevinmcaleer/radar_robot

### Radar.py

Radar.py will scan the area in front of the robot by rotating the range finder. Each of the readings will be written to a `readings.csv` file on the Pico.

``````# radar.py
# Kevin McAleer
# Nov 2022

from servo import Servo
from time import sleep
from range_finder import RangeFinder

from machine import Pin

trigger_pin = 2
echo_pin = 3

DATA_FILE = 'readings.csv'

s = Servo(0)
r = RangeFinder(trigger_pin=trigger_pin, echo_pin=echo_pin)

def take_readings(count):
readings = []
with open(DATA_FILE, 'ab') as file:
for i in range(0, 90):
s.value(i)
value = r.distance
print(f'distance: {value}, angle {i} degrees, count {count}')
sleep(0.01)
for i in range(90,-90, -1):
s.value(i)
value = r.distance
readings.append(value)
print(f'distance: {value}, angle {i} degrees, count {count}')
sleep(0.01)
for item in readings:
file.write(f'{item}, ')
file.write(f'{count} \n')

print('wrote datafile')
for i in range(-90,0,1):
s.value(i)
value = r.distance
print(f'distance: {value}, angle {i} degrees, count {count}')
sleep(0.05)

def demo():
for i in range(-90, 90):
s.value(i)
print(f's: {s.value()}')
sleep(0.01)
for i in range(90,-90, -1):
s.value(i)
print(f's: {s.value()}')
sleep(0.01)

def sweep(s,r):
""" Returns a list of readings from a 180 degree sweep """

readings = []

for i in range(-90,90):
s.value(i)
sleep(0.01)
readings.append(r.distance)
return readings

for count in range(1,2):
take_readings(count)
sleep(0.25)
``````

### Radar_Display.py

``````from picographics import PicoGraphics, DISPLAY_PICO_EXPLORER
import gc
from math import sin, radians
gc.collect()
from time import sleep
from range_finder import RangeFinder
from machine import Pin
from servo import Servo
from motor import Motor

m1 = Motor((4, 5))
m1.enable()

# run the motor full speed in one direction for 2 seconds
m1.to_percent(100)

trigger_pin = 2
echo_pin = 3

s = Servo(0)
r = RangeFinder(trigger_pin=trigger_pin, echo_pin=echo_pin)

display = PicoGraphics(DISPLAY_PICO_EXPLORER, rotate=0)
WIDTH, HEIGHT = display.get_bounds()

REALLY_DARK_GREEN = {'red':0, 'green':64, 'blue':0}
DARK_GREEN = {'red':0, 'green':128, 'blue':0}
GREEN = {'red':0, 'green':255, 'blue':0}
LIGHT_GREEN = {'red':255, 'green':255, 'blue':255}
BLACK = {'red':0, 'green':0, 'blue':0}

def create_pen(display, color):
return display.create_pen(color['red'], color['green'], color['blue'])

black = create_pen(display, BLACK)
green = create_pen(display, GREEN)
dark_green = create_pen(display, DARK_GREEN)
really_dark_green = create_pen(display, REALLY_DARK_GREEN)
light_green = create_pen(display, LIGHT_GREEN)

length = HEIGHT //2
middle = WIDTH // 2

angle = 0

def calc_vectors(angle, length):
# Solve and AAS triangle
# angle of c is
#
#  B        x1, y1
#  |\        \
#  | \        \
# a|_ \c       \
#  |_|_\        \
# C  b  A       x2,y2

A = angle
C = 90
B = (180 - C) - angle
c = length
a = int((c * sin(radians(A))) / sin(radians(C))) # a/sin A = c/sin C
b = int((c * sin(radians(B))) / sin(radians(C)))  # b/sin B = c/sin C
x1 = middle - b
y1 = (HEIGHT -1) - a
x2 = middle
y2 = HEIGHT -1

#     print(f'a:{a}, b:{b}, c:{c}, A:{A}, B:{B}, C:{C}, angle: {angle}, length {length}, x1: {x1}, y1: {y1}, x2: {x2}, y2: {y2}')
return x1, y1, x2, y2

a = 1
while True:

#     print(f'x1:{x1}, y1:{y1}, x2:{x2}, y2:{y2}')
s.value(a)
distance = r.distance
if a > 1:
x1, y1, x2, y2 = calc_vectors(a-1, 100)
display.set_pen(really_dark_green)

display.line(x1, y1, x2, y2)

if a > 2:
x1, y1, x2, y2 = calc_vectors(a-2, 100)
display.set_pen(dark_green)
display.line(x1, y1, x2, y2)

#     if a > 3:
#         x1, y1, x2, y2 = calc_vectors(a-3, 100)
#         display.set_pen(black)
#         display.line(x1, y1, x2, y2)

# Draw the full length
x1, y1, x2, y2 = calc_vectors(a, 100)
display.set_pen(light_green)
display.line(x1, y1, x2, y2)

# Draw lenth as a % of full scan range (1200mm)
scan_length = int(distance * 3)
if scan_length > 100: scan_length = 100
print(f'Scan length is {scan_length}, distance is: {distance}')
x1, y1, x2, y2 = calc_vectors(a, scan_length)
display.set_pen(green)
display.line(x1, y1, x2, y2)

display.update()

a += 1
if a > 180:
a = 1
display.set_pen(black)
display.clear()
display.update()
``````

## STL files

Download the STL files for this project here:

Difficulty:
Type: project
Categories:
robots pico 3dprinting

Kevin McAleer

I build robots, bring them to life with code, and have a whole load of fun along the way

Social Links:

### Did you find this content useful?

If you found this high quality content useful please consider supporting my work, so I can continue to create more content for you.

I give away all my content for free: Weekly video content on YouTube, 3d Printable designs, Programs and Code, Reviews and Project write-ups, but 98% of visitors don't give back, they simply read/watch, download and go. If everyone who reads or watches my content, who likes it, helps fund it just a little, my future would be more secure for years to come. A price of a cup of coffee is all I ask.

There are a couple of ways you can support my work financially:

If you can't afford to provide any financial support, you can also help me grow my influence by doing the following:

Thank you again for your support and helping me grow my hobby into a business I can sustain.
- Kevin McAleer