About a month ago I worked on a prototype project which would allow users to measure their rooms using an iPhone. It was a very interesting experience and I would like to share what I have learned. First question which comes to mind is – is it even possible? Answer – yes, it’s possible and was done before (look at Roomscan app on the AppStore).
As usual with this kind of task, before writing the first line of code, I started with some research on the subject. Turns out this topic is quite often picked up by a variety of people on different sites. Almost all of them are told that it’s not possible or very hard to accomplish. Don’t be discouraged by this, just as I wasn’t. I just told myself, that there is no learning without trying and picked up the first physics book that I was able to find. Yes, physics! You will see why…
As most of you know, the iPhone can detect its movement. What you probably didn’t know, is that it has three sensors to do so. First and most important is the accelerometer – it measures the acceleration affecting the device in a 3D space.
The second one is the gyroscope which detects the rotation of the device, relative to the gravity.
Last but not least is the magnetometer, detecting magnetic direction the phone is facing in relation to the real world north direction.
The data from those sensors is easily accessible with the Core Motion framework. So where is the problem? Well, first of all we can get the acceleration, the rotation and the direction, but not the distance. So how to transform the data we have to the data we need?
At this point, we cannot proceed without some basic knowledge of physics. What is acceleration and how to derive distance from it? Acceleration is a vector (a vector is a mathematical entity which has a value and a direction in a 3D space – x, y, z coordinates) which describes velocity changes in a given time and can be described with the formula below:
acceleration = velocity change / time change
If the acceleration is constant or time change is very small we can say:
velocity = acceleration * time
Now we have the velocity which gets us one step closer to getting the distance. Velocity is also a vector – it describes the rate at which an object changes its position.
velocity = distance traveled / time of travel
Again if the velocity is constant or time of travel is very small we can say:
distance traveled = velocity * time
If the initial velocity is equal to zero then:
distance traveled = (acceleration * time) * time
And if not then:
distance traveled = initial velocity * time + (acceleration * time) * time
Simple? Maybe… But we have to remember that those formulas are 100% accurate only if time between measurements is infinitely small, which in real life isn’t possible. Core Motion maximum sampling frequency may vary between the devices but should be in a range of 50 – 100 measurements per second. It’s not much, but it’s enough to get some usable data.
Ok. We have displacement (vector of distance) but its x, y, z coordinates are in the coordinate system of the device, which changes when we rotate the device (z axis is always pointing from the screen).
This type of data is useless, unless we transform it to the world’s coordinates system. To do so we need the data from the other two sensors – gyroscope and magnetometer. To make this kind of transformation we can use a rotation matrix (you can find more information on the subject here). Fortunately, we don’t have to calculate the matrix manually, as it’s already done by the Core Motion framework. Our only job is to use it to transform the initial displacement vector.
At this point, if we were living in a perfect world, we would get a perfect displacement vector, which means we would be able to accurately reproduce the device’s movement. Unfortunately, inaccuracy of motion sensors comes into play.
Boosting data accuracy
Don’t give up yet, as there is hope! Instead of using raw data from the sensors, if we pass it through a well selected virtual filter, we can get some nice results. A low pass filter will smooth out the data we receive and a high pass filter will cut out the noise from our measurements.
Accelerometer data diagram with and without low pass filter; Source.
With filtered data we get much better results – not perfect but usable. To get the best results I tried a lot of different approaches. In the end I chose LERP (a kind of a linear interpolation which takes a previous data point into account when calculating the next one) for its simplicity and rather fine results.
xValue = oldXValue * lerpFactor + newXValue * (1 – lerpFactor)
After about a week of research I was able to receive results with about 20 – 40 cm of measurement error which in my opinion is bearable. With a better filter the measurement error can be further decreased by a meaningful amount. Here are some screens from my app – the curved lines are paths I walked with the app turned on.
I walked through my room from corner to corner touching each one (green points). It’s clearly visible that I didn’t walk in straight lines (because I had to bypass furniture). The last two corners I touched twice in the same spot but the green points are misplaced. It happened because of the device’s inaccuracy.
So is indoor distance measurement usable for real life applications or is it just a meaningless feature? It depends on how would you like to use it and what level of accuracy you require. I’ll leave it for everyone to decide on their own.
PS. I was working on iPhone 5. iPhone 6 and 6+ have a new generation of motion sensors. Can’t say without testing, but it’s possible that distance can be measured with a much better accuracy on the new devices.