As a proof of concept I wanted to see if I could pass readings from a door mounted accelerometer to a decision tree algorithm to predict if the door is opening or closing when movement is detected. Using an old smartphone I built an App that takes data from the phones accelerometer and builds a predictive model to classify the movement of a door.

After fixing the phone to a door the first step is to collect the data to train the model. This is done by repeating three steps a number of times. At the start of each step a three second countdown beings after which accelerometer data is collected every 100ms over a one second period and averaged into a single set of x, y and z axis readings. The first step is to hold the door still so the model can learn what it looks like when the door is not moving. Second step is to open the door and the third is to close it. Each step is repeated a number of times, typically three to five and the result is a set of averaged x, y and z axis readings each labelled with their respective steps.

// training data
[
    { x: 0.39, y: 9.89, z: -0.23, step: 'still' },
    { x: 0.47, y: 9.89, z: -0.22, step: 'still' },
    { x: 0.38, y: 9.89, z: -0.26, step: 'still' },
    { x: 1.16, y: 9.85, z: 0.03, step: 'open' },
    { x: 0.55, y: 9.88, z: 0.36, step: 'open' },
    { x: 0.98, y: 9.87, z: 0.04, step: 'open' },
    { x: 1.11, y: 9.88, z: -0.51, step: 'close' },
    { x: 1.11, y: 9.85, z: -0.38, step: 'close' },
    { x: 0.97, y: 9.90, z: -0.50, step: 'close' },
]
Example training data

Once training is completed the data collected is used to build the predictive model. I have used a CART based decision tree algorithm for this purpose, I created it using this video and the related code as a guide. In this problem domain the most important value from the accelerometer readings is the z axis, as this is the axis affected by the swinging motion of the door opening and closing. For this reason the tree is built entirely on the z axis readings from the training data.

Is z >= -0.26
--> True
    Is z >= 0.03
    --> True
        Predict:{"open":"100%"}
    --> False
        Predict:{"still":"100%"}
--> False
    Predict:{"close":"100%}
The tree generated by training data for the cupboard door

Monitoring the door state

With the predictive tree model generated, real time data can be classified. The three classifications are still, open and closed. The still state acts as a buffer between the open and closed states, if a classification of still is made the door state remains the same. This way the predicted state is only ever seen to switch between open and closed.

To know when to being sampling real time accelerometer readings a movement threshold must be broken. First step to creating the threshold is to take the average of every z axis reading when the door was held still during training. Using this average a positive and negative threshold is created by adding and subtracting an arbitrary value. Every 100ms a reading is taken from the accelerometer and if the z axis value exceeds either the positive or negative thresholds then sampling begins for x amount of seconds.

Once a sample is recorded it is averaged to create a single set of x, y and z axis readings. From this averaged sample a prediction is made against the decision tree model which results in one of the three classifications. If the resulting classification is not still and it has changed since the previous classification you would see the door state switch from open to closed or from closed to open.

Detection on a cupboard door

Configuration

I have managed to achieve relatively good results when testing the App. It's accuracy relies heavily on the quality of training. Typically if training steps to open, close and hold still the door are correctly followed the quality of training data will be high.

Accuracy is also reliant on the configuration of certain values that need to differ depending on the size and swing of the door being monitored. These configuration values are:

Sampling period

On short doors with a small swing such as cupboards, a lower sampling period works best. Typically a sample time of 300ms, resulting in a sample of 3 accelerometer readings (one every 100ms) gives the best results for small doors. This is most likely because the movement of the door swing is spread over a more condensed period of time.

Larger doors benefit from a longer sampling period, between 800ms and 1000ms, as the doors larger arc of swing is spread across a greater period of time.

Positive and negative movement thresholds

The value added and subtracted to create the positive and negative movement thresholds has an effect on sensitivity.

Higher values reduce the chance of a slight knock or shake to the door being incorrectly interpreted as movement to close or open. However a higher value also drives down sensitivity. The inverse is true for lower values.

A value between 0.05 and 0.03 works best.

Cooldown period between switching states

When opening or closing a door force is often applied in the opposite direction towards the end of the doors swing in order to stop the door slamming open or shut. To prevent this force being mis-interpreted a cooldown period is applied between state changes during which all movement is ignored.

It's important to set the cooldown just right, too long and legitimate movement could get ignored, too short and detection becomes too sensitive to be accurate. During testing the cooldown was set to 2 seconds, this is possibly too long and may be better around the 1 second mark.

Further work

The App was created primarily as a proof of concept and I'm happy that it achieved it's aims. There are shortcomings and the accuracy should not be relied upon but for the most part if configured correctly the predicted door state usually reflects the actual physical reality.

A more promising direction would be to apply this machine learning process to help distinguish between significant door movements and minor wobbles. This could reduce any false positives generated from slight movement or rattling of the door. An example where this could be beneficial is monitoring a door for security concerns when you want to be alerted to human interaction but not environmental.

I have pushed the decision tree code up to my GitHub. It's a complete re-write of the original Python example I found. Finally I have a video of the detection working on a larger door:

Detection on a large door