Tuesday, March 27, 2012

A while ago I got a question if 3-level expandable list views can be
implemented in Android. Android does not explicitly support it but the
flexibility of Android UI widget framework provides a relatively simple
implementation option: second-level expandable list views are to be
inserted into the first-level expandable list view as child views. There was a blog post with an example program and
everybody was happy. Even then I made a note that I don't agree with
the approach from the UI point of view because the display becomes
unnecessarily messy.

Now there was a comment at that blog post whether 4-level lists are
possible. Of course they are possible, it is just not a good UI design.
But I guess, you better try it yourself if you don't believe me. The
principle is the same (expandable lists embedded as children of
higher-level expandable lists), it is just much more complex to make
sure that everything is recalculated correctly for every event. In
order to follow the operation, I left a good amount of debugging code
in the project.

Saturday, March 10, 2012

I hope you are well prepared by now what you can expect from the compass-accelerometer sensor fusion - if not, please read my thoughts about the limitations. This time we are going to see, how to compensate the accelerometer with the compass.

The idea is very simple. The accelerometer and the compass both measure
vectors fixed to the Earth. These vectors are different - gravity and
ambient magnetic field - but we hope that their angle is constant
because they are both fixed to the same coordinate system. But while
the accelerometer is sensitive to the motion acceleration, the compass
is not. If we manage to rotate and scale the magnetic field vector into
the gravity vector, we have a reference while the accelerometer is
subject to motion acceleration and we can extract the motion
acceleration. Plus, we have a reliable gravity vector and an "in
motion" indication.

So the process is the following:

Calibrate the compass to the location and figure out the offsets
we discussed in the limitation part. Also measure the reference gravity
vector length.

If we find that the acceleration vector's length is close enough
to the reference gravity vector length, we assume that this means "no
motion" (see the discussion of this issue here). Find then the rotation axis and angle to rotate the compass
vector into the acceleration vector. We call these values reference
rotation axis, reference rotation angle, reference magnetic vector and
reference acceleration vector, respectively. Keep updating these values
as long as we are in "no motion" state.

If we find, that the acceleration vector's length is
significantly shorter or longer than the reference gravity vector
length, we are in "motion" state. Then we use the previously recorded
reference values and the actual magnetic field value measured by the
compass to calculate a simulated gravity vector. Using the measured
acceleration vector and the simulated gravity vector, we extract the
motion acceleration vector.

Simple, isn't it? Yes, it is, the only part requiring attention is the
rotation bit. The task is the following: we have a reference magnetic
vector and a reference acceleration vector from an earlier measurement.
Then the acceleration vector length was close to reference gravity so
we can assume that the reference acceleration vector mostly contains
gravity component. Now we have a current magnetic vector and a current
acceleration vector. The acceleration vector, however, seems to contain
motion acceleration component too. How to simulate a gravity vector
from the current magnetic vector?

The reference rotation axis nicely rotates the reference magnetic
vector to the reference acceleration vector but we need to apply the
rotation to the current magnetic vector. We need a new rotation axis
for this and we obtain it by rotating the reference rotation axis by
the rotation operation that rotates reference magnetic vector to the
current magnetic vector. This yields a current rotation axis.

Then we simply rotate the current magnetic vector around the
current rotation axis with the reference rotation angle. This yields a
simulated gravity vector. We also have to scale this vector so that its
length is equal to the reference gravity.

Once we are done with this, we can subtract the simulated gravity
vector from the current acceleration vector and we have the motion
acceleration.

The program is really just a variation of the gyroscope/accelerometer example program.
The main difference is that when the sampling is started, you have to
calibrate the compass first. The application instructs you, which
edge/side of the device should be turned toward the Earth and the
calibration steps already passed disappear from the screen. This is
really a major annoyance compared to the gyroscope version but that's
the price we pay for having a widely available sensor instead of the
rare gyroscope.

After the calibration you see the familiar ball that visualizes the
measured motion acceleration (the size of the ball is proportional with
the z component of the motion acceleration). As with the gyroscope case,
the motion acceleration is rotated into the Earth's coordinate system.
If you need the motion acceleration in the device's coordinate system,
just remove the rotateToEarth() method invocation.

If you set DEBUG to true in SamplingService.java, the program
creates a (potentially large) file containing measurement data as
/sdcard/capture.csv. You can analyze it with this Sage script.

Now we have two methods of extracting motion acceleration. The
gyroscope is more convenient because it is not subject to external
magnetic fields or metal objects but is available in less devices. There
are more hassles with the compass but at the end it works nicely too.
The next step is to recognize movement patterns in the 3D motion
acceleration vectors.

Saturday, March 3, 2012

Anybody out there who has experience with android.intent.action.PHONE_STATE broadcasts and 4.0.3? I wrote this simple application that is just a receiver for this broadcast intent. It works beautifully on 2.2 but does not capture any broadcasts on a Nexus S with the official Google 4.0.3 firmware. The manifest has the permission (XML mangling because of the blog engine):

Thursday, March 1, 2012

In the previous parts of our sensor processing series,
we have seen how to use the gyroscope to separate the motion and the
gravity acceleration components measured by the accelerometer. But
gyroscope-equipped phones are rare. Can we have a "poor man's gyroscope"
that is more widely deployed in today's devices?

Let's reiterate the problem. We have an accelerometer that is
exposed to two main kinds of accelerations: gravity and motion
acceleration. Gravity acceleration is always present. Sadly, its
direction is not constant but changes with the direction of the device.
If the device is subject to motion acceleration (e.g. the user is
walking), motion acceleration is added to the gravity component. Motion
acceleration vector changes dynamically due to the movement phases. If
we have to assume that motion acceleration is present but we don't know
the gravity acceleration vector, separating the gravity and motion
acceleration components is impossible. We need another sensor therefore
and we made a good use of the gyroscope so far.

The other evident sensor that can be used to determine the Earth's
coordinate system relative to the device coordinate system is the
compass or magnetic sensor. Compass is attractive because it is widely
available in today's devices. Unfortunately it has certain drawbacks
that make it more inconvenient to support the accelerometer than
gyroscope. In this part I will go through the characteristics of the
compass to set the expectations before we start to discuss, how to use
it together with the accelerometer.

First the basics. The compass measures the magnetic field the device
is exposed to. This magnetic field has many components. We are most
interested in the Earth's magnetic field but obviously there are other
disturbances, e.g. metal objects, magnets, magnetic fields generated by
electric devices. The Earth's magnetic field is not trivial either.
Contrary to a belief I hear often, the Earth's magnetic field points
mostly downward, toward the center of the Earth. The effect is called
magnetic inclination and it means that the Earth's magnetic field has a
varying degree relative to the Earth's surface. Where I live, the
inclination is about 70 degrees. The component that compasses measure
to find the "North" (more exactly: magnetic North) is just the x and y
components of the magnetic field which are smaller than the z component.

External magnetic fields can make measuring the Earth's magnetic
field virtually impossible. The graph below shows an extreme case when I
started compass sampling and walked into an underground train that
eventually left the station.

The x axis is the sample count (proportional to time), the y axis is
the length of the magnetic field vector measured by the compass. You
can see that around 1900 sample count the metal body of the train starts
to show its effect. Then around 2400 the electrical traction motors of
the train start to operate which generates at its peak 6 times larger
magnetic field than the Earth's magnetic field. This magnetic field also
varies in time as the power of the motors fall after the train is
accelerated to its cruising speed. First rule therefore is to avoid
large electric devices, particularly those that vary their power in
time.

The compass also has characteristics that make life harder. The two
diagrams below show two measurements. In each measurement the device was
rotated around its y axis a full circle (see the SensorEvent documentation about device axes in Android)
then it was rotated 90 degrees around its x axis and rotated fully
around the y axis again. The two measurements were different only in
their location. Both were performed in average rooms, relatively far
from metal objects (as much as it is possible in average rooms and
buildings).
We expect to see neat circles centered around the origo. What we see
is that the circles are not centered around the origo but have
characteristic offsets. Worse, those offsets are not the same even
though the measurements were done with exactly the same device.

In order to support the accelerometer with the compass, we have to swallow the following two limitations.

No magnetic fields around that vary in time (typically electric devices, trains, etc.)

The compass has to be calibrated at each location (by rotating the
device around). This pretty much excludes measurements on the move (e.g.
walking).

If these limitations are acceptable, however, then we have a
reference sensor that can replace the accelerometer while the device is
subject to motion acceleration and we can extract motion acceleration.
We can implement those Wii-like games even if there is no gyroscope in
the phone.

About the blog

This blog is a personal diary about my adventures with the Google Android platform. I write it in the hope that others may find my experiences useful but please, beware. The blog is created as I gain experience about the platform myself so errors, omissions, etc. may be found in the entries.