Skip to content

Commit 6ab60c5

Browse files
committed
v3.0
* Add Kotlin
1 parent 1695023 commit 6ab60c5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+303
-310
lines changed

README.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ Android Sensor Filter and Fusion
44
![Alt text](http://kircherelectronics.com.23.38-89-161.groveurl.com/wp-content/uploads/2017/12/FSensor.png "FSensor")
55

66
## Introduction
7-
FSensor (FusionSensor) is an Android library that (hopefully) removes some/most of the complexity of using Androids orientation sensors (Acceleration, Magnetic and Gyroscope). FSensor expands greatly on the "out-of-the-box" sensor implementations provided by Android allowing you to customize sensor filters and fusions for your specific needs, or just add default filters on what Android already provides.
7+
FSensor (FusionSensor) is an Android library that (hopefully) removes some/most of the complexity of using Androids rotation sensors (Acceleration, Magnetic and Gyroscope). FSensor expands greatly on the "out-of-the-box" sensor implementations provided by Android allowing you to customize sensor filters and fusions for your specific needs, or just add default filters on what Android already provides.
88

99
* Provides device/sensor agnostic averaging filters in the of mean, median and low-pass varieties
10-
* Provides IMU sensor fusion backed estimations of device orientation in the complimentary and Kalman varieties
10+
* Provides IMU sensor fusion backed estimations of device rotation in the complimentary and Kalman varieties
1111
* Provides estimations of linear acceleration (linear acceleration = acceleration - gravity) in the averaging filter and sensor fusion varieties
1212

1313
## Get FSensor
@@ -61,17 +61,17 @@ FSensor uses a median filter designed to smooth the data points based on a time
6161

6262
## Orientation Sensor Fusions
6363

64-
FSensor offers two different estimations of rotation using IMU sensor fusions. These filters can be found in the *.filter.fusion* package. One fusion is based on a quaternion backed complimentary filter and the second fusion is based on a quaternion backed Kalman filter. Both fusions use the acceleration sensor, magnetic sensor and gyroscope sensor to provide an estimation the devices orientation relative to world space coordinates.
64+
FSensor offers two different estimations of rotation using IMU sensor fusions. These filters can be found in the *.filter.fusion* package. One fusion is based on a quaternion backed complimentary filter and the second fusion is based on a quaternion backed Kalman filter. Both fusions use the acceleration sensor, magnetic sensor and gyroscope sensor to provide an estimation the devices rotation relative to world space coordinates.
6565

66-
The gyroscope is used to measure the devices orientation. However, the gyroscope tends to drift due to round off errors and other factors. Most gyroscopes work by measuring very small vibrations in the earth's rotation, which means they really do not like external vibrations. Because of drift and external vibrations, the gyroscope has to be compensated with a second estimation of the devices orientation, which comes from the acceleration sensor and magnetic sensor. The acceleration sensor provides the pitch and roll estimations while the magnetic sensor provides the azimuth.
66+
The gyroscope is used to measure the devices rotation. However, the gyroscope tends to drift due to round off errors and other factors. Most gyroscopes work by measuring very small vibrations in the earth's rotation, which means they really do not like external vibrations. Because of drift and external vibrations, the gyroscope has to be compensated with a second estimation of the devices rotation, which comes from the acceleration sensor and magnetic sensor. The acceleration sensor provides the pitch and roll estimations while the magnetic sensor provides the azimuth.
6767

6868
### Quaternions Complimentary Filter
6969

7070
Quaternions offer an angle-axis solution to rotations which do not suffer from many of the singularies, including gimbal lock, that you will find with rotation matrices. Quaternions can also be scaled and applied to a complimentary filter. The quaternion complimentary filter is probably the most elegant, robust and accurate of the filters, although it can also be the most difficult to implement.
7171

7272
The complementary filter is a frequency domain filter. In its strictest sense, the definition of a complementary filter refers to the use of two or more transfer functions, which are mathematical complements of one another. Thus, if the data from one sensor is operated on by G(s), then the data from the other sensor is operated on by I-G(s), and the sum of the transfer functions is I, the identity matrix. In practice, it looks nearly identical to a low-pass filter, but uses two different sets of sensor measurements to produce what can be thought of as a weighted estimation.
7373

74-
A complimentary filter is used to fuse the two orientation estimations (the gyroscope and acceleration/magnetic, respectively) together. It takes the form of gyro[0] = alpha * gyro[0] + (1 - alpha) * accel/magnetic[0]. Alpha is defined as alpha = timeConstant / (timeConstant + dt) where the time constant is the length of signals the filter should act on and dt is the sample period (1/frequency) of the sensor.
74+
A complimentary filter is used to fuse the two rotation estimations (the gyroscope and acceleration/magnetic, respectively) together. It takes the form of gyro[0] = alpha * gyro[0] + (1 - alpha) * accel/magnetic[0]. Alpha is defined as alpha = timeConstant / (timeConstant + dt) where the time constant is the length of signals the filter should act on and dt is the sample period (1/frequency) of the sensor.
7575

7676
### Quaternion Kalman Filter
7777

@@ -93,7 +93,7 @@ The most simple linear acceleration filter is based on a low-pass filter. It has
9393

9494
### IMU Sensor Fusion Linear Acceleration
9595

96-
Calculating the gravity components of a normalized orientation is trivial, so FSensor can use the IMU orientation fusions to provide an estimation of linear acceleration that is far more customizable than what Android provides alone.
96+
Calculating the gravity components of a normalized rotation is trivial, so FSensor can use the IMU rotation fusions to provide an estimation of linear acceleration that is far more customizable than what Android provides alone.
9797

9898
## Sensor Offset Calibration
9999

build.gradle

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ buildscript {
1010

1111
dependencies {
1212
classpath 'com.android.tools.build:gradle:8.1.4'
13-
14-
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22"
13+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1514
}
1615
}
1716

documentation/USAGE.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public void onSensorChanged(SensorEvent event) {
120120
System.arraycopy(event.values, 0, acceleration, 0, event.values.length);
121121
orientationFusion.setAcceleration(acceleration);
122122
123-
// Apply the orientation to the raw acceleration to estimate linear acceleration
123+
// Apply the rotation to the raw acceleration to estimate linear acceleration
124124
linearAcceleration = linearAccelerationFilter.filter(acceleration)
125125
} else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
126126
// Android reuses events, so you probably want a copy
@@ -172,7 +172,7 @@ public void onSensorChanged(SensorEvent event) {
172172
System.arraycopy(event.values, 0, magnetic, 0, event.values.length);
173173
orientationFusion.setMagneticField(this.magnetic);
174174
175-
// Compensate for tilt. Note: This sensor orientation assumes portrait mode with the device laying flat and the compass // pointing out of the top of the device. Your milage may vary.
175+
// Compensate for tilt. Note: This sensor rotation assumes portrait mode with the device laying flat and the compass // pointing out of the top of the device. Your milage may vary.
176176
float[] output = TiltCompensationUtil.compensateTilt(new float[]{magnetic[0], -magnetic[1], magnetic[2]}, new float[]{fusedOrientation[1], fusedOrientation[2], 0});
177177
// Reorient to the device
178178
output[1] = -output[1];
@@ -264,7 +264,7 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
264264
System.arraycopy(event.values, 0, acceleration, 0, event.values.length);
265265
orientationFusion.setAcceleration(acceleration);
266266
267-
// Apply the orientation to the raw acceleration to estimate linear acceleration
267+
// Apply the rotation to the raw acceleration to estimate linear acceleration
268268
linearAcceleration = linearAccelerationFilter.filter(acceleration);
269269
} else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
270270
// Android reuses events, so you probably want a copy
@@ -350,7 +350,7 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
350350
System.arraycopy(event.values, 0, acceleration, 0, event.values.length);
351351
orientationFusion.setAcceleration(acceleration);
352352
353-
// Apply the orientation to the raw acceleration to estimate linear acceleration
353+
// Apply the rotation to the raw acceleration to estimate linear acceleration
354354
linearAcceleration = linearAccelerationFilter.filter(acceleration);
355355
} else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
356356
// Android reuses events, so you probably want a copy

