diff --git a/IMU/code/sourceCode/try.py b/IMU/code/sourceCode/try.py new file mode 100644 index 0000000000000000000000000000000000000000..d3a74a2fc026f250fe53744b4cffd7d60a505471 --- /dev/null +++ b/IMU/code/sourceCode/try.py @@ -0,0 +1,77 @@ +import sys +from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QSurfaceFormat +from vpython import canvas, sphere, box, color, vector +import matplotlib.pyplot as plt +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas +import numpy as np + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("VPython and Matplotlib in PyQt5") + self.setGeometry(100, 100, 1200, 600) # 设置窗口的大小 + + # 创建一个 QWidget 作为中心窗口 + central_widget = QWidget() + self.setCentralWidget(central_widget) + + # 创建一个布局 + layout = QVBoxLayout() + central_widget.setLayout(layout) + + # 创建 VPython 画布 + # 使用 QSurfaceFormat 来设置 VPython 画布的 OpenGL 环境 + format = QSurfaceFormat() + format.setDepthBufferSize(24) + format.setMajorVersion(3) + format.setMinorVersion(3) + format.setProfile(QSurfaceFormat.CoreProfile) + QSurfaceFormat.setDefaultFormat(format) + + # 创建 VPython 画布,并将其嵌入到一个 QWidget 中 + self.vpython_canvas = canvas(width=600, height=300, background=color.white, insert=True) + sphere(radius=1, color=color.red) + box(size=vector(2,2,2), color=color.green) + + # 创建一个 QWidget 用来包装 VPython 画布 + vpython_widget = QWidget.createWindowContainer(self.vpython_canvas) + layout.addWidget(vpython_widget) + + # 创建 Matplotlib 图形 + self.fig, self.ax = plt.subplots(figsize=(6, 3)) + self.ax.set_title('Matplotlib 2D Contour Plot') + + # 创建示例数据 + x = np.linspace(-5, 5, 100) + y = np.linspace(-5, 5, 100) + X, Y = np.meshgrid(x, y) + Z = np.sin(np.sqrt(X**2 + Y**2)) + + # 绘制 2D 等高线图 + self.ax.contourf(X, Y, Z, cmap='viridis') + self.ax.set_xlabel('X-axis') + self.ax.set_ylabel('Y-axis') + + # 将 Matplotlib 图形嵌入到 PyQt5 中 + self.canvas = FigureCanvas(self.fig) + layout.addWidget(self.canvas) + + # 设置一个定时器来刷新 VPython 场景 + self.timer_counter = 0 # 计数器 + self.timer = self.startTimer(50) # 每 50ms 刷新一次 + + def timerEvent(self, event): + # 更新 VPython 画布中的内容 + # 你可以在这里添加动画效果或更新对象的位置 + self.vpython_canvas.scene.camera.pos = vector(5 * np.sin(self.timer_counter), 5, 5) + self.vpython_canvas.scene.camera.lookat = vector(0, 0, 0) + self.timer_counter += 0.1 # 更新计数器 + +# 运行 PyQt5 应用程序 +if __name__ == '__main__': + app = QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec_()) diff --git a/integration/communications.py b/integration/communications.py new file mode 100644 index 0000000000000000000000000000000000000000..0cddc7a61647f65afe902a54cefb88acaad8cdd6 --- /dev/null +++ b/integration/communications.py @@ -0,0 +1,65 @@ +import serial + + +class Communications: + # Defaults + _port = None + _baudrate = 9600 + _timeout = 1 + + _data = None + _serial_process = None + _send = None + _receive = None + connected = False + + adc_data = [0, 0] + adc_values = [0, 0] + + def configure(self, port, baudrate, timeout): # Set the serial settings + self._port = port + self._baudrate = baudrate + self._timeout = timeout + + def connect(self): + connected = True # Assume the connection was successful + try: + self._data = serial.Serial(port=self._port, baudrate=self._baudrate, timeout=self._timeout) + except IOError: + connected = False # If connection fails, return False + return connected + + def disconnect(self): + self.connected = False + self._data.close() + + def flush(self): + self._data.flushInput() + self._data.read() # Discard the first result, usually brings an incomplete piece of data. + + def read(self): + self.adc_data = self._decode(self._data.readline()) + return self.adc_data + + def _decode(self, serial_data): + serial_string = serial_data.decode(errors="ignore") + adc_string_1 = "" + adc_string_2 = "" + self.adc_values = [0, 0] + if '\n' in serial_string: + # remove new line character + serial_string = serial_string.replace("\n", "") + if serial_string != '': + # Convert number to binary, placing 0s in empty spots + serial_string = format(int(serial_string, 10), "024b") + + # Separate the input number from the data + for i0 in range(0, 12): + adc_string_1 += serial_string[i0] + for i0 in range(12, 24): + adc_string_2 += serial_string[i0] + + self.adc_values[0] = int(adc_string_1, base=2) + self.adc_values[1] = int(adc_string_2, base=2) + + return self.adc_values diff --git a/integration/integrate.py b/integration/integrate.py index 046179c4a633766adb555b5de93fe3eece841318..03f4c133717b49e9a6af05596a792ee56d7a881d 100644 --- a/integration/integrate.py +++ b/integration/integrate.py @@ -1,15 +1,22 @@ import tkinter as tk from tkinter import ttk from matplotlib.pyplot import Figure +from vpython import * +from time import sleep, 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 -import time + +from vpython import canvas + +from communications import Communications + class Interface: + _serial_data = Communications() def __init__(self): print("start") self.treeview = None @@ -41,15 +48,17 @@ class Interface: # 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") - label1 = ttk.Label(frame1, text="If IMU is connected to the laptop please click the Connect button",wraplength=width/4) - label1.place(relx=0.5, rely=0.9, anchor='center') - label2 = ttk.Label(frame1,text="Port: None ") - label2.place(relx=0.35, rely=0.8, anchor='e') + 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") - label2 = ttk.Label(frame2, text="Section 2") - label2.place(relx=0.5, rely=0.5, anchor='center') + 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') + label2 = ttk.Label(frame2, text="Port: None ") + label2.place(relx=0.35, rely=0.8, anchor='center') + frame2.update_idletasks() + #self._initialise_IUM() # Second row, two sections frame3 = ttk.Frame(popup, borderwidth=1, relief="solid",width=width/3,height=height/2) @@ -64,34 +73,61 @@ class Interface: 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(frame1, text="Connect", command=self.IMU_Connect) + 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: - arduino = serial.Serial('COM4', 115200, timeout=1) + self.arduino = serial.Serial('COM6', 115200, timeout=1) column_limit = 9 - label2 = ttk.Label(frame1, text="Port: COM4 ") - label2.place(relx=0.2, rely=0.8, anchor='center') + label2 = ttk.Label(frame2, text="Port: COM6 ") + 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) + + # Conversions + toRad = 2 * np.pi / 360 + toDeg = 1 / toRad + + scene.range = 5 + + scene.forward = vector(-1, -1, -1) + + 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 True: - while arduino.inWaiting() == 0: + while self.arduino.inWaiting() == 0: pass try: - data_packet = arduino.readline().decode('utf-8').strip() + data_packet = self.arduino.readline().decode('utf-8').strip() data = data_packet.split(',') if len(data) == column_limit: @@ -104,8 +140,27 @@ class Interface: 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 - print(f"EMG: {emg}, Roll: {roll}, Pitch: {pitch}, Yaw: {yaw}") else: print("Data packet does not match column limit") @@ -115,74 +170,54 @@ class Interface: print("Error in decoding data") except KeyboardInterrupt: - arduino.close() + self.arduino.close() print("Disconnected from IMU") + def IMU_disconnect(self): + if self.arduino and self.arduino.is_open: + self.arduino.close() + print("IMU disconnected") + if scene: + scene.delete() def EMG_Connect(self): try: - self.arduino = serial.Serial('COM5', 115200, timeout=1) + # self.arduino = serial.Serial('COM5', 115200, timeout=1) label2 = ttk.Label(frame3, text="Port: COM5 ") 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._initialise_real_time_classification_graph() - self.read_and_display_emg_data() + 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 read_and_display_emg_data(self): - while True: - emg_values = self.read_emg_data() - if emg_values is not None: - print(f"EMG Value 1: {emg_values[0]}, EMG Value 2: {emg_values[1]}") - if len(emg_values) >= 2: - # 更新表格 - self.update_table(emg_values) + def EMG_disconnect(self): + if self._serial_data.configure(port='COM5', baudrate=9600, timeout=1): + self._serial_data.disconnect() + print("EMG disconnected") - # 更新数据存储 - self.x_data.append(len(self.x_data) + 1) - self.y1_data.append(emg_values[0]) - self.y2_data.append(emg_values[1]) - - # 保持数据长度 - if len(self.x_data) > 100: - self.x_data = self.x_data[-100:] - self.y1_data = self.y1_data[-100:] - self.y2_data = self.y2_data[-100:] - - # 更新图表 - self.update_graph() - # 在这里添加数据分析代码 - # 例如,计算平均值,滤波等 - else: - print("Failed to read EMG data") - time.sleep(0.1) # 控制读取频率 - - - - # 定时读取数据 (1000ms = 1秒) - #popup.after(1000, self.read_and_display_emg_data, self.arduino) - - def update_graph(self): - # 更新线的数据 - self.line1.set_data(self.x_data, self.y1_data) - self.line2.set_data(self.x_data, self.y2_data) + def start_data_collection(self): + print("Starting EMG data collection...") + try: + while 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") + time.sleep(0.1) # 控制读取频率 + except KeyboardInterrupt: + print("Stopping EMG data collection.") - # 设置 X 轴范围 - self.ax.set_xlim(min(self.x_data), max(self.x_data)) - # 自适应 Y 轴范围 - self.ax.relim() - self.ax.autoscale_view() - # 更新图表 - self.canvas.draw() def update_table(self, emg_data): if self.treeview: @@ -191,9 +226,6 @@ class Interface: 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 diff --git a/integration/readEMG.py b/integration/readEMG.py index d1a0dd13162f0ab38ebc63ac41f439a29ef07bf0..eabd7473c1379cbfcded961632989ccc632665ea 100644 --- a/integration/readEMG.py +++ b/integration/readEMG.py @@ -70,8 +70,6 @@ class EMGSensor: emg_values = self.read_emg_data() if emg_values is not None: print(f"EMG Value 1: {emg_values[0]}, EMG Value 2: {emg_values[1]}") - # 在这里添加数据分析代码 - # 例如,计算平均值,滤波等 else: print("Failed to read EMG data") time.sleep(0.1) # 控制读取频率