├── .gitignore ├── README.md ├── figure.py └── main.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ### Python ### 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | pytestdebug.log 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | db.sqlite3-journal 66 | 67 | # Flask stuff: 68 | instance/ 69 | .webassets-cache 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | doc/_build/ 77 | 78 | # PyBuilder 79 | target/ 80 | 81 | # Jupyter Notebook 82 | .ipynb_checkpoints 83 | 84 | # IPython 85 | profile_default/ 86 | ipython_config.py 87 | 88 | # pyenv 89 | .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 99 | __pypackages__/ 100 | 101 | # Celery stuff 102 | celerybeat-schedule 103 | celerybeat.pid 104 | 105 | # SageMath parsed files 106 | *.sage.py 107 | 108 | # Environments 109 | .env 110 | .venv 111 | env/ 112 | venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | pythonenv* 117 | 118 | # Spyder project settings 119 | .spyderproject 120 | .spyproject 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | 136 | # pytype static type analyzer 137 | .pytype/ 138 | 139 | # profiling data 140 | .prof 141 | 142 | ### vscode ### 143 | .vscode/* 144 | !.vscode/settings.json 145 | !.vscode/tasks.json 146 | !.vscode/launch.json 147 | !.vscode/extensions.json 148 | *.code-workspace -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyIMU_3Dvisualizer 2 | IMU 3D visualizer with Python, OpenGL and MPU6050 3 | 4 | Note: The "Yaw" mode is not implemented yet. 5 | 6 | _Simulador 3D para acelerometro/gyroscopio MPU6050_ 7 | ![](https://drive.google.com/uc?export=view&id=1g5SrGnZ_wfWsqmL7pKUehwe6HWRJV3sw) 8 | ## Comenzando 🚀 9 | 10 | ### Pre-requisitos 📋 11 | 12 | * Python 3 13 | * Tkinter 14 | * pySerial 15 | * pyOpenGL 16 | 17 | * MPU6050 18 | * Microcontroller 19 | * USB-Serial Converter 20 | 21 | 22 | ## Wiki 📖 23 | 24 | Puedes ver más acerca del proyecto en el video [demo](https://www.youtube.com/watch?v=vh91z3-3ncE) 25 | 26 | Para comenzar es necesario contar con el microcontrolador encargado adquirir los datos del 27 | MPU6050 y enviarlos de forma serial. 28 | 29 | Aqui hay algunos ejemplos de como hacerlo utilizando: 30 | * [Microcip PIC16F1709 MCU](https://github.com/MA-Lugo/PIC16F1709_MPU6050_ex) 31 | * [STM32F1 MCU](https://github.com/MA-Lugo/STM32F1_MPU6050_lib/blob/main/Core/Src/main.c) 32 | 33 | ### Formato de la trama de datos: 34 | En la siguiente imagen se muestra el formato de la trama de datos necesarios. 35 | 36 | ![](https://drive.google.com/uc?export=view&id=1YwkYYE5qgLod-2wsBejAZDGvy2fpD29x) 37 | 38 | ## Pruebas 🛠️ 39 | 40 | ![](https://drive.google.com/uc?export=view&id=1FfHM9EbRliaT2iBzzAqZF8uXnvBeMvwJ) 41 | 42 | 43 | ## Autor ✒️ 44 | 45 | * **Mario A. Lugo** [MA-Lugo](https://github.com/MA-Lugo) 46 | -------------------------------------------------------------------------------- /figure.py: -------------------------------------------------------------------------------- 1 | ##Vertices 0-7 (x,y,z) 2 | vertices= ( 3 | (3, -.2, -1), 4 | (3, .2, -1), 5 | (-3, .2, -1), 6 | (-3, -.2, -1), 7 | (3, -.2, 1), 8 | (3, .2, 1), 9 | (-3, .2, 1), 10 | (-3, -.2, 1) 11 | ) 12 | 13 | edges = ( 14 | (0,1), 15 | (0,3), 16 | (0,4), 17 | (2,1), 18 | (2,3), 19 | (2,6), 20 | (5,1), 21 | (5,4), 22 | (5,6), 23 | (7,3), 24 | (7,4), 25 | (7,6) 26 | ) 27 | 28 | surfaces = ( 29 | (0,1,2,3), 30 | (4,5,6,7), 31 | (1,5,4,0), 32 | (3,2,6,7), 33 | (1,2,6,5), 34 | (0,3,7,4) 35 | ) 36 | 37 | colors = ( 38 | ((1.0/255*41),(1.0/255*217),(1.0/255*152)), 39 | ((1.0/255*41),(1.0/255*217),(1.0/255*152)), 40 | ((1.0/255*242),(1.0/255*66),(1.0/255*128)), 41 | ((1.0/255*242),(1.0/255*66),(1.0/255*128)), 42 | ((1.0/255*19),(1.0/255*94),(1.0/255*242)), 43 | ((1.0/255*242),(1.0/255*66),(1.0/255*128)), 44 | ) -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from pygame.locals import * 3 | from OpenGL.GL import * 4 | from OpenGL.GLU import * 5 | from figure import * 6 | from tkinter import * 7 | from tkinter import ttk 8 | import time 9 | import serial 10 | import threading 11 | 12 | ApplicationGL = False 13 | 14 | class PortSettings: 15 | Name = "COM1" 16 | Speed = 9600 17 | Timeout = 2 18 | class IMU: 19 | Roll = 0 20 | Pitch = 0 21 | Yaw = 0 22 | 23 | 24 | 25 | myport = PortSettings() 26 | myimu = IMU() 27 | 28 | def RunAppliction(): 29 | global ApplicationGL 30 | myport.Name = Port_entry.get() 31 | myport.Speed = Baud_entry.get() 32 | ApplicationGL = True 33 | ConfWindw.destroy() 34 | 35 | ConfWindw = Tk() 36 | ConfWindw.title("Configure Serial Port") 37 | ConfWindw.configure(bg = "#2E2D40") 38 | ConfWindw.geometry('300x150') 39 | ConfWindw.resizable(width=False, height=False) 40 | positionRight = int(ConfWindw.winfo_screenwidth()/2 - 300/2) 41 | positionDown = int(ConfWindw.winfo_screenheight()/2 - 150/2) 42 | ConfWindw.geometry("+{}+{}".format(positionRight, positionDown)) 43 | 44 | Port_label = Label(text = "Port:",font =("",12),justify= "right",bg = "#2E2D40",fg = "#FFFFFF") 45 | Port_label.place(x = 50, y =30,anchor = "center") 46 | Port_entry = Entry(width = 20,bg = "#37364D", fg = "#FFFFFF", justify = "center") 47 | Port_entry.insert(INSERT,myport.Name) 48 | Port_entry.place(x = 180, y = 30,anchor = "center") 49 | 50 | Baud_label = Label(text = "Speed:",font =("",12),justify= "right",bg = "#2E2D40",fg = "#FFFFFF") 51 | Baud_label.place(x = 50, y =80,anchor = "center") 52 | Baud_entry = Entry(width = 20,bg = "#37364D", fg = "#FFFFFF", justify = "center") 53 | Baud_entry.insert(INSERT,str(myport.Speed)) 54 | Baud_entry.place(x = 180, y = 80,anchor = "center") 55 | 56 | ok_button = Button(text = "Ok",width = 8,command = RunAppliction,bg="#135EF2",fg ="#FFFFFF") 57 | ok_button.place(x = 150, y = 120,anchor="center") 58 | 59 | def InitPygame(): 60 | global display 61 | pygame.init() 62 | display = (640,480) 63 | pygame.display.set_mode(display, DOUBLEBUF|OPENGL) 64 | pygame.display.set_caption('IMU visualizer (Press Esc to exit)') 65 | 66 | 67 | def InitGL(): 68 | glClearColor((1.0/255*46),(1.0/255*45),(1.0/255*64),1) 69 | glEnable(GL_DEPTH_TEST) 70 | glDepthFunc(GL_LEQUAL) 71 | glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) 72 | 73 | gluPerspective(100, (display[0]/display[1]), 0.1, 50.0) 74 | glTranslatef(0.0,0.0, -5) 75 | 76 | 77 | def DrawText(textString): 78 | font = pygame.font.SysFont ("Courier New",25, True) 79 | textSurface = font.render(textString, True, (255,255,0), (46,45,64,255)) 80 | textData = pygame.image.tostring(textSurface, "RGBA", True) 81 | glDrawPixels(textSurface.get_width(), textSurface.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, textData) 82 | 83 | def DrawBoard(): 84 | 85 | glBegin(GL_QUADS) 86 | x = 0 87 | 88 | for surface in surfaces: 89 | 90 | for vertex in surface: 91 | glColor3fv((colors[x])) 92 | glVertex3fv(vertices[vertex]) 93 | x += 1 94 | glEnd() 95 | 96 | def DrawGL(): 97 | 98 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) 99 | glLoadIdentity() 100 | gluPerspective(90, (display[0]/display[1]), 0.1, 50.0) 101 | glTranslatef(0.0,0.0, -5) 102 | 103 | glRotatef(round(myimu.Pitch,1), 0, 0, 1) 104 | glRotatef(round(myimu.Roll,1), -1, 0, 0) 105 | 106 | DrawText("Roll: {}° Pitch: {}°".format(round(myimu.Roll,1),round(myimu.Pitch,1))) 107 | DrawBoard() 108 | pygame.display.flip() 109 | 110 | def SerialConnection (): 111 | global serial_object 112 | serial_object = serial.Serial( myport.Name, baudrate= myport.Speed, timeout = myport.Timeout) 113 | 114 | def ReadData(): 115 | while True: 116 | 117 | serial_input = serial_object.readline() 118 | if(len(serial_input) == 9 and serial_input[0] == 0x24 ): 119 | X = [serial_input[2], serial_input[1]] 120 | Ax = int.from_bytes(X,byteorder = 'big',signed=True) 121 | 122 | Y = [serial_input[4], serial_input[3]] 123 | Ay = int.from_bytes(Y,byteorder = 'big',signed=True) 124 | 125 | myimu.Roll = Ax/16384.0*90 126 | myimu.Pitch = Ay/16384.0*90 127 | 128 | 129 | def main(): 130 | ConfWindw.mainloop() 131 | if ApplicationGL == True: 132 | InitPygame() 133 | InitGL() 134 | 135 | try: 136 | SerialConnection() 137 | myThread1 = threading.Thread(target = ReadData) 138 | myThread1.daemon = True 139 | myThread1.start() 140 | while True: 141 | event = pygame.event.poll() 142 | if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): 143 | pygame.quit() 144 | break 145 | 146 | DrawGL() 147 | pygame.time.wait(10) 148 | 149 | except: 150 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) 151 | DrawText("Sorry, something is wrong :c") 152 | pygame.display.flip() 153 | time.sleep(5) 154 | 155 | 156 | 157 | 158 | if __name__ == '__main__': main() --------------------------------------------------------------------------------