fsensor/src/main/java/com/tracqi/fsensor/filter/MeanFilter.java

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.tracqi.fsensor.filter;
22

3+
import android.util.Log;
4+
35
import java.util.ArrayDeque;
46
import java.util.Arrays;
57

@@ -108,9 +110,4 @@ private float[] getMean(ArrayDeque<float[]> data) {
108110

109111
return mean;
110112
}
111-
112-
public void reset() {
113-
super.reset();
114-
this.values.clear();
115-
}
116113
}

fsensor/src/main/java/com/tracqi/fsensor/filter/MedianFilter.java

-5
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,4 @@ private float[] getMedian(ArrayDeque<float[]> data) {
117117

118118
return mean;
119119
}
120-
121-
public void reset() {
122-
super.reset();
123-
this.values.clear();
124-
}
125120
}

fsensor/src/main/java/com/tracqi/fsensor/filter/SensorFilter.java

-5
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,6 @@ public SensorFilter(float timeConstant) {
3737
this.timeConstant = timeConstant;
3838
}
3939

40-
public void reset() {
41-
startTime = 0;
42-
count = 0;
43-
}
44-
4540
public void setTimeConstant(float timeConstant) {
4641
this.timeConstant = timeConstant;
4742
}

fsensor/src/main/java/com/tracqi/fsensor/math/angle/AngleUtils.java fsensor/src/main/java/com/tracqi/fsensor/math/angle/Angles.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
* limitations under the License.
1919
*/
2020

