class PygameController: comms = None def __init__(self, serial_name, baud_rate): self.comms = Communication(serial_name, baud_rate) def run(self): # 1. make sure data sending is stopped by ending streaming self.comms.send_message("stop") self.comms.clear() # 2. start streaming orientation data input("Ready to start? Hit enter to begin.\n") self.comms.send_message("start") # 3. Forever collect orientation and send to PyGame until user exits print("Use <CTRL+C> to exit the program.\n") while True: message = self.comms.receive_message() if (message != None): command = None message = int(message) # if message == 0: # command = "FLAT" # if message == 1: # command = "UP" if message == 2: command = "FIRE" elif message == 3: command = "LEFT" elif message == 4: command = "RIGHT" if command is not None: mySocket.send(command.encode("UTF-8"))
def collect_samples(): num_samples = 500 # 10 seconds of data @ 50Hz times = CircularList([], num_samples) ax = CircularList([], num_samples) ay = CircularList([], num_samples) az = CircularList([], num_samples) comms = Communication("/dev/cu.ag-ESP32SPP", 115200) try: comms.clear() # just in case any junk is in the pipes # wait for user to start walking before starting to collect data input("Ready to collect data? Press [ENTER] to begin.\n") sleep(3) comms.send_message("wearable") # begin sending data sample = 0 while (sample < num_samples): message = comms.receive_message() if (message != None): try: (m1, m2, m3, m4) = message.split(',') except ValueError: # if corrupted data, skip the sample continue # add the new values to the circular lists times.add(int(m1)) ax.add(int(m2)) ay.add(int(m3)) az.add(int(m4)) sample += 1 print("Collected {:d} samples".format(sample)) # a single ndarray for all samples for easy file I/O data = np.column_stack([times, ax, ay, az]) except (Exception, KeyboardInterrupt) as e: print(e) # exiting the program due to exception finally: comms.send_message("sleep") # stop sending data comms.close() return data
def collect_samples(): num_samples = 500 # 10 seconds of data @ 50Hz times = CircularList([], num_samples) ppg = CircularList([], num_samples) comms = Communication("/dev/cu.usbserial-1420", 115200) try: comms.clear() # just in case any junk is in the pipes # wait for user and then count down input("Ready to collect data? Press [ENTER] to begin.\n") print("Start measuring in...") for k in range(3,0,-1): print(k) sleep(1) print("Begin!") comms.send_message("wearable") # begin sending data sample = 0 while(sample < num_samples): message = comms.receive_message() if(message != None): try: (m1, _, _, _, m2) = message.split(',') except ValueError: # if corrupted data, skip the sample continue # add the new values to the circular lists times.add(int(m1)) ppg.add(int(m2)) sample += 1 print("Collected {:d} samples".format(sample)) # a single ndarray for all samples for easy file I/O data = np.column_stack([times, ppg]) except(Exception, KeyboardInterrupt) as e: print(e) # exiting the program due to exception finally: comms.send_message("sleep") # stop sending data comms.close() return data
fs = 50 # sampling rate num_samples = 500 # 10 seconds of data @ 50Hz process_time = 1 # compute the heart beat every second MAX_HEART_RATE = 200 hr = HRMonitor(num_samples, 50) comms = Communication("/dev/cu.ag-ESP32SPP", 115200) comms.clear() # just in case any junk is in the pipes comms.send_message("wearable") # begin sending data print("Ready!") hr_plot = CircularList([], num_samples) t = CircularList([], num_samples) # Live data try: previous_time = time() while(True): message = comms.receive_message() if (message != None): try: (m1, _, _, _, m2) = message.split(',') except ValueError: # if corrupted data, skip the sample continue # Collect data in the pedometer hr.add(int(m1)/1e3, int(m2)) t.add(int(m1)/1e3) t_plot = np.array(t) # if enough time has elapsed, process the data and plot it current_time = time() if (current_time - previous_time > process_time): previous_time = current_time try: heart_rate, peaks, filtered = hr.process()
class Processing(): __num_samples = 250 # 2 seconds of data @ 50Hz __refresh_time = 0.5 # update the plot every 0.1s (10 FPS) # Thresholds for idle detection __ax_idle_threshold = 1935 __ay_idle_threshold = 1918 __az_idle_threshold = 2430 """ Initializes the processing object and sets the field variables @:param transformation_method: the type of transformation that will be plotted and computed @:param port_name: The Serial port name used for Serial communication """ def __init__(self, transformation_method, port_name): self.comms = Communication(port_name, 115200) __transform_dict = { "average acceleration": self.__average_value, "sample difference": self.__sample_difference, "L2 norm": self.__l2_norm_calculation, "L1 norm": self.__l1_norm_calculation, "Maximum acceleration:": self.__max_acceleration } # This will keep track of the transformation method to be called self.__transformation_method = __transform_dict[transformation_method] self.transform_x = CircularList([], self.__num_samples) self.transform_y = CircularList([], self.__num_samples) self.transform_z = CircularList([], self.__num_samples) # These will contain the acceleration values read from the accelerometer self.times = CircularList([], self.__num_samples) self.ax = CircularList([], self.__num_samples) self.ay = CircularList([], self.__num_samples) self.az = CircularList([], self.__num_samples) # Set up the plotting fig = plt.figure() self.ax1 = fig.add_subplot(311) self.ax2 = fig.add_subplot(312) self.ax3 = fig.add_subplot(313) self.graph_type = transformation_method # times to keep track of when self.__last_idlecheck_time = 0 self.__idle_state = False self.__last_active_time = 0 """ Updates the plot, displaying the acceeleration values as well as the transformation values """ def __plot(self): # Clears the plots and resets them self.ax1.cla() self.ax2.cla() self.ax3.cla() # Sets the title of the plot self.ax1.set_title("X acceleration (Red) and " + self.graph_type + " (Blue)") self.ax2.set_title("Y acceleration (Red) and " + self.graph_type + " (Blue)") self.ax3.set_title("X acceleration (Red) and " + self.graph_type + " (Blue)") # Plots the acceleration values along with the transformation self.ax1.plot(self.transform_x, 'b', self.ax, 'r') self.ax2.plot(self.transform_y, 'b', self.ay, 'r') self.ax3.plot(self.transform_z, 'b', self.az, 'r') plt.show(block=False) plt.pause(0.0001) """ Determines whether the device is inactive or not @:param average_x: The average x-acceleration @:param average_y: The average y-acceleration @:param average_z: The average z-acceleration @:return: a boolean representing whether the device is inactive or not """ def __is_inactive(self, average_x, average_y, average_z): return average_x <= self.__ax_idle_threshold and average_y <= self.__ay_idle_threshold and average_z <= self.__az_idle_threshold """ Computes the average value for an axis @:param acceleration_list: The acceleration over 5 seconds in either x,y,z direction @:return: A scalar which is the average from the acceleration list """ def __average_value(self, acceleration_list): return np.average(np.array(acceleration_list)) """ Computes the difference between each adjacent indexes @:param acceleration_list: The acceleration over 5 seconds in either x,y,z direction @:return: a numpy array containing the differences between each point """ def __sample_difference(self, acceleration_list): np.diff(np.array(acceleration_list)) return np.diff(np.array(acceleration_list)) """ Computes the euclidean distance for the acceleration list @:param x_acceleration: one sample of the x-acceleration @:param y_acceleration: one sample of the y-acceleration @:param z_acceleration: one sample of the z-acceleration @:return: A scalar which is the square root of the sum of each number in the """ def __l2_norm_calculation(self, x_acceleration, y_acceleration, z_acceleration): return np.linalg.norm( np.array([x_acceleration, y_acceleration, z_acceleration])) """ Computes the L1 norm for the acceleration lsit @:param x_acceleration: one sample of the x-acceleration @:param y_acceleration: one sample of the y-acceleration @:param z_acceleration: one sample of the z-acceleration @:return: The sum of the absolute value of each acceleration value """ def __l1_norm_calculation(self, x_acceleration, y_acceleration, z_acceleration): return np.linalg.norm(np.array( [x_acceleration, y_acceleration, z_acceleration]), ord=1) """ Finds the max acceleration in one of the accelerations @:param: acceleration_list: The acceleration over 5 seconds in either x,y,z direction @:return: The maximum value in the acceleration list """ def __max_acceleration(self, acceleration_list): return int(np.max(np.array(acceleration_list))) """ Records the acceleration and times from the accelerometer @:param time: the time from the Serial monitor @:param x_acceleration: the x-acceleration @:param y_acceleration: the y-acceleration @:param z_acceleration: the z-acceleration """ def __record_acceleration(self, time, x_acceleration, y_acceleration, z_acceleration): # add the new values to the circular lists self.times.add(int(time)) self.ax.add(int(x_acceleration)) self.ay.add(int(y_acceleration)) self.az.add(int(z_acceleration)) """ Records the transformation value based on what transformation type the instance uses @:param x_acceleration: one sample of the x-acceleration @:param y_acceleration: one sample of the y-acceleration @:param z_acceleration: one sample of the z-acceleration """ def __record_transformation(self, x_acceleration, y_acceleration, z_acceleration): # These if statements are used because some of these methods have different parameters and return types if self.__transformation_method == self.__l1_norm_calculation or self.__transformation_method == self.__l2_norm_calculation: norm_number = self.__transformation_method() self.transform_x.add(norm_number) self.transform_y.add(norm_number) self.transform_z.add(norm_number) # sets each transformation method to the sample difference array elif self.__transformation_method == self.__sample_difference: self.transform_x = self.__transformation_method(self.ax) self.transform_y = self.__transformation_method(self.ay) self.transform_z = self.__transformation_method(self.az) # This will either call average_value or maximum_acceleration else: self.transform_x.add(self.__transformation_method(self.ax)) self.transform_y.add(self.__transformation_method(self.ay)) self.transform_z.add(self.__transformation_method(self.az)) """ Checks if the device has been idle for 5 seconds or if it's been active for 1 second. This will either cause the motor to buzz or another message displaying that the person has been active. @:param current_time: The current time the program is at """ def __check_idle(self, current_time): # If it's been 5 seconds since the last time the person has been inactive if current_time - self.__last_idlecheck_time >= 5: # get the average acceleration over 5 seconds average_x = self.__average_value(self.ax) average_y = self.__average_value(self.ay) average_z = self.__average_value(self.az) print(average_x, ",", average_y, ",", average_z) self.__last_idlecheck_time = current_time # if the device has been idle for 5 seconds, buzz the motor if self.__is_inactive(average_x, average_y, average_z): self.__idle_state = True self.comms.send_message("Buzz motor") else: self.__idle_state = False # If the person has been inactive but has become active for 1 second if self.__idle_state and current_time - self.__last_active_time >= 1: self.__last_active_time = current_time # get the average values for the last 1 second average_x = self.__average_value(self.ax[200:]) average_y = self.__average_value(self.ay[200:]) average_z = self.__average_value(self.az[200:]) if not self.__is_inactive(average_x, average_y, average_z): print("Active accelerations: ", average_x, average_y, average_z) self.__last_idlecheck_time = current_time # this ensures that the person must be inactive for 5 seconds after their activity self.comms.send_message("Keep it up!") """ Runs all the processing for the Serial communication, including plotting the acceleration values and checking whether the device has been inactive or not. """ def run(self): self.comms.clear() # just in case any junk is in the pipes self.comms.send_message("wearable") # begin sending data try: previous_time = 0 while (True): message = self.comms.receive_message() if (message != None): try: (m1, m2, m3, m4) = message.split(',') except ValueError: # if corrupted data, skip the sample continue # Record the acceleration and transformation self.__record_acceleration(m1, m2, m3, m4) self.__record_transformation(m2, m3, m4) current_time = time() if current_time - previous_time > self.__refresh_time: previous_time = current_time self.__plot() self.__check_idle(current_time) except (Exception, KeyboardInterrupt) as e: print(e) # Exiting the program due to exception finally: print("Closing Connection") self.comms.send_message("sleep") # stop sending data self.comms.close() sleep(1)
class PygameController: comms = None filename = "./scores/topscores.csv" def __init__(self, serial_name, baud_rate): self.comms = Communication(serial_name, baud_rate) # Save data to file def save_data(self, filename, data): np.savetxt(filename, data, delimiter=",") # Load data from file def load_data(self, filename): return np.genfromtxt(filename, delimiter=",") """ Receives a message from the server @:return: the message sent from the server """ def receive_from_server(self): game_status = None # Receive game status try: game_status, _ = mySocket.recvfrom(1024) game_status = game_status.decode('utf-8') except: pass return game_status """ Orders the top three scores with the score from the last round @:param top_scores: the last top three scores @:param score: the score from the last round @:return: the new top three scores """ def order_top_scores(self, top_scores, score): top_scores.append( score) # add the score from last round to the top scores top_scores.sort( reverse=True) # sort all the scores from greatest to least return top_scores[:len( top_scores ) - 1] # return the new top scores with the lowest score removed """ Updates the top three scores of the user after their game is zero @:param score: The score the user got in their last game @:return: message send to Arduino which contains the top three scores """ def update_top_scores(self, score): # Load the top three scores from the file and sort them # from greatest to least top_scores = list(self.load_data(self.filename)) top_scores.sort(reverse=True) # Update the top three scores and save them to the file top_scores = self.order_top_scores(top_scores, score) self.save_data(self.filename, np.array(top_scores)) # This is the message that will be sent to the MCU to be displayed on the LED message = "Top Scores:" + "," + "#1: " + str(int( top_scores[0])) + "," + "#2: " + str(int(top_scores[1])) message = message + "," + "#3: " + str(int(top_scores[2])) return message """ Method that only terminates once the game is ready to start """ def wait_until_start(self): # Wait until the user starts the game again while True: game_status = self.receive_from_server() if game_status == "START": print("Game started") self.comms.send_message("start") break """ Checks the current game status, which is sent by the server @:param game_status: a string which represents the game status """ def check_game_status(self, game_status): # If the game ends, we stall sending data until a new game starts if game_status is not None and "GAME OVER" in game_status: # make sure data sending is stopped by ending streaming once game is over self.comms.send_message("stop") self.comms.clear() print("GAME OVER...") score = game_status.split(",")[ 1] # Get the score from the last round message = self.update_top_scores( int(score)) # Update the top three scores print(message) self.comms.send_message( message) # send the top three score to the MCU self.wait_until_start() # If the player has been hit, buzz the motor elif game_status == "BUZZ": print("Player hit!") self.comms.send_message("buzz") def run(self): # 1. make sure data sending is stopped by ending streaming self.comms.send_message("stop") self.comms.clear() # 2. start streaming orientation data input("Ready to start? Hit enter to begin.\n") mySocket.send("CONTROLLER ON".encode('utf-8')) # 3. Forever collect orientation and send to PyGame until user exits print("Waiting for game to start...") self.wait_until_start() print("Use <CTRL+C> to exit the program.\n") while True: message = self.comms.receive_message() if (message != None): command = None message = int(message) if message == -1: self.comms.send_message("LOW BATTERY") command = "QUIT" elif message == 0: command = "FLAT" elif message == 1: command = "UP" elif message == 2: command = "FIRE" elif message == 3: command = "LEFT" elif message == 4: command = "RIGHT" elif message == 5: command = "FIRE LEFT" elif message == 6: command = "FIRE RIGHT" elif message == 10: command = "PAUSE" elif message == 11: command = "PLAY" if command is not None: mySocket.send(command.encode("UTF-8")) print(command) # Check the current game status game_status = self.receive_from_server() self.check_game_status(game_status)