1
votes

I am trying to visualize data from a sonar sensor that turns 180 degrees back and forth.

After reading the sensor data and processing it with a regex variable "dist" contains a float representing distance in cm. The variable "angl" contains an integer between 0 and 180 representing rotation state.

So far so good, but I can't quite wrap my mind around how to represent this on a canvas with Tkinter. I've been trying all sorts of things but I've cleaned up the code a little bit for this thread.

The goal is to have point (1000, 1000) on the canvas as a center and move the green circle to (x, y) coordinates scaled accordingly.

Here is a sample readout from the terminal

ANGLE: 174
DISTANCE: 208.11
X: -72.99856014995218
Y: -194.88710146142
ANGLE: 175
DISTANCE: 161.67
X: 96.75694368800949
Y: -129.51943000243384
ANGLE: 176
DISTANCE: 100.88
X: 100.62718668260311
Y: 7.13748557578522
ANGLE: 177
DISTANCE: 43.61
X: 20.907170903220738
Y: 38.27169064496002

import serial
import re
import math
import tkinter
import time

w_width = 2000
w_height = 1000


def create_animation_window():
    window = tkinter.Tk()
    window.title("WALL-E SONAR")
    window.geometry(f'{w_width}x{w_height}')
    return window

def create_animation_canvas(window):
    canvas = tkinter.Canvas(window)
    canvas.configure(bg="black")
    canvas.pack(fill="both", expand=True)
    return canvas


ser = serial.Serial('/dev/ttyACM0', 9600)
strPort = '/dev/ttyACM0'
def animate_sonar(window, canvas):
    intercept = canvas.create_oval(0,0,20,20, fill="green")

    while True:
        rawData = ser.readline() # rawData input example: b'D:140.98A:57\r\n
        decodedData = rawData.decode("utf-8")
        line = re.search(r'D:(.*)A:(.*)\r\n', decodedData)

        if line:
            dist = float(line.group(1))
            angl = int(line.group(2))


            print(f"ANGLE:  {angl}")
            print(f"DISTANCE: {dist}")
            x = dist * math.cos(angl)
            y = dist * math.sin(angl)
            print(f"X:  {x}")
            print(f"Y:  {y}")

            canvas.moveto(intercept, x,y)

            window.update()



animation_window = create_animation_window()
animation_canvas = create_animation_canvas(animation_window)
animate_sonar(animation_window, animation_canvas)
1
'Canvas' object has no attribute 'moveto' have you tried just drawing something before you jump into animating itHelder Sepulveda
What do you mean? I have no problem using canvas.moveto()Jimmy
You must have something I don't because that is the error I get on the console running your codeHelder Sepulveda
Nope, no such problems. It's the math and mapping the values that are causing me a head ache.Jimmy

1 Answers

0
votes

There are few things I see:

  • The angle that you use in sin and cos should be in radians
  • You have to use positions relative to your canvas

Here is what I would do:

import math
import tkinter
import time

window = tkinter.Tk()
window.title("WALL-E SONAR")
window.geometry(f'2000x1000')

canvas = tkinter.Canvas(window)
canvas.configure(bg="black")
canvas.pack(fill="both", expand=True)


def animate_sonar(window, canvas):
    dist = 200
    for angl in range(180, 360):
        rad = angl * math.pi / 180
        x = dist * math.cos(rad) + 300
        y = dist * math.sin(rad) + 300

        canvas.delete("all")
        canvas.create_oval(x-10, y-10, x+10, y+10, fill="green")
        canvas.create_line(300, 300, x, y, fill="red")
        window.update()
        time.sleep(0.05)


animate_sonar(window, canvas)
window.mainloop()

If you run that you should see something like:

The 300 in my code is there is just to put something, but you have to play with the dimensions of your window and canvas to place it correctly where you want.

        rad = angl * math.pi / 180
        x = dist * math.cos(rad) + 300
        y = dist * math.sin(rad) + 300

Start with something really simple, mock the data, that will make it easier for others to reproduce your code, then incorporate your sensor. When I first read the question I almost close the page, my first thought was no way I can reproduce this without that sensor...

I also used canvas.delete("all") your canvas.moveto gives me errors, but if it works for you perfect, use it in a similar way to my example.

hope that sends you on the right path...