Using raw motion data to build a Basic Sleep Detector in Python
Since the Aidlab SDK 1.0.3, we opened up the possibility to start playing with raw motion data (such as acceleration) from Aidlab Chest Strap. This tutorial will cover all the necessary details to connect, read, and analyze data, ending with creating a basic Sleep Detector.
- Create an empty directory with a
- Download the Aidlab SDK.
- Unpack it, and open the
- Drag the
Aidlabinside your project directory, so the structure should look similar to:
SleepDetector/ |-- Aidlab/ |-- basic-sleep-detector.py
To connect with Aidlab, all you have to do is to invoke the
connect function from the
Aidlab module, and set the receiver's callback:
# basic-sleep-detector.py import Aidlab from time import sleep, time from datetime import datetime class MainManager(Aidlab.Aidlab): def __init__(self): super().__init__() self.startTimeOfSleepingPosition = 0 self.isInSleepingPosition = False def did_connect(self, aidlab): print("Connected to: ", aidlab.address) def did_disconnect(self, aidlab): print("Disconnected from: ", aidlab.address) def did_receive_quaternion(self, aidlab, timestamp, qw, qx, qy, qz): self.naiveSleepDetector([qw, qx, qy, qz]) if __name__ == '__main__': signals = ["orientation"] main_manager = MainManager() main_manager.connect(signals) while True: pass
At this point, you are ready to listen for an upcoming stream of data from the 9-axis Inertial Motion Unit (IMU) sensor, at a 30Hz rate.
Getting the user's vertical orientation
Our sleep detection algorithm will use the user’s position to determine whether they are lying on the bed or not. To do so, we have to estimate their vertical orientation, e.g.: check whether the chest is parallel to the floor or not. Possible vertical orientations are:
Up- when the user is lying on his back
Down- the user is lying on his belly (or is doing push-ups)
Front- the user is standing, walking, sitting, etc.
First, let's define a function giving us sleep detection based on a naive method which takes the user's vertical position:
def did_receive_quaternion(self, aidlab, timestamp, qw, qx, qy, qz): self.naiveSleepDetector([qw, qx, qy, qz]) def naiveSleepDetector(self, value): quaternion = value[0:4] verticalOrientation = self.determineVerticalOrientation( quaternion, quaternion, quaternion, quaternion) # Sleep detection heuristic self.basicSleepDetector(verticalOrientation)
To determine the vertical orientation, we will use the given formula:
Quaternion x UpVector
Z from the normal, we might say:
Z >= 0.5then the vertical orientation is
Z <= -0.5, then the vertical orientation is
- else, the vertical orientation is
Coding such logic:
def determineVerticalOrientation(self, qW, qX, qY, qZ): normalVec = self.normalVectorToUp(qW, qX, qY, qZ) if normalVec >= 0.5: return "OrientationDown" elif normalVec <= -0.5: return "OrientationUp" else: return "OrientationFront"
The multiplication of quaternion and vector is represented as follows:
V' = Q * V * conjugate(Q)
where the vector
V is being treated as a quaternion with
def normalVectorToUp(self, qW, qX, qY, qZ): quat = self.multQuat(qW, qX, qY, qZ, 0, 0, 0, 1) quat = self.multQuat(quat, quat, quat, quat, qW, -qX, -qY, -qZ) return [quat, quat, quat] def multQuat(self, w, x, y, z, qW, qX, qY, qZ): newW = w * qW - x * qX - y * qY - z * qZ newX = w * qX + x * qW + y * qZ - z * qY newY = w * qY + y * qW + z * qX - x * qZ newZ = w * qZ + z * qW + x * qY - y * qX return [newW, newX, newY, newZ]
Basic Sleep Detector
We have the
verticalOrientation, so we could build the Basic Sleep Detector that will be based on the time period when we are in a sleeping position (
OrientationUp) for more than 10 minutes. The basic heuristic goes as follows: if a user is in a sleeping position for more than 10 minutes, then he or she is sleeping:
def __init__(self): super().__init__() self.startTimeOfSleepingPosition = 0 self.isInSleepingPosition = False def basicSleepDetector(self, verticalOrientation): if (verticalOrientation == 'OrientationUp' or verticalOrientation == 'OrientationDown') and self.isInSleepingPosition == False: self.isInSleepingPosition = True self.startTimeOfSleepingPosition = time() elif verticalOrientation == 'OrientationFront' and self.isInSleepingPosition: self.startTimeOfSleepingPosition = 0 self.isInSleepingPosition = False # Sleep detection heuristic: # We are sleeping if we are in sleeping position for longer than 10 minutes if self.isInSleepingPosition and (time() - self.startTimeOfSleepingPosition > 10 * 60): print("I am sleeping")
Turn on Aidlab and start the script:
Of course, the algorithm will be fooled in situations when the user is laying on his side (we could simply overcome this by using the RPY axes instead), but the main purpose for this article is to present the whole process, starting with connecting to Aidlab, through collecting raw data and creating the necessary math behind calculating the vertical orientation, to building the basic sleep detector. We encourage you to experiment by yourself to get the desired result and improvements in the sleep detection method. Using other signals such as heart rate, respiration, or even the slight changes of the skin temperature, will highly improve the quality of your sleep detector.
The full source code is available on our GitHub (see