Skip to content
Snippets Groups Projects
integrate.py 10.80 KiB
import tkinter as tk
from tkinter import ttk
from matplotlib.pyplot import Figure
from vpython import *
from time import time

import serial
from math import atan2, asin, cos, sin
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np

from vpython import canvas

from communications import Communications


class Interface:
    _serial_data = Communications()
    def __init__(self):
        print("start")
        self.treeview = None
        self.x_data = []
        self.y1_data = []
        self.y2_data = []


    def create_popup(self):
        global frame1, frame2, frame3, frame4,width,height,popup
        popup = tk.Tk()
        popup.title("Project")

        # Set the initial size and position of the popup window
        width = 1000
        height = 600
        screen_width = popup.winfo_screenwidth()
        screen_height = popup.winfo_screenheight()
        x = (screen_width // 2) - (width // 2)
        y = (screen_height // 2) - (height // 2)
        popup.geometry(f"{width}x{height}+{x}+{y}")

        # Configure the grid to be expandable
        popup.columnconfigure(0, weight=1)
        popup.columnconfigure(1, weight=1)
        popup.rowconfigure(0, weight=1)
        popup.rowconfigure(1, weight=1)

        # First row, two sections
        frame1 = ttk.Frame(popup, borderwidth=1, relief="solid",width=width/3,height=height/2)
        frame1.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")


        frame2 = ttk.Frame(popup, borderwidth=1, relief="solid",width=width*2/3,height=height/2)
        frame2.grid(row=0, column=1, padx=10, pady=10, sticky="nsew")
        label1 = ttk.Label(frame2, text="If IMU is connected to the laptop please click the Connect button",
                           wraplength=width / 2)
        label1.place(relx=0.5, rely=0.9, anchor='center')
        self.label2 = ttk.Label(frame2, text="Port: None ")
        self.label2.place(relx=0.35, rely=0.8, anchor='center')
        frame2.update_idletasks()

        # Second row, two sections
        frame3 = ttk.Frame(popup, borderwidth=1, relief="solid",width=width/3,height=height/2)
        frame3.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")
        label3 = ttk.Label(frame3, text="If EMG is connected to the laptop please click the Connect button",wraplength=width/4)
        label3.place(relx=0.5, rely=0.9, anchor='center')
        label4 = ttk.Label(frame3, text="Port: None ")
        label4.place(relx=0.35, rely=0.8, anchor='e')

        frame4 = ttk.Frame(popup, borderwidth=1, relief="solid",width=width*2/3,height=height/2)
        frame4.grid(row=1, column=1, padx=10, pady=10, sticky="nsew")
        frame4.grid_propagate(False)
        label4 = ttk.Label(frame4, text="Section 4")
        label4.place(relx=0.5, rely=0.5, anchor='center')
        self._initialise_real_time_classification_graph()

        # Add a button to frame1 to trigger adding widgets
        add_button1 = ttk.Button(frame2, text="Connect", command=self.IMU_Connect)
        add_button1.place(relx=0.5, rely=0.8, anchor='center')
        add_button3 = ttk.Button(frame2, text="Disconnect", command=self.IMU_disconnect)
        add_button3.place(relx=0.8, rely=0.8, anchor='center')

        add_button2 = ttk.Button(frame3, text="Connect", command=self.EMG_Connect)
        add_button2.place(relx=0.5, rely=0.8, anchor='center')
        add_button4 = ttk.Button(frame3, text="Disconnect", command=self.EMG_disconnect())
        add_button4.place(relx=0.8, rely=0.8, anchor='center')

        popup.mainloop()

    def IMU_Connect(self):
        try:
            self._iMU_isConnected=True
            self.arduino = serial.Serial('COM6', 115200, timeout=1)
            column_limit = 9
            self.label2 = ttk.Label(frame2, text="Port: COM6 ")
            self.label2.place(relx=0.35, rely=0.8, anchor='center')
            print("Connected to IMU")
        except serial.SerialException:
            print("IMU is not connected")
            return
        sleep(1)

        scene.range = 5  # 修改场景的范围为10
        scene.forward = vector(-1, -1, -1)  # 保持视角不变
        scene.width = 300  # 修改场景的宽度为800
        scene.height = 300  # 修改场景的高度为800
        scene.center = vector(0, 0, 0)  # 将场景中心位置调整为 (0, 0, 0)

        scene.width = 600
        scene.height = 600

        frontArrow = arrow(axis=vector(1, 0, 0), length=4, shaftwidth=.1, color=color.purple)
        upArrow = arrow(axis=vector(0, 1, 0), length=1, shaftwidth=.1, color=color.magenta)
        sideArrow = arrow(axis=vector(0, 0, 1), length=2, shaftwidth=.1, color=color.orange)

        bBoard = box(length=3, width=4, height=.2, color=color.white, opacity=0.8, pos=vector(0, 0, 0))
        bno = box(color=color.blue, length=1, width=.75, height=0.1, pos=vector(-0.5, 0.1 + 0.05, 0))
        nano = box(length=1.75, width=.6, height=.1, pos=vector(-2, .1 + .05, 0), color=color.green)
        myObj = compound([bBoard, bno, nano])
        last_print_time = time()

        try:
            while (self._iMU_isConnected==True):
                while self.arduino.inWaiting() == 0:
                    pass

                try:
                    data_packet = self.arduino.readline().decode('utf-8').strip()
                    data = data_packet.split(',')

                    if len(data) == column_limit:
                        emg = float(data[0])  # emg sensor data
                        q0 = float(data[1])  # qw
                        q1 = float(data[2])  # qx
                        q2 = float(data[3])  # qy
                        q3 = float(data[4])  # qz

                        roll = atan2(2 * (q0 * q1 + q2 * q3), 1 - 2 * (q1 * q1 + q2 * q2))
                        pitch = -asin(2 * (q0 * q2 - q3 * q1))
                        yaw = -atan2(2 * (q0 * q3 + q1 * q2), 1 - 2 * (q2 * q2 + q3 * q3))
                        current_time = time()

                        if current_time - last_print_time >= 0.01:
                            print(f"EMG: {emg}, Roll: {roll}, Pitch: {pitch}, Yaw: {yaw}")
                        k = vector(cos(yaw) * cos(pitch), sin(pitch), sin(yaw) * cos(pitch))  # this is the x vector
                        y = vector(0, 1, 0)  # y vector: pointing down
                        s = cross(k, y)  # this is pointing to the side
                        v = cross(s, k)  # the up vector
                        vrot = v * cos(roll) + cross(k, v) * sin(roll)
                        #    print(k,y,s,v)

                        frontArrow.axis = k
                        sideArrow.axis = cross(k, vrot)
                        upArrow.axis = vrot
                        myObj.axis = k
                        myObj.up = vrot
                        sideArrow.length = 2
                        frontArrow.length = 4
                        upArrow.length = 1
                        last_print_time = current_time

                    else:
                        print("Data packet does not match column limit")

                except ValueError:
                    print("Error in data conversion")
                except UnicodeDecodeError:
                    print("Error in decoding data")

        except KeyboardInterrupt:
            self.arduino.close()
            print("Disconnected from IMU")

    def IMU_disconnect(self):
        self._iMU_isConnected = False
        if self.arduino and self.arduino.is_open:
                self.arduino.close()
                print("IMU disconnected")
        if scene:
                scene.delete()


    def EMG_Connect(self):
        try:
            self._EMG_isConnected = True
            self.label2 = ttk.Label(frame3, text="Port: COM5 ")
            self.label2.place(relx=0.35, rely=0.8, anchor='e')
            label_EMG_title = ttk.Label(frame3, text="Auto Calibralian",font=("Helvetica", 16))
            label_EMG_title.place(relx=0.5, rely=0.1, anchor='center')
            label_EMG_Gesture = ttk.Label(frame3, text="Gesture :", font=("Helvetica", 13))
            label_EMG_Gesture.place(relx=0.2, rely=0.5, anchor='center')
            self._serial_data.configure(port='COM5', baudrate=9600, timeout=1)
            self._serial_data.connect()
            self.start_data_collection()
            print("Connected to EMG")
        except serial.SerialException:
            print("EMG is not connected")
            return

    def EMG_disconnect(self):
        self._EMG_isConnected = False
        if self._serial_data.connected==True:
            self._serial_data.disconnect()
            print("EMG disconnected")

    def start_data_collection(self):
        print("Starting EMG data collection...")
        try:
            while (self._EMG_isConnected==True):
                emg_data=self._serial_data.read()
                if emg_data is not None:
                    print(f"EMG Value 1: {emg_data[0]}, EMG Value 2: {emg_data[1]}")
                else:
                    print("Failed to read EMG data")
        except KeyboardInterrupt:
            print("Stopping EMG data collection.")




    def update_table(self, emg_data):
        if self.treeview:
            self.treeview.delete(*self.treeview.get_children())  # 清除现有数据
            self.treeview.insert("", "end", values=("Gesture 1", f"{emg_data[0]:.2f}"))
            self.treeview.insert("", "end", values=("Gesture 2", f"{emg_data[1]:.2f}"))


    def _initialise_real_time_classification_graph(self):
            # Create a figure and axis
            fig = Figure(figsize=((width / 200), (height / 200)))  # Adjusting figsize based on frame size
            self.ax = fig.add_subplot(111)

            self.ax.set_title("Electromyography Envelope", fontsize=14, pad=0)


            self.ax.set_xlim(0, 5)
            self.ax.set_ylim(0, 5)

            self.ax.set_xlabel("Sample(20 samples per second)",fontsize=8,labelpad=-2)
            self.ax.set_ylabel("Magnitude",labelpad=0)

            self.ax.set_xticks(np.arange(0, 41, 8))
            self.ax.set_yticks(np.arange(0, 1001, 200))

            for x_tick in self.ax.get_xticks():
                self.ax.axvline(x_tick, color='gray', linestyle='--', linewidth=0.5)
            for y_tick in self.ax.get_yticks():
                self.ax.axhline(y_tick, color='gray', linestyle='--', linewidth=0.5)




            # Plot two lines
            self.line1, = self.ax.plot([], [], color='red', label='Outer Wrist Muscle (Extensor Carpi Ulnaris)')
            self.line2, = self.ax.plot([], [], color='blue', label='Inner Wrist Muscle (Flexor Carpi Radialis)')
            self.ax.legend(fontsize=9, loc='upper right')

            


            # Embed the plot in the tkinter frame
            self.canvas = FigureCanvasTkAgg(fig, master=frame4)
            self.canvas.draw()
            self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)





if __name__ == "__main__":
    windows = Interface()
    windows.create_popup()