Convert Magnetic Field X, Y, Z Values from Device into Global Reference Frame

Convert magnetic field X, Y, Z values from device into global reference frame

In my comment on the checked answer on the link you provided above, I referred to my simple answer at calculate acceleration in reference to true north

Let me answer here again with more clarification. The answer is the product of the rotation matrix and the magnetic field values. If you read further on the "X is always very small" is the correct value.

The accelerometer and magnetic field sensors measure the acceleration of the device and the magnetic field of the earth at the device location respectively. They are vectors in 3 dimentional space, let call them a and m respectively.

If you stand still and rotate your device, theoretically m does not change assuming there are no magnetic interference from surrounding objects (actually m should change little, if you move around since the magnetic field of the earth should change little in a short distance). But a does change, even though it should not be drastic in most situation.

Now a vector v in 3 dimensional space can be represented by a 3-tuples (v_1, v_2, v_3) with respect to some basis (e_1, e_2, e_3), i.e v = v_1 e_1 + v_2 e_2 + v_3 e_3. (v_1, v_2, v_3) are called the coordinates of v with respect to the basis (e_1, e_2, e_3).

In Android devices, the basis is (x, y, z) where, for most phone, x is along the shorter side and pointing right, y is along the longer side and pointing up and z is perpendicular to the screen and pointing out.

Now this basis changes as the position of the device changes. One can think these bases as a function of time (x(t), y(t), z(t)), in mathematics term it is a moving coordinate system.

Thus even though m does not change, but the event.values returns by the sensors are different because the basis is different (I will talk about fluctuation later). As is, the event.values are useless because it gives us the coordinates but we do not know what the basis is, i.e with respect to some basis we know.

Now the question is: is it possible to find the coordinates of a and m with respect to the fixed world basis (w_1, w_2, w_3) where w_1 points toward East, w_2 points toward magnetic North and w_3 points up toward the sky?

The answer is yes provided 2 important assumptions are satisfied.
With these 2 assumptions it is simple to calculate (just a few cross products) the change of basis matrix R from the basis (x, y, z) to the basis (w_1, w_2, w_3), which in Android is called the Rotation matrix. Then the coordinates of a vector v with respect to the basis (w_1, w_2, w_3) is obtained by multiply R with the coordinates of v with respect to (x, y, z). Thus the coordinates of m with respect to the world coordinates system is just the product of the rotation matrix and the event.values returned by the TYPE_MAGNETIC_FIELD sensor and similarly for a.

In android the rotation matrix is obtained by calling getRotationMatrix (float[] R, float[] I, float[] gravity, float[] geomagnetic) and we normally pass in the returned accelerometer values for the gravity parameter and the magnetic field values for the geomagnetic.

The 2 important assumptions are:

1- The gravity parameter represents a vector lying in w_3, more particular it is the minus of the vector influenced by gravity alone.

Thus if you pass in the accelerometer values without filtering, the rotation matrix will be slightly off. That is why you need to filter the accelerometer so that the filter values are approximately just the minus gravity vector. Since the gravitational acceleration is the dominant factor in the accelerometer vector, normally low pass filter is sufficient.

2- The geomagnetic parameter represents a vector lying in the plane spanned by the w_2 and the w_3 vectors. That is it lies in the North-Sky plane. Thus in term of the (w_1, w_2, w_3) basis, the first coordinate should be 0. Therefore, the "X is always very small" as you stated it above is the correct value, ideally it should be 0. Now the magnetic field values will fluctuate quite a bit. This is kind of expected, just as a regular compass needle will not stand still if you keep it in your hand and your hand shakes a little. Also, you may get interference from objects surround you and in this case the magnetic field values are unpredictable. I once test my compass app sitting near a "stone" table and my compass was off by more than 90 degrees, only by using a real compass that I found out that there is nothing wrong with my app and the "stone" table produces a real strong magnetic field.

With gravity as a dominant factor you can filter accelerometer values, but without any other knowledge, how do you fitler magnetic values? How do you know if there is or isn't any interference from surrounding objects?

You can do a lot more like complete knowledge of your device spatial position etc with the understanding of rotation matrix.

How to convert the magnetic field vector from sensor coordinate space to camera coordinate space?

There were two issues with my code above.

First, I was using compose incorrectly. To transform by A and then B you do B.compose(A). With that fix I started getting consistent sensorToCameraPose's.

Second, after that fix, I had a 90° rotation between x and y. From u/inio on Reddit:

So usually for phone form-factor devices there will be a 90° rotation between the camera coordinate system (which is defined to have +x point in the diction of the horizontal axis of the physical camera image, typically the long axis of the device) and the android sensor coordinate system (which has +y pointing away from the android navigation buttons, and +x thus along the short axis of the device). The difference you describe is an 88.8° rotation. Maybe you want the virtual camera pose? Source

I tested with using the getDisplayOrientedPose(). With it, I get what I expect when I am in portrait mode. But, if I flip to landscape, then the coordinate system changes, and I am off by a 90° rotation. So I instead did the rotation myself.

public void processFrame(Frame frame) {
if (frame.getCamera().getTrackingState() != TrackingState.TRACKING) {
return;
}

// Get the magnetic vector in sensor that we stored in the onSensorChanged() delegate
float[] magneticVectorInSensor = {x,y,z};

// Get sensor to world
Pose sensorToWorldPose = frame.getAndroidSensorPose();

// Get camera to world
Pose cameraToWorldPose = frame.getCamera().getPose();

// +90° rotation about Z
// https://github.com/google-ar/arcore-android-sdk/issues/535#issuecomment-418845833
Pose CAMERA_POSE_FIX = Pose.makeRotation(0, 0, ((float) Math.sqrt(0.5f)), ((float) Math.sqrt(0.5f)));
Pose rotatedCameraToWorldPose = cameraToWorldPose.compose(CAMERA_POSE_FIX);

// Get world to camera
Pose worldToCameraPose = rotatedCameraToWorldPose.inverse();

// Get sensor to camera
Pose sensorToCameraPose = worldToCameraPose.compose(sensorToWorldPose);

// Get the magnetic vector in camera coordinate space
float[] magneticVectorInCamera = sensorToCameraPose.rotateVector(magneticVectorInSensor);
}

Getting magnetic field values in global coordinates

The coordinates of M with respect to the word coordinate is just the multiplication R*M.

The rotation matrix R is mathematically the change of basis matrix from the device coordinate to the word coordinate.
Let X, Y, Z be the device coordinate basis and W_1, W_2, W_3 be the word coordinate basis then

M = m_1 X + m_2 Y + m_3

and also

M = c_1 W_1 + c_2 W_2 + c_3 W_3
where R * (m_1, m_2, m_3) = (c_1, c_2, c_3) transpose.

Low pass filter is only used to filter out accelerations in the X, Y directions. RemapCoordinateSystem is used to change the order of the basis, ie changing from W_1, W_2, W_3 to W_1, W_3, W_2.

Magnetic Fields, Rotation Matrix And global coordinates

If you read my answer at Convert magnetic field X, Y, Z values from device into global reference frame You still do not understand it.

A1. You multiply the Rotation matrix with the coordinates of the magnetic field vector in device coordinate system to get the coordinates of the magnetic field vector in the world coordinate system.

Let me emphasize: The above said Rotation matrix and not inverted rotation matrix.

The Rotation matrix obtained by calling getRotationMatrix is the change of basis matrix from the device basis to the world basis. That is given any vector v with coordinates in device coordinate sytem, the coordinates in world coordinate system of the same vector v can be obtained by multiply the rotation matrix with the coordinates in device system coordinate.

The inverted rotation matrix is the change of basis matrix from the world basis to the device basis. Thus when you multitply this matrix with a coordinates, it is interpreted as multiply the matrix with the coordinates of a vector in world coordinate system to obtain the coordinates of the same vector in device coordinate system. Therefore if you multiply the inverted rotation matrix with the coordinates of the magnetic field vector as returned by the magnetic sensor. Then the coordinates is interpreted as the coordinates of a vector in the world coordinate system and thus do not represent the magnetic field vector and the resulting product is not the coordinates of the magnetic field vector in the world coordinates system. Actually it is the coordinates of a vector in device coordinate system.

A2. getOrientation is meaningful only if the device is flat. For me it just a bunch of angle calculations. I look at what I try to do geometrically and then use the rotation matrix to calculate what I want. For example, to calculate the direction where the back camera pointed, I look at it as the direction of -z (the opposite of the vector orthogonal to the screen). Thus to find this direction, I projected -z to the world East-North plane and calculate the angle between this projection vector and the North axis. Now if you think this way then rotation of the device will not change the direction of -z, thus the projection vectors are the same as you rotate the device. If you use getOrientation then you have to precall remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR) for getOrientation to give you the correct result.

A3. The getRotationMatrix assumes that the geomagnetic parameter is the coordinates of a vector lying entirely in the North-Sky plane. Well any vector lying in this plane has to have x coordinate equal to 0. This is just basic linear algebra.

A4. The answer is no. To get the spatial position you have to be to express these vectors relative to a fixed coordinate system. with only coordinates of these vectors in device coordinate system, there is no way you can find a fixed basis that allow you to calculate the change of basis matrix from the device basis to this fixed basis. The 2 conditions stated in my link above need to be satisfied to calculate the change of basis.

Why is the X coordinate value so close to zero when android magnetic field sensor values are converted to Earth coordinate system?

The data is correct, the x values in world coordinate should be close to 0. This is because the magnetic field sensor vector is assume to lie on the world North-Sky plane, thus the x-coordinate (East coordinate) should be zero.
In the first graph, x varies because the device basis varies. Think of a fixed vector but the coordinates represent this vector change. Thus 2 different coordinates may represent the same vector.

Getting Voltage from Magnetic Sensors

This isn't a programming question. It's physics. You CANNOT measure voltage indirectly using Android sensors. You can measure a magnetic field but you cannot derive a voltage from that without other information, which you cannot measure. For example, I can charge something to a million volts, and have a very large electric field but a very small magnetic field. Equally, I can push 10 amps at 12v through a coil and get a huge magnetic field.

When you say "close to a voltage-producer", I assume that you mean a mains cable or a generator? What you're measuring there is magnetic field, not voltage. As an example, do your test near to a mains cable supplying a large load, say 10A. Now turn off the appliance (not at the wall outlet, at the appliance) and do your test again. You will see a much smaller value but the cable is still at 240v (or 115v or whatever it is in your country). It's just that there is no current flowing, perhaps some small residual leakage, but the whole cable is still at the same potential voltage (for purists reading, I'm ignoring the insignificant reduction in voltage when the device is on....)

Start off by reading "Maxwells' Equations".

PS. There are sensors that can measure voltage remotely, but they don't come in phones!

Cheers



Related Topics



Leave a reply



Submit