21-
public class AngleUtils {
22-
private static final String TAG = AngleUtils.class.getSimpleName();
21+
public class Angles {
22+
private static final String TAG = Angles.class.getSimpleName();
2323

2424
/**
2525
* https://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm

fsensor/src/main/java/com/tracqi/fsensor/math/gravity/GravityUtil.java fsensor/src/main/java/com/tracqi/fsensor/math/gravity/Gravity.java

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package com.tracqi.fsensor.math.gravity;
22

33
import android.hardware.SensorManager;
4-
import android.util.Log;
5-
6-
import java.util.Arrays;
74

85
/*
96
* Copyright 2024, Tracqi Technology, LLC
@@ -24,7 +21,7 @@
2421
/**
2522
* Created by kaleb on 7/6/17.
2623
*/
27-
public class GravityUtil {
24+
public class Gravity {
2825
/**
2926
* Assumes a positive, counter-clockwise, right-handed rotation
3027
* orientation[0] = pitch, rotation around the X axis.

fsensor/src/main/java/com/tracqi/fsensor/math/magnetic/AzimuthUtil.java fsensor/src/main/java/com/tracqi/fsensor/math/magnetic/Azimuth.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
* A helper class for
2121
* Created by kaleb on 3/18/18.
2222
*/
23-
public class AzimuthUtil {
23+
public class Azimuth {
2424

2525
/**
2626
* Get the azimuth from a magnetic sensor.

fsensor/src/main/java/com/tracqi/fsensor/math/magnetic/declination/DeclinationUtil.java fsensor/src/main/java/com/tracqi/fsensor/math/magnetic/declination/Declination.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@
2424
*
2525
* Created by kaleb on 3/18/18.
2626
*/
27-
public class DeclinationUtil {
27+
public class Declination {
2828

2929
private GeomagneticField geoMagField;
3030

3131
/**
3232
* Default constructor.
3333
* @param location The location to use for the compensation.
3434
*/
35-
public DeclinationUtil(Location location) {
35+
public Declination(Location location) {
3636
setGeomagneticField(location);
3737
}
3838

fsensor/src/main/java/com/tracqi/fsensor/math/magnetic/tilt/TiltCompensationUtil.java fsensor/src/main/java/com/tracqi/fsensor/math/magnetic/tilt/TiltCompensation.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
* Providers helpers to compensate for the tilt of a magnetic sensor.
2121
* Created by kaleb on 3/18/18.
2222
*/
23-
public class TiltCompensationUtil {
23+
public class TiltCompensation {
2424

2525
/**
2626
* Get the rotation vector based on the tilt of the acceleration sensor.

fsensor/src/main/java/com/tracqi/fsensor/math/rotation/RotationUtil.java fsensor/src/main/java/com/tracqi/fsensor/math/rotation/Rotation.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import android.hardware.SensorManager;
44
import android.renderscript.Matrix3f;
5-
import android.util.Log;
65

76
import org.apache.commons.math3.complex.Quaternion;
87

@@ -28,7 +27,7 @@
2827
* Created by kaleb on 4/1/18.
2928
*/
3029

31-
public class RotationUtil {
30+
public class Rotation {
3231

3332
/**
3433
* Calculates a rotation vector from the gyroscope angular speed values.

fsensor/src/main/java/com/tracqi/fsensor/orientation/Orientation.java fsensor/src/main/java/com/tracqi/fsensor/rotation/Rotation.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.tracqi.fsensor.orientation;
1+
package com.tracqi.fsensor.rotation;
22

33

44
/*
@@ -17,7 +17,7 @@
1717
* limitations under the License.
1818
*/
1919

20-
public interface Orientation {
20+
public interface Rotation {
2121
void start(int sensorDelay);
2222
void stop();
2323
float[] getOrientation();

fsensor/src/main/java/com/tracqi/fsensor/orientation/fusion/FusedOrientation.java fsensor/src/main/java/com/tracqi/fsensor/rotation/fusion/FusedRotation.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
package com.tracqi.fsensor.orientation.fusion;
1+
package com.tracqi.fsensor.rotation.fusion;
22

3-
import com.tracqi.fsensor.orientation.Orientation;
3+
import com.tracqi.fsensor.rotation.Rotation;
44

55
import org.apache.commons.math3.complex.Quaternion;
66

@@ -25,7 +25,7 @@
2525
* Created by kaleb on 7/6/17.
2626
*/
2727

28-
public abstract class FusedOrientation implements Orientation {
28+
public abstract class FusedRotation implements Rotation {
2929

3030
protected static final float EPSILON = 0.000000001f;
3131
// Nano-second to second conversion

fsensor/src/main/java/com/tracqi/fsensor/orientation/fusion/complementary/ComplimentaryOrientation.java fsensor/src/main/java/com/tracqi/fsensor/rotation/fusion/complementary/ComplimentaryRotation.java

+15-18
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
1-
package com.tracqi.fsensor.orientation.fusion.complementary;
1+
package com.tracqi.fsensor.rotation.fusion.complementary;
22

33
import android.hardware.Sensor;
44
import android.hardware.SensorEvent;
55
import android.hardware.SensorEventListener;
66
import android.hardware.SensorManager;
7-
import android.util.Log;
87

9-
import com.tracqi.fsensor.math.gravity.GravityUtil;
10-
import com.tracqi.fsensor.orientation.fusion.FusedOrientation;
11-
import com.tracqi.fsensor.math.angle.AngleUtils;
12-
import com.tracqi.fsensor.math.rotation.RotationUtil;
8+
import com.tracqi.fsensor.math.gravity.Gravity;
9+
import com.tracqi.fsensor.rotation.fusion.FusedRotation;
10+
import com.tracqi.fsensor.math.angle.Angles;
11+
import com.tracqi.fsensor.math.rotation.Rotation;
1312

1413
import org.apache.commons.math3.complex.Quaternion;
1514

16-
import java.util.Arrays;
17-
1815
/*
1916
* Copyright 2024, Tracqi Technology, LLC
2017
*
@@ -84,9 +81,9 @@
8481
*
8582
* @author Kaleb
8683
*/
87-
public class ComplimentaryOrientation extends FusedOrientation {
84+
public class ComplimentaryRotation extends FusedRotation {
8885

89-
private static final String TAG = ComplimentaryOrientation.class.getSimpleName();
86+
private static final String TAG = ComplimentaryRotation.class.getSimpleName();
9087

9188
private static final float DEFAULT_TIME_CONSTANT = 0.18f;
9289

@@ -111,11 +108,11 @@ public class ComplimentaryOrientation extends FusedOrientation {
111108
/**
112109
* Initialize a singleton instance.
113110
*/
114-
public ComplimentaryOrientation(SensorManager sensorManager) {
111+
public ComplimentaryRotation(SensorManager sensorManager) {
115112
this.sensorManager = sensorManager;
116113
}
117114

118-
public ComplimentaryOrientation(SensorManager sensorManager, float timeConstant) {
115+
public ComplimentaryRotation(SensorManager sensorManager, float timeConstant) {
119116
this.sensorManager = sensorManager;
120117
this.timeConstant = timeConstant;
121118
}
@@ -177,10 +174,10 @@ private void calculateFusedOrientation(float[] gyroscope, long timestamp, float[
177174
float oneMinusAlpha = (1.0f - alpha);
178175

179176
// Get last known orientation
180-
float[] orientation = AngleUtils.getAngles(rotationVector.getQ0(), rotationVector.getQ1(), rotationVector.getQ2(), rotationVector.getQ3());
177+
float[] orientation = Angles.getAngles(rotationVector.getQ0(), rotationVector.getQ1(), rotationVector.getQ2(), rotationVector.getQ3());
181178

182179
// Calculate the gravity vector from the orientation
183-
float[] gravity = GravityUtil.getGravityFromOrientation(orientation);
180+
float[] gravity = Gravity.getGravityFromOrientation(orientation);
184181

185182
for(int i = 0; i < gravity.length; i++) {
186183
// Apply acceleration sensor
@@ -189,11 +186,11 @@ private void calculateFusedOrientation(float[] gyroscope, long timestamp, float[
189186
}
190187

191188
// Get orientation from acceleration and magnetic
192-
Quaternion rotationVectorAccelerationMagnetic = RotationUtil.getOrientationVector(gravity, magnetic);
189+
Quaternion rotationVectorAccelerationMagnetic = Rotation.getOrientationVector(gravity, magnetic);
193190

194191
if (rotationVectorAccelerationMagnetic != null) {
195192

196-
rotationVector = RotationUtil.integrateGyroscopeRotation(rotationVector, gyroscope, dT, EPSILON);
193+
rotationVector = Rotation.integrateGyroscopeRotation(rotationVector, gyroscope, dT, EPSILON);
197194

198195
// Apply the complementary fusedOrientation. // We multiply each rotation by their
199196
// coefficients (scalar matrices)...
@@ -206,7 +203,7 @@ private void calculateFusedOrientation(float[] gyroscope, long timestamp, float[
206203
// output[0] = alpha * output[0] + (1 - alpha) * input[0];
207204
Quaternion result = scaledRotationVectorGyroscope.add(scaledRotationVectorAccelerationMagnetic);
208205

209-
float[] angles = AngleUtils.getAngles(result.getQ0(), result.getQ1(), result.getQ2(), result.getQ3());
206+
float[] angles = Angles.getAngles(result.getQ0(), result.getQ1(), result.getQ2(), result.getQ3());
210207
System.arraycopy(angles, 0, this.output, 0, angles.length);
211208
}
212209
}
@@ -242,7 +239,7 @@ public void onSensorChanged(SensorEvent event) {
242239
hasMagnetic = false;
243240

244241
if (!isBaseOrientationSet()) {
245-
setBaseOrientation(RotationUtil.getOrientationVector(acceleration, magnetic));
242+
setBaseOrientation(Rotation.getOrientationVector(acceleration, magnetic));
246243
} else {
247244
calculateFusedOrientation(rotation, rotationTimestamp, acceleration, magnetic);
248245
}

0 commit comments

Comments
 (0)