EPx professional blog and repository for braindumps

2009/08/28

PyS60: more fun with N85 accelerometer and rotation sensors

I am no longer surprised about how many accelerometer-related iPhone applications have been created. This thing is really fun and addictive to play with. It has been very pleasant to exercise the brain's L2 cache and remember high-school Physics classes.

Unfortunately, there will be no time to deep discussion of this topic in upcoming Python for Series 60 course, in PythonBrasil event. So FWIW I will post some extra information here, where any interested party can find it.

In that video I made some days ago, I stated that accelerometer applet was lacking gravity compensation. First thing I had to discover, is why gravity induces a opposite-sign reading on accelerometers. That's why:



When the phone is accelerated e.g. by your hand, the seismic weight keeps "behind" the frame. The circuit is wired to measure this kind of acceleration. But, when gravity acceleration is being measured, the opposite happens:


Since the phone is not moving, the gravity pulls the weight to the same direction as the acceleration vector, giving a inverted-sign reading.

Having understood this "feature" of accelerometers, I could work on gravity compensation, so my graphical acceleration applet could discount gravity from readings. First I need to take a "sample" of gravity force, since N85 sensors return accelerometer values in an arbitrary unit. I did it the laziest way possible:


# detect gravity value
# for this, cell phone is expected to over a table,
# perfectly horizontal, and steady
e32.ao_sleep(.5)
g = accel.z
print "gravity = %d" % g


From now on, every Z axis accelerometer can be subtracted by "g", in order to get a gravity-free acceleration.

Of course, this is valid only while the cell phone is kept horizontally to the ground. If you tilt it in a different way, the gravity effect will spread through all axis. And, to detect tilt angles, I need to read the rotation sensor too.

After some trail-and-error based on common sense, I found the following formulas to calculate the gravity force for each axis:


# Get raw accelerometer measures
ax, ay, az = accel.x, accel.y, accel.z
# Calculate gravity force based on tilt angles
gz = g * math.sin(rx) * abs((-math.cos(ry)))
gx = g * math.sin(ry) * abs(math.sin(rz))
gy = g * math.cos(rx) * abs(math.cos(rz))
# Discount gravity from original measures
ax = ax - gx
ay = ay - gy
az = az - gz
# Now we have gravity-free accelerometer measures in ax, ay, az

I am not 100% sure those formulas are right; I can just say that they worked, that is, they allowed me to show only "true" acceleration forces, regardless of tilt. Also, I know that a matrix multiplication would be opportune, but honestly I never learnt matrixes very well :)

Sometimes the accelerometer applet gave spurious readings, flashing big acceleration values briefly. While debugging this, I saw that rotation sensors returned -1 at some readings, breaking down that gravity discount calculations. I chose to ignore all sensor data on -1 angles, and the spurious flashes were gone.

I have made a series of pictures to show how the accelerometer and rotation axis "feel" on the phone. Let's begin with accelerometers, which are simpler to understand:



The axis are relative to the cell phone; it is obvious, but very easy to forget -- after playing too much time with the phone over a table, numbers feel funny for a while when you put it straight up.

When acceleration happens in the direction of the arrow, the measure will be positive. Z axis is positive when phone is accelerated towards front. As we saw, gravity will be perceived as opposite-sign measures.

At least for me, the rotation axis are more difficult to "get", so I made several pictures in order to understand it better:



The axis are the same as the acceleration axis. The circular arrows try to show the tilt direction that increases the angle (by the way, they are clockwise for all three axis). The measured angles for this position are: X=90º, Y=180º and Z=undefined (may return any angle).

The "North" (0º) position for the X axis is when cell phone is upright, like in the following picture:



Note that, in this case, the Y axis becomes undefined. Here we have a limitation of these rotation sensors: they are not gyroscopes, so they depend on gravity to read the angle. When two axis get parallel to the ground, the third one becomes undefined.

The origin (0º) for the Y axis is when the cell phone is e.g. on a flat table, face down:



In the following example, cell phone rests on its right side, and so the Y axis reads 270º:




The origin for Z axis is, like for the X axis, the phone in upright position. Z axis detects "yaw" rotation, like the following picture shows:



The "undefined axis" problem disappears when phone is tilted (neither perpendicular nor parallel to the ground). In the example above, as we add yaw to the phone, Y axis tends to resolve to 270º (assuming that phone is perfectly vertical in X and Y axis).

When the phone has left yaw, Y resolves to 90º:



And finallly, an example of X axis rotation:



DYI INS

Unfortunately, relative rotation plus acceleration is not enough input for a inertial navigation system (INS). That's too bad, I was willing to write something like this, despite the low precision of rotation sensors (15º). A true, three-dimentional INS system (which can estimate position and altitude) needs 3 accelerometers and 3 gyroscopes (or one gyroscope with 3 gimbals).

Just to illustrate the impossibility: if the cell phone undergoes a circular trajectory parallel to the ground, the perceived centrifugal acceleration can't be distinguished from a linear acceleration, because the change in heading angle is not known. The INS wouldn't know if it is going somewhere or if it is walking in circles!

We can only integrate the acceleration if we guarantee an one-dimensional trajectory. Like a straight line, or a train that runs on a predefined track. And we must strap down the phone to the vehicle in a predefined position and angle.

One possibility would be to integrate GPS data with sensors, but the very objective of an INS is to navigate without the need of a GPS... fail!

Some cell phones (including Nokia ones) have a magnetometer sensor (compass). This allows for a two-dimensional INS. If we add some simplifying assumptions e.g. that vehiche is a car and will never have a "roll" angle (except in case of accident :) I think it is even possible to have a 3D INS, without gyros.

13 comentários:

torsak disse...

Thank you for your post, this is really interesting! I am working on an augmented reality application on a N97, and wanted to get the 3d position as well as the orientation from the rotation and accelerometer sensors, looks like it is not possible :(
You said there was only one gyroscope measuring the rotation on the cellphone, so there is no way the 3 axes can be resolved in the rotation, right? For example in your last picture, if you turn the cellphone around the vertical (gravity) axis, these changes are not detected.
Long story short, the cellphone only compares the 3 axes to the vertical (gravity) axis, am I right?

EPx disse...

Yes, that's what I found, regarding the rotation sensors. You have only one true "absolute" angle, which is cell phone angle in relation with the horizon/ground plane.

But I think N97 has a magnetometer/compass, and rotation sensors + compass should be enough for a ground-level AR app. Better yet, an AR application would throw GPS in the mix.

My N85 does not have the compass, so I can't test this myself :)

torsak disse...

Yes I forgot about the magnetometer on the N97. But I will have to use also the rotation sensor to get the full orientation, because the magnetometer gives only the direction of the north, and I need another axis.

By the way, there might be a problem in your formula to express g in the local coordinates thanks to the rotation angles: when there is a pure rotation on the z-axis (rx = ry = 0), your formula gives gy = g cos(rx), gx = gz = 0; so the norm of the vector is not g. There must have been a component on the x-axis.
Also in your formula, what do you take as an angle when the angle is undefined?

EPx disse...

Certainly it has this and more errors. I'd consult a textbook if I were to do it for a "production" app, using matrix multiplication etc. To be frank, I am amazed I could make it "work" without such help...

When the angle is undefined, PyS60 sensor returns -1, and then I quit early on function, so the calculations never have to deal with that situation.

torsak disse...

Thanks to the Euler angles representation of rotation matrices, I came up with this formula which seem to work in all cases:
gx = -g * sin(rz)
gy = g * cos(rz) * cos(rx)
gz = g * cos(rz) * sin(rx)
I do not think the ry angle should be involved in the formula, because the the y axis is actuallly gravity, and the sensor cannot measure the rotation around this axis. What do you think?

Also if you have any textbook reference, can you give me a link?
Thanks.

EPx disse...

It might be right. I didn't test them in the phone, but I imagined a situation where an upright cell phone (rz=0) is rotated towards rz=90 and then it is rotated 45 degrees in Y axis. But rotating in Y axis also affects RZ, so taking only RZ into account does the trick.

I was pretty sure that those abs() in my formulas were a symptom of an error :)

EPx disse...

i just tested the formula on cell phone, and they seem not to be right. When cell phone is upright, the accelerations are correctly reset, but when RY=45, X and Z axis show gravity acceleration.

Visually, my formulas "look" right, so I need to stay with them for my course, until we find the correct answer :)

torsak disse...

I am having some trouble to understand the real meanings of these angles.
Let's take a cellphone A in any orientation, and a cellphone B in an upright position. The rotation angles A are 0,0,0 and for B they are rx,ry,rz.
If we apply the three successive rotations (rx around X-axis, ry around Y-axis and rz around Z-axis) to the cellphone A, does it have the same orientation than cellphone B? (when I wrote x, y and z in capital letters I meant the 3D *absolute* axes of the upright cellphone).

Anônimo disse...

I have problem with sensor :( it doesnt work when i run script. ImportantError: No module named sensor. - this is error when i try on this application. Help me pls

EPx disse...

@Anônimo: either your cell phone does not have this API (only 3rd Edition FP2 and 5th Edition have the API as I have described in this post), or you are using an older verson of PyS60.

@torsak, good question :)

torsak disse...

Just another question: Can the ry value take other values than 0, 90, 180 and 270 deg?

Also when you say that my formulas worked for an upright phone, but not when ry = 45deg. But when ry = 45deg, rx = rz = 0, the phone is still upright, right?

I have not received my N97 yet, and I can only get the x-axis component of the rotation sensor on my N95, so I cannot do any test right now :(

Can you test the following formula?
gx = -g sin(rz) f(ry)
gy = -g cos(rx) cos(rz)
gz = g sin(rx) f(ry)
where f(ry) = -1 if ry is in [45, 315] and 1 otherwise.

Thanks :)

EPx disse...

At least in N85, every rotation sensor can take any value with 15 degree resolution. Only the N95's sensor is limited to 4 values, AFAIK.

I mentioned RY=45, but I had some tilt in other axis too. Actually, RY is undefined (can be of any value) when the cell phone is upright.

I can't test your formulas right now in my sample application. But I think that they won't work, because I

a) begin in upright position (rx=0, ry=undefined, rz=0)

b) rotate cell phone to the right, making rz=90, ry defining to 270 and rx getting undefined

c) then lean the cell phone 45 degs, making ry=135 or 45, while keeping rz=90, ry changing to 225 or 315; and rx resolving to 90

|gx| = g |sin(rz)| = g (wrong, should be 0,707 g)
|gy| = g |cos(rx) cos(rz)| = 0 (right, Y axis is parallel to the ground)
|gz| = g |sin(rx)| = g (wrong, should be 0,707g)

The rx=90, ry=225/315 and rz=90 are actual values read from cell phone sensor; they are not product of my imagination (I confirmed that).

In face of them, you formula seems wrong because two axis (X and Z) will show full g acceleration while g should be distributed among them, as I rotate the cell phone in Y axis.

Your formula WOULD be right if Z axis "rotated" together when Y axis is rotated. In that case, if I made ry=225, rz would have to go to 45 or 135. But the axis itself does not rotate. RZ sensor keeps reporting (correctly) 90 degrees in Z plane, unless RY is made 0 or 180, case in which RZ becomes undefined (due to physical impossibility of measuring the tilt).

torsak disse...

Okay thanks for the example, I thought the rotation angles were referring to angles around the axes of the upright phone.

However, I still do not understand completely the meaning of them. For example, when the phone has left yaw, rx = 0, ry is undefined and rz = 30. Why is ry undefined? I would say that ry = 90deg, so that after this rotation the x-axis is perpendicular to g...

Is there any way to define these angles formally? (for example I would say that ry is the angle so that after rotation of ry around y-axis, x is perpendicular to g, but it does not seem to be right when the phone has yaw)

Postar um comentário