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
current_time = time() if (current_time - previous_time > process_time): previous_time = current_time try: heart_rate, peaks, filtered = hr.process() except: continue print("Heart Rate: " + str(heart_rate)) plt.cla() plt.plot(t_plot, filtered) plt.plot(t_plot[peaks], filtered[peaks], 'rx') plt.plot(t_plot, [0.6]*len(filtered), 'b--') # If the heart is too large, then it should be displayed as an inaccurate reading if(heart_rate > MAX_HEART_RATE): plt.title("Inaccurate Reading") comms.send_message("Inaccurate Reading") # Otherwise display the Heart Rate on the Plot and send to the LED display else: plt.title("Heart Rate: %d" % heart_rate) comms.send_message("HR: " + str(int(heart_rate)) + " bpm") plt.show(block=False) plt.pause(0.001) except(Exception, KeyboardInterrupt) as e: print(e) # Exiting the program due to exception finally: print("Closing connection.") comms.send_message("sleep") # stop sending data comms.close()
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)