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()


Beispiel #4
0
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)