Skip to content
Snippets Groups Projects
Commit 15234b08 authored by ym13n22's avatar ym13n22
Browse files

second version combined

parent d5c1860b
No related branches found
No related tags found
No related merge requests found
import threading
import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
import serial
import serial.tools.list_ports
from time import sleep
import math
import time
class Window:
def __init__(self, root):
self.root = root
self.root.title("Integration")
self.ports = [port.device for port in serial.tools.list_ports.comports()]
self.arduino = None
# Set the initial size and position of the popup window
self.width = 1000
self.height = 600
screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight()
x = (screen_width // 2) - (self.width // 2)
y = (screen_height // 2) - (self.height // 2)
self.root.geometry(f"{self.width}x{self.height}+{x}+{y}")
# Configure the grid to be expandable
self.root.columnconfigure(0, weight=1)
self.root.columnconfigure(1, weight=1)
self.root.rowconfigure(0, weight=1)
self.root.rowconfigure(1, weight=1)
# Create a frame
self.frame1 = tk.Frame(self.root, borderwidth=1, relief="solid", width=self.width / 3, height=self.height / 2)
self.frame1.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")
self.frame2 = tk.Frame(self.root, borderwidth=1, relief="solid", width=self.width * 2 / 3, height=self.height / 2)
self.frame2.grid(row=0, column=1, padx=10, pady=10, sticky="nsew")
self.frame3 = tk.Frame(self.root, borderwidth=1, relief="solid", width=self.width / 3, height=self.height / 2)
self.frame3.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")
self.frame4 = tk.Frame(self.root, borderwidth=1, relief="solid", width=self.width * 2 / 3, height=self.height / 2)
self.frame4.grid(row=1, column=1, padx=10, pady=10, sticky="nsew")
self.frame4.grid_propagate(False)
label4 = tk.Label(self.frame4, text="Section 4")
label4.place(relx=0.5, rely=0.5, anchor='center')
self.imu_thread = threading.Thread(target=self.initial_IMU)
self.emg_thread = threading.Thread(target=self._initialise_EMG_graph)
self.emg_thread.start()
self.imu_thread.start()
self.emg_data_1 = [-1] * 41
self.emg_data_2 = [-1] * 41
#self.initial_IMU()
#self._initialise_EMG_graph()
self.display_IMU_thread=threading.Thread(target=self.update_display)
self.display_EMG_thread=threading.Thread(target=self.EMG_Display)
def initial_IMU(self):
# Serial Port Setup
if'COM7' in self.ports:#port maybe different on different laptop
self.label2 = tk.Label(self.frame2, text="Port: COM7 ")
self.label2.place(relx=0.35, rely=0.8, anchor='center')
self.label1 = tk.Label(self.frame2,
text="click the Connect button to see the animation",
wraplength=self.width / 2)
self.label1.place(relx=0.5, rely=0.9, anchor='center')
# Add a button to start data transmission
self.start_button = tk.Button(self.frame2, text="connect", command=self.start_data_transmission)
self.start_button.place(relx=0.5, rely=0.8, anchor='center')
self.start_button = tk.Button(self.frame2, text="Disconnect", command=self.disconnect)
self.start_button.place(relx=0.7, rely=0.8, anchor='center')
else:
print("IMU is not connected")
self.label2 = tk.Label(self.frame2, text="Port: None ")
self.label2.place(relx=0.35, rely=0.8, anchor='center')
self.label1 = tk.Label(self.frame2,
text="Please check the IUM connection",
wraplength=self.width / 2)
self.label1.place(relx=0.5, rely=0.9, anchor='center')
sleep(1)
# Conversions
self.transmitting = False
self.toRad = 2 * np.pi / 360
self.toDeg = 1 / self.toRad
# Initialize Parameters
self.count = 0
self.averageroll = 0
self.averageyaw = 0
self.averagepitch = 0
self.averageemg = 0
self.iterations = 10 # EMG measurements to get average
# Create a figure for the 3D plot
self.fig = Figure(figsize=((self.width / 300), (self.height / 200)))
self.ax = self.fig.add_subplot(111, projection='3d')
# Set Limits
self.ax.set_xlim(-2, 2)
self.ax.set_ylim(-2, 2)
self.ax.set_zlim(-2, 2)
# Set labels
self.ax.set_xlabel('X')
self.ax.set_ylabel('Y')
self.ax.set_zlabel('Z',labelpad=0)
# Draw Axes
self.ax.quiver(0, 0, 0, 2, 0, 0, color='red', label='X-Axis', arrow_length_ratio=0.1) # X Axis (Red)
self.ax.quiver(0, 0, 0, 0, -2, 0, color='green', label='Y-Axis', arrow_length_ratio=0.1) # Y Axis (Green)
self.ax.quiver(0, 0, 0, 0, 0, 4, color='blue', label='Z-Axis', arrow_length_ratio=0.1) # Z Axis (Blue)
# Draw the board as a rectangular prism (solid)
self.prism_vertices = np.array([
[-1.5, -1, 0], [1.5, -1, 0], [1.5, 1, 0], [-1.5, 1, 0], # bottom vertices
[-1.5, -1, 0.1], [1.5, -1, 0.1], [1.5, 1, 0.1], [-1.5, 1, 0.1]
# top vertices (height=0.1 for visual thickness)
])
self.prism_faces = [
[self.prism_vertices[j] for j in [0, 1, 2, 3]], # bottom face
[self.prism_vertices[j] for j in [4, 5, 6, 7]], # top face
[self.prism_vertices[j] for j in [0, 1, 5, 4]], # side face
[self.prism_vertices[j] for j in [1, 2, 6, 5]], # side face
[self.prism_vertices[j] for j in [2, 3, 7, 6]], # side face
[self.prism_vertices[j] for j in [3, 0, 4, 7]] # side face
]
self.prism_collection = Poly3DCollection(self.prism_faces, facecolors='gray', linewidths=1, edgecolors='black',
alpha=0.25)
self.ax.add_collection3d(self.prism_collection)
# Front Arrow (Purple)
self.front_arrow, = self.ax.plot([0, 2], [0, 0], [0, 0], color='purple', marker='o', markersize=10,
label='Front Arrow')
# Up Arrow (Magenta)
self.up_arrow, = self.ax.plot([0, 0], [0, -1], [0, 1], color='magenta', marker='o', markersize=10,
label='Up Arrow')
# Side Arrow (Orange)
self.side_arrow, = self.ax.plot([0, 1], [0, -1], [0, 1], color='orange', marker='o', markersize=10,
label='Side Arrow')
# Create a canvas to draw on
self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame1)
self.canvas.draw()
self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
# Create a label for average EMG
# self.emg_label = tk.Label(self.frame1, text="Average EMG: 0", font=("Arial", 14))
# self.emg_label.pack(pady=10)
self.roll_label = tk.Label(self.frame2, text="roll is : " )
self.roll_label.config(font=("Arial", 12))
self.roll_label.place(relx=0.2, rely=0.3, anchor='w')
self.pitch_label = tk.Label(self.frame2, text="pitch is : " )
self.pitch_label.config(font=("Arial", 12))
self.pitch_label.place(relx=0.2, rely=0.4, anchor='w')
self.yaw_label = tk.Label(self.frame2, text="yaw is : " )
self.yaw_label.config(font=("Arial", 12))
self.yaw_label.place(relx=0.2, rely=0.5, anchor='w')
def _initialise_EMG_graph(self):
if 'COM7' in self.ports:#port maybe different on different laptop
self.label2 = tk.Label(self.frame3, text="Port: COM7 ")
self.label2.place(relx=0.23, rely=0.8, anchor='center')
self.label1 = tk.Label(self.frame3,
text="click the Connect button to see the animation",
wraplength=self.width / 2)
self.label1.place(relx=0.5, rely=0.9, anchor='center')
# Add a button to start data transmission
self.start_button = tk.Button(self.frame3, text="connect", command=self.start_EMG_data_transmission)
self.start_button.place(relx=0.45, rely=0.8, anchor='center')
#self.start_button = tk.Button(self.frame3, text="Disconnect", command=self.disconnect)
self.start_button.place(relx=0.7, rely=0.8, anchor='center')
else:
print("EMG is not connected")
self.label2 = tk.Label(self.frame3, text="Port: None ")
self.label2.place(relx=0.35, rely=0.8, anchor='center')
self.label1 = tk.Label(self.frame3,
text="Please check the IUM connection",
wraplength=self.width / 2)
self.label1.place(relx=0.5, rely=0.9, anchor='center')
# Create a figure and axis
self.EMG_transmitting = False
fig = Figure(figsize=((self.width / 200), (self.height / 200))) # Adjusting figsize based on frame size
self.ax1 = fig.add_subplot(111)
self.ax1.set_title("Electromyography Envelope", fontsize=14, pad=0)
self.ax1.set_xlim(0, 5)
self.ax1.set_ylim(0, 5)
self.ax1.set_xlabel("Sample(20 samples per second)",fontsize=8,labelpad=-2)
self.ax1.set_ylabel("Magnitude",labelpad=0)
self.ax1.set_xticks(np.arange(0, 41, 8))
self.ax1.set_yticks(np.arange(0, 1001, 200))
for x_tick in self.ax1.get_xticks():
self.ax1.axvline(x_tick, color='gray', linestyle='--', linewidth=0.5)
for y_tick in self.ax1.get_yticks():
self.ax1.axhline(y_tick, color='gray', linestyle='--', linewidth=0.5)
# Plot two lines
self.line1, = self.ax1.plot([], [], color='red', label='Outer Wrist Muscle (Extensor Carpi Ulnaris)')
self.line2, = self.ax1.plot([], [], color='blue', label='Inner Wrist Muscle (Flexor Carpi Radialis)')
self.ax1.legend(fontsize=9, loc='upper right')
# Embed the plot in the tkinter frame
self.canvas1 = FigureCanvasTkAgg(fig, master=self.frame4)
self.canvas1.draw()
self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True)
self.EMG_Display()
self.outer_EMG_label = tk.Label(self.frame3, text=f"EMG for Extensor Carpi Ulnaris is :")
self.outer_EMG_label.config(font=("Arial", 12))
self.outer_EMG_label.place(relx=0.1, rely=0.2, anchor='w')
self.outer_EMG_Number = tk.Label(self.frame3, text="",fg="red")
self.outer_EMG_Number.config(font=("Arial", 12))
self.outer_EMG_Number.place(relx=0.2, rely=0.3, anchor='w')
self.inner_EMG_label = tk.Label(self.frame3, text=f"EMG for Flexor Carpi Radialis is :")
self.inner_EMG_label.config(font=("Arial", 12))
self.inner_EMG_label.place(relx=0.1, rely=0.4, anchor='w')
self.inner_EMG_Number = tk.Label(self.frame3, text="",fg="blue")
self.inner_EMG_Number.config(font=("Arial", 12))
self.inner_EMG_Number.place(relx=0.2, rely=0.5, anchor='w')
self.gesture_label = tk.Label(self.frame3, text=f"Gesture is :")
self.gesture_label.config(font=("Arial", 12))
self.gesture_label.place(relx=0.1, rely=0.6, anchor='w')
def start_data_transmission(self):
# Set the transmitting flag to True and start the update loop
if 'COM7' in self.ports:
if self.arduino==None:
self.arduino = serial.Serial('COM7', 115200)
self.transmitting = True
self.update_display()
def start_EMG_data_transmission(self):
# Set the transmitting flag to True and start the update loop
if 'COM7' in self.ports:
if self.arduino==None:
self.arduino = serial.Serial('COM7', 115200)
self.EMG_transmitting = True
self.EMG_Display()
def disconnect(self):
self.transmitting = False
self.EMG_transmitting=False
self.root.after_cancel(self.update_display_id)
if self.arduino is not None:
self.arduino.close()
self.arduino = None
def collect_emg_data(self):
column_limit = 6
emg_data = []
start_time = time.time()
while (self.arduino.inWaiting() > 0):
data = self.arduino.readline().decode().strip().split(',')
if len(data) == column_limit:
try:
emg1 = float(data[0])
emg2 = float(data[1])
print("Emg1: ",emg1)
print("Emg2: ",emg2)
emg_data.append((emg1, emg2))
except ValueError:
continue
if emg_data:
return np.mean(emg_data, axis=0)
else:
return np.array([0.0, 0.0]) # Default value if no data is collected
def update_display(self):
if self.transmitting:
try:
while ((self.arduino.inWaiting() > 0)and
(self.transmitting==True)):
dataPacket = self.arduino.readline()
dataPacket = dataPacket.decode()
cleandata = dataPacket.replace("\r\n", "")
row = cleandata.strip().split(',')
if len(row) == 6:
splitPacket = cleandata.split(',')
q0 = float(splitPacket[2]) # qw
q1 = float(splitPacket[3]) # qx
q2 = float(splitPacket[4]) # qy
q3 = float(splitPacket[5]) # qz
# Calculate Angles
roll = math.atan2(2 * (q0 * q1 + q2 * q3), 1 - 2 * (q1 * q1 + q2 * q2))
pitch = -math.asin(2 * (q0 * q2 - q3 * q1))
yaw = -math.atan2(2 * (q0 * q3 + q1 * q2), 1 - 2 * (q2 * q2 + q3 * q3))
self.roll_label.config( text="roll is : "+str(roll))
self.pitch_label.config(text="pitch is : "+str(pitch))
self.yaw_label.config(text="yaw is : "+str(yaw))
# Rotation matrices
Rz = np.array([
[np.cos(yaw), -np.sin(yaw), 0],
[np.sin(yaw), np.cos(yaw), 0],
[0, 0, 1]
])
Ry = np.array([
[np.cos(pitch), 0, np.sin(pitch)],
[0, 1, 0],
[-np.sin(pitch), 0, np.cos(pitch)]
])
Rx = np.array([
[1, 0, 0],
[0, np.cos(roll), -np.sin(roll)],
[0, np.sin(roll), np.cos(roll)]
])
R = Rz @ Ry @ Rx # Combined rotation matrix
# Apply the rotation
rotated_vertices = (R @ self.prism_vertices.T).T
prism_faces_rotated = [
[rotated_vertices[j] for j in [0, 1, 2, 3]], # bottom face
[rotated_vertices[j] for j in [4, 5, 6, 7]], # top face
[rotated_vertices[j] for j in [0, 1, 5, 4]], # side face
[rotated_vertices[j] for j in [1, 2, 6, 5]], # side face
[rotated_vertices[j] for j in [2, 3, 7, 6]], # side face
[rotated_vertices[j] for j in [3, 0, 4, 7]] # side face
]
# Update the collection
self.prism_collection.set_verts(prism_faces_rotated)
# Update Arrows
k = np.array([np.cos(yaw) * np.cos(pitch), np.sin(pitch), np.sin(yaw) * np.cos(pitch)]) # X vector
y = np.array([0, 1, 0]) # Y vector: pointing down
s = np.cross(k, y) # Side vector
v = np.cross(s, k) # Up vector
vrot = v * np.cos(roll) + np.cross(k, v) * np.sin(roll) # Rotated Up vector
self.front_arrow.set_data([0, k[0] * 2], [0, k[1] * 2])
self.front_arrow.set_3d_properties([0, k[2] * 2])
self.up_arrow.set_data([0, vrot[0] * 1], [0, vrot[1] * 1])
self.up_arrow.set_3d_properties([0, vrot[2] * 1])
self.side_arrow.set_data([0, s[0] * 1], [0, s[1] * 1])
self.side_arrow.set_3d_properties([0, s[2] * 1])
# Update canvas
self.canvas.draw()
self.averageroll += roll * self.toDeg
self.averageyaw += yaw * self.toDeg
self.averagepitch += pitch * self.toDeg
#self.averageemg += emg
if self.count == self.iterations:
self.averageroll = self.averageroll / self.iterations
self.averageyaw = self.averageyaw / self.iterations
self.averagepitch = self.averagepitch / self.iterations
#self.averageemg = self.averageemg / self.iterations
self.averageroll = round(self.averageroll)
self.averageyaw = round(self.averageyaw)
self.averagepitch = round(self.averagepitch)
# Print the averaged results
print("iterations:", self.iterations)
print("averageroll is", self.averageroll)
print("averageyaw is", self.averageyaw)
print("averagepitch is", self.averagepitch)
print("averageemg=", self.averageemg)
self.count = 0
self.averageyaw = 0
self.averageroll = 0
self.averagepitch = 0
self.averageemg = 0
else:
self.count += 1
# Update EMG Label
#self.emg_label.config(text=f"Average EMG: {self.averageemg:.2f}")
except Exception as e:
print(f"An error occurred: {e}")
# Call update_display() again after 50 milliseconds
self.update_display_id =self.root.after(50, self.update_display)
def EMG_Display(self):
data_collection_duration = 3
if self.EMG_transmitting:
try:
#while ((self.arduino_EMG.inWaiting() > 0) and
# (self.EMG_transmitting == True)):
# data = self.arduino_EMG.readline()
emg_data = self.collect_emg_data()
if emg_data is not None:
print(f"EMG 1: {emg_data[0]} , EMG 2: {emg_data[1]}")
# Append the new data to the lists
if self.EMG_transmitting:
self.outer_EMG_Number.config(text=f"{emg_data[0]}")
self.inner_EMG_Number.config(text=f"{emg_data[1]}")
self.emg_data_1.append(emg_data[0])
self.emg_data_1.pop(0)
self.emg_data_2.append(emg_data[1])
self.emg_data_2.pop(0)
# Update the line data to shift the line from right to left
self.line1.set_data(range(len(self.emg_data_1)), self.emg_data_1)
self.line2.set_data(range(len(self.emg_data_2)), self.emg_data_2)
# Redraw the canvas
self.canvas1.draw() # Redraw the canvas
except Exception as e:
print(f"An error occurred: {e}")
# Call update_display() again after 50 milliseconds
self.EMG_display_id=self.root.after(50, self.EMG_Display)
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
if __name__ == "__main__":
root = tk.Tk()
app = Window(root)
root.mainloop()
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment