Added error calculations for GesturePath :: MotionProcessor.averageError, MotionProcessor.getErrors, GesturePath::getError
This commit is contained in:
@@ -7,7 +7,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.example.fitbot"
|
||||
minSdk 23
|
||||
minSdk 29
|
||||
targetSdk 29
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package com.example.fitbot;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
@@ -9,9 +8,10 @@ import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.app.ActionBarDrawerToggle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.example.fitbot.ui.SportMenuActivity;
|
||||
import com.example.fitbot.util.processing.GesturePath;
|
||||
import com.example.fitbot.util.processing.MotionProcessor;
|
||||
import com.example.fitbot.util.processing.Vector3;
|
||||
|
||||
public class MainScreen extends AppCompatActivity {
|
||||
|
||||
|
@@ -1,28 +1,101 @@
|
||||
package com.example.fitbot.util.processing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GesturePath {
|
||||
|
||||
public Vector3[] vectors;
|
||||
|
||||
public GesturePath(Vector3[] vectors)
|
||||
{
|
||||
public GesturePath(Vector3[] vectors) {
|
||||
this.vectors = vectors;
|
||||
}
|
||||
|
||||
public Vector3 getError(Vector3 compare) {
|
||||
Vector3 error = new Vector3(0, 0, 0);
|
||||
double distance, previous = Double.MAX_VALUE;
|
||||
/**
|
||||
* Get the error between a line segment and the reference point.
|
||||
*
|
||||
* @param linePointA The first point of the line.
|
||||
* @param linePointB The second point of the line.
|
||||
* @param referencePoint The reference point to calculate the error of.
|
||||
* @return The error offset between the line and the reference point.
|
||||
*/
|
||||
public static double getError(Vector3 linePointA, Vector3 linePointB, Vector3 referencePoint) {
|
||||
double lineDistance = linePointA.distanceSq(linePointB);
|
||||
if (lineDistance == 0)
|
||||
return referencePoint.distanceSq(linePointA);
|
||||
|
||||
// Calculate the minimum distance between the vectors.
|
||||
// This is used to determine the error.
|
||||
for (int i = 0; i < vectors.length; i++) {
|
||||
distance = vectors[i].distance(compare);
|
||||
if ( distance < previous ) {
|
||||
error = vectors[i];
|
||||
previous = distance;
|
||||
double t = ((referencePoint.x - linePointA.x) * (linePointB.x - linePointA.x) +
|
||||
(referencePoint.y - linePointA.y) * (linePointB.y - linePointA.y) +
|
||||
(referencePoint.z - linePointA.z) * (linePointB.z - linePointA.z)) / lineDistance;
|
||||
|
||||
t = Math.max(0, Math.min(1, t));
|
||||
|
||||
return referencePoint.distanceSq(new Vector3(
|
||||
linePointA.x + t * (linePointB.x - linePointA.x),
|
||||
linePointA.y + t * (linePointB.y - linePointA.y),
|
||||
linePointA.z + t * (linePointB.z - linePointA.z))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error between an arbitrary path segment and the reference point.
|
||||
*
|
||||
* @param referencePoint The reference point to calculate the error of.
|
||||
* @return The error offset between the path and the reference point.
|
||||
*/
|
||||
public double getError(Vector3 referencePoint) {
|
||||
double distance = Double.MAX_VALUE;
|
||||
for (int i = 0; i < vectors.length - 1; i++) {
|
||||
double error = getError(vectors[i], vectors[i + 1], referencePoint);
|
||||
if (error < distance) {
|
||||
distance = error;
|
||||
}
|
||||
}
|
||||
return error;
|
||||
return distance;
|
||||
}
|
||||
|
||||
// Builder class for the GesturePath object.
|
||||
public static class Builder {
|
||||
|
||||
// List of vectors to add to the GesturePath object.
|
||||
private final List<Vector3> vectors;
|
||||
|
||||
/**
|
||||
* Constructor for the Builder object.
|
||||
*
|
||||
* @param vectors The list of vectors to add.
|
||||
*/
|
||||
public Builder(List<Vector3> vectors) {
|
||||
this.vectors = vectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor for the Builder object.
|
||||
*/
|
||||
public Builder() {
|
||||
this.vectors = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a vector to the GesturePath object.
|
||||
*
|
||||
* @param vector The vector to add.
|
||||
* @return The Builder object.
|
||||
*/
|
||||
public Builder addVector(Vector3 vector) {
|
||||
vectors.add(vector);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the GesturePath object.
|
||||
*
|
||||
* @return The GesturePath object.
|
||||
*/
|
||||
public GesturePath build() {
|
||||
return new GesturePath(vectors.toArray(new Vector3[0]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -60,22 +60,4 @@ public class MotionData {
|
||||
Float.parseFloat(parts[5])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for representing parsed motion data.
|
||||
* This data is parsed in the `MotionProcessor` class.
|
||||
*/
|
||||
public static class ParsedVector {
|
||||
public Vector3 relativePosition;
|
||||
public Vector3 relativeError;
|
||||
public ParsedVector(Vector3 relativePosition, Vector3 relativeError) {
|
||||
this.relativePosition = relativePosition;
|
||||
this.relativeError = relativeError;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "position[" + relativePosition.toString() + "] error[" + relativeError.toString() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -9,15 +9,14 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MotionProcessor {
|
||||
|
||||
private static final int MAX_PATH_LENGTH = 100;
|
||||
public static final String DELIMITER = ";";
|
||||
|
||||
private final List<MotionData> motionData = new ArrayList<>();
|
||||
private final List<MotionData.ParsedVector> parsedData = new ArrayList<>();
|
||||
private final Vector3[] relativePath = new Vector3[MAX_PATH_LENGTH];
|
||||
private final List<Vector3> relativePath = new ArrayList<>(); // Relative path of the motion data
|
||||
private Vector3 ZERO = new Vector3(0, 0, 0);
|
||||
private double sampleRate = 1.0D; // samples/second
|
||||
|
||||
@@ -76,33 +75,91 @@ public class MotionProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for calculating the path of the device.
|
||||
* This only works when the ZERO point has been provided.
|
||||
* Function for adding motion data to the processor.
|
||||
*
|
||||
* @param data The motion data to add.
|
||||
*/
|
||||
public void calculatePath(GesturePath path) {
|
||||
public void addMotionData(MotionData data) {
|
||||
this.motionData.add(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for calculating the 'absolute' path of the motion data.
|
||||
* This converts the relative acceleration and rotation vectors and converts
|
||||
* them to a position that is relative to the ZERO point.
|
||||
*/
|
||||
public void calculatePath() {
|
||||
|
||||
relativePath.clear(); // Clear previous path
|
||||
|
||||
int offset = Math.max(0, motionData.size() - MAX_PATH_LENGTH);
|
||||
Vector3 previous = ZERO.copy();
|
||||
|
||||
for (int i = offset; i < motionData.size(); i++) {
|
||||
for (int i = 0; i < motionData.size(); i++) {
|
||||
MotionData data = motionData.get(i);
|
||||
|
||||
Vector3 relativePosition = data.acceleration
|
||||
.rotate(data.rotation.negate()) // Rotate acceleration vector with it's negated angle to get the acceleration relative to the vector of gravity
|
||||
.multiply(sampleRate * sampleRate / 6.0D) // Apply double integration to get the relative position
|
||||
.add(previous); // Add the previous position to get the new position
|
||||
Vector3 relativeVector = getRelativeVector(data)
|
||||
.add(previous);
|
||||
|
||||
Vector3 relativeError = path.getError(relativePosition);
|
||||
|
||||
previous = relativePosition;
|
||||
|
||||
// Get relative vector from the ZERO point
|
||||
parsedData.add(new MotionData.ParsedVector(relativePosition, relativeError));
|
||||
relativePath.add(relativeVector);
|
||||
previous = relativeVector;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for getting the relative vector of the motion data.
|
||||
* This function will calculate the relative position of the motion data
|
||||
* based on its acceleration and rotation vectors. This has to be done since
|
||||
* the acceleration vector is relative to its own rotation vector.
|
||||
*
|
||||
* @param motionData The motion data to calculate the relative vector for.
|
||||
* @return The relative vector of the motion data.
|
||||
*/
|
||||
public Vector3 getRelativeVector(MotionData motionData) {
|
||||
|
||||
// Rotate the acceleration vector back by the rotation vector to make it
|
||||
// perpendicular to the gravity vector, then apply double integration to get the relative position.
|
||||
// s = 1/2 * a * t^2
|
||||
return motionData.acceleration
|
||||
.rotate(motionData.rotation.negate())
|
||||
.multiply(sampleRate * sampleRate / 6.0D);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for getting the error offsets of the provided path and the
|
||||
* received motion data.
|
||||
*
|
||||
* @param referencePath The reference path to compare the motion data to.
|
||||
* @return A list of error offsets of the motion data compared to the reference path.
|
||||
*/
|
||||
public List<Double> getErrors(GesturePath referencePath) {
|
||||
|
||||
// If the relative path is empty, calculate it.
|
||||
if ( relativePath.isEmpty())
|
||||
calculatePath();
|
||||
|
||||
// Return the errors of the relative path compared to the reference path.
|
||||
return relativePath
|
||||
.stream()
|
||||
.map(referencePath::getError)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for calculating the average error of the motion data
|
||||
* compared to the reference path.
|
||||
*
|
||||
* @param referencePath The reference path to compare the motion data to.
|
||||
* @return The average error of the motion data compared to the reference path.
|
||||
*/
|
||||
public double averageError(GesturePath referencePath) {
|
||||
return getErrors(referencePath)
|
||||
.stream()
|
||||
.mapToDouble(Double::doubleValue)
|
||||
.average()
|
||||
.orElse(0.0D);
|
||||
}
|
||||
|
||||
public void printData() {
|
||||
for (MotionData.ParsedVector vector : parsedData) {
|
||||
Log.i("MotionProcessor", vector.toString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -175,6 +175,16 @@ public class Vector3 {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the squared distance between this vector and another vector.
|
||||
*
|
||||
* @param compare The other vector.
|
||||
* @return The squared distance between the two vectors.
|
||||
*/
|
||||
public double distanceSq(Vector3 compare) {
|
||||
return Math.pow(compare.x - x, 2) + Math.pow(compare.y - y, 2) + Math.pow(compare.z - z, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the distance between this vector and another vector.
|
||||
*
|
||||
@@ -182,7 +192,7 @@ public class Vector3 {
|
||||
* @return The distance between the two vectors.
|
||||
*/
|
||||
public double distance(Vector3 compare) {
|
||||
return Math.sqrt(Math.pow(compare.x - x, 2) + Math.pow(compare.y - y, 2) + Math.pow(compare.z - z, 2));
|
||||
return Math.sqrt(distanceSq(compare));
|
||||
}
|
||||
|
||||
public Vector3 map(VectorMapFunction function) {
|
||||
|
Reference in New Issue
Block a user