Skip to content

Commit 0c859d1

Browse files
authored
Create python-radar-plotter.py
Python code used to read Arduino angle and distance output, and plot them on a polar plot that emulates a radar plan position indicator
1 parent ad2ca5d commit 0c859d1

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed

python-radar-plotter.py

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# Python + Arduino-based Radar Plotter
2+
#
3+
# ** Works with any motor that outputs angular rotation
4+
# ** and with any distance sensor (HC-SR04, VL53L0x,LIDAR)
5+
#
6+
import numpy as np
7+
import matplotlib
8+
matplotlib.use('TkAgg')
9+
import matplotlib.pyplot as plt
10+
from matplotlib.widgets import Button
11+
import serial,sys,glob
12+
import serial.tools.list_ports as COMs
13+
#
14+
#
15+
############################################
16+
# Find Arudino ports, select one, then start communication with it
17+
############################################
18+
#
19+
def port_search():
20+
if sys.platform.startswith('win'): # Windows
21+
ports = ['COM{0:1.0f}'.format(ii) for ii in range(1,256)]
22+
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
23+
ports = glob.glob('/dev/tty[A-Za-z]*')
24+
elif sys.platform.startswith('darwin'): # MAC
25+
ports = glob.glob('/dev/tty.*')
26+
else:
27+
raise EnvironmentError('Machine Not pyserial Compatible')
28+
29+
arduinos = []
30+
for port in ports: # loop through to determine if accessible
31+
if len(port.split('Bluetooth'))>1:
32+
continue
33+
try:
34+
ser = serial.Serial(port)
35+
ser.close()
36+
arduinos.append(port) # if we can open it, consider it an arduino
37+
except (OSError, serial.SerialException):
38+
pass
39+
return arduinos
40+
41+
arduino_ports = port_search()
42+
ser = serial.Serial(arduino_ports[0],baudrate=115200) # match baud on Arduino
43+
ser.flush() # clear the port
44+
#
45+
############################################
46+
# Start the interactive plotting tool and
47+
# plot 180 degrees with dummy data to start
48+
############################################
49+
#
50+
fig = plt.figure(facecolor='k')
51+
win = fig.canvas.manager.window # figure window
52+
screen_res = win.wm_maxsize() # used for window formatting later
53+
dpi = 150.0 # figure resolution
54+
fig.set_dpi(dpi) # set figure resolution
55+
56+
# polar plot attributes and initial conditions
57+
ax = fig.add_subplot(111,polar=True,facecolor='#006d70')
58+
ax.set_position([-0.05,-0.05,1.1,1.05])
59+
r_max = 100.0 # can change this based on range of sensor
60+
ax.set_ylim([0.0,r_max]) # range of distances to show
61+
ax.set_xlim([0.0,np.pi]) # limited by the servo span (0-180 deg)
62+
ax.tick_params(axis='both',colors='w')
63+
ax.grid(color='w',alpha=0.5) # grid color
64+
ax.set_rticks(np.linspace(0.0,r_max,5)) # show 5 different distances
65+
ax.set_thetagrids(np.linspace(0.0,180.0,10)) # show 10 angles
66+
angles = np.arange(0,181,1) # 0 - 180 degrees
67+
theta = angles*(np.pi/180.0) # to radians
68+
dists = np.ones((len(angles),)) # dummy distances until real data comes in
69+
pols, = ax.plot([],linestyle='',marker='o',markerfacecolor = 'w',
70+
markeredgecolor='#EFEFEF',markeredgewidth=1.0,
71+
markersize=10.0,alpha=0.9) # dots for radar points
72+
line1, = ax.plot([],color='w',
73+
linewidth=4.0) # sweeping arm plot
74+
75+
# figure presentation adjustments
76+
fig.set_size_inches(0.96*(screen_res[0]/dpi),0.96*(screen_res[1]/dpi))
77+
plot_res = fig.get_window_extent().bounds # window extent for centering
78+
win.wm_geometry('+{0:1.0f}+{1:1.0f}'.\
79+
format((screen_res[0]/2.0)-(plot_res[2]/2.0),
80+
(screen_res[1]/2.0)-(plot_res[3]/2.0))) # centering plot
81+
fig.canvas.toolbar.pack_forget() # remove toolbar for clean presentation
82+
fig.canvas.set_window_title('Arduino Radar')
83+
84+
fig.canvas.draw() # draw before loop
85+
axbackground = fig.canvas.copy_from_bbox(ax.bbox) # background to keep during loop
86+
87+
############################################
88+
# button event to stop program
89+
############################################
90+
91+
def stop_event(event):
92+
global stop_bool
93+
stop_bool = 1
94+
prog_stop_ax = fig.add_axes([0.85,0.025,0.125,0.05])
95+
pstop = Button(prog_stop_ax,'Stop Program',color='#FCFCFC',hovercolor='w')
96+
pstop.on_clicked(stop_event)
97+
# button to close window
98+
def close_event(event):
99+
global stop_bool,close_bool
100+
if stop_bool:
101+
plt.close('all')
102+
stop_bool = 1
103+
close_bool = 1
104+
close_ax = fig.add_axes([0.025,0.025,0.125,0.05])
105+
close_but = Button(close_ax,'Close Plot',color='#FCFCFC',hovercolor='w')
106+
close_but.on_clicked(close_event)
107+
108+
fig.show()
109+
110+
############################################
111+
# inifinite loop, constantly updating the
112+
# 180deg radar with incoming Arduino data
113+
############################################
114+
#
115+
start_word,stop_bool,close_bool = False,False,False
116+
while True:
117+
try:
118+
if stop_bool: # stops program
119+
fig.canvas.toolbar.pack_configure() # show toolbar
120+
if close_bool: # closes radar window
121+
plt.close('all')
122+
break
123+
ser_bytes = ser.readline() # read Arduino serial data
124+
decoded_bytes = ser_bytes.decode('utf-8') # decode data to utf-8
125+
data = (decoded_bytes.replace('\r','')).replace('\n','')
126+
if start_word:
127+
vals = [float(ii) for ii in data.split(',')]
128+
if len(vals)<2:
129+
continue
130+
angle,dist = vals # separate into angle and distance
131+
if dist>r_max:
132+
dist = 0.0 # measuring more than r_max, it's likely inaccurate
133+
dists[int(angle)] = dist
134+
if angle % 5 ==0: # update every 5 degrees
135+
pols.set_data(theta,dists)
136+
fig.canvas.restore_region(axbackground)
137+
ax.draw_artist(pols)
138+
139+
line1.set_data(np.repeat((angle*(np.pi/180.0)),2),
140+
np.linspace(0.0,r_max,2))
141+
ax.draw_artist(line1)
142+
143+
fig.canvas.blit(ax.bbox) # replot only data
144+
fig.canvas.flush_events() # flush for next plot
145+
else:
146+
if data=='Radar Start': # stard word on Arduno
147+
start_word = True # wait for Arduino to output start word
148+
print('Radar Starting...')
149+
else:
150+
continue
151+
152+
except KeyboardInterrupt:
153+
plt.close('all')
154+
print('Keyboard Interrupt')
155+
break

0 commit comments

Comments
 (0)