Added error calculations for GesturePath :: MotionProcessor.averageError, MotionProcessor.getErrors, GesturePath::getError
This commit is contained in:
@@ -7,7 +7,7 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.example.fitbot"
|
applicationId "com.example.fitbot"
|
||||||
minSdk 23
|
minSdk 29
|
||||||
targetSdk 29
|
targetSdk 29
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package com.example.fitbot;
|
package com.example.fitbot;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.design.widget.NavigationView;
|
import android.support.design.widget.NavigationView;
|
||||||
import android.support.v4.view.GravityCompat;
|
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.ActionBarDrawerToggle;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
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 {
|
public class MainScreen extends AppCompatActivity {
|
||||||
|
|
||||||
|
@@ -1,28 +1,101 @@
|
|||||||
package com.example.fitbot.util.processing;
|
package com.example.fitbot.util.processing;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class GesturePath {
|
public class GesturePath {
|
||||||
|
|
||||||
public Vector3[] vectors;
|
public Vector3[] vectors;
|
||||||
|
|
||||||
public GesturePath(Vector3[] vectors)
|
public GesturePath(Vector3[] vectors) {
|
||||||
{
|
|
||||||
this.vectors = vectors;
|
this.vectors = vectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector3 getError(Vector3 compare) {
|
/**
|
||||||
Vector3 error = new Vector3(0, 0, 0);
|
* Get the error between a line segment and the reference point.
|
||||||
double distance, previous = Double.MAX_VALUE;
|
*
|
||||||
|
* @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.
|
double t = ((referencePoint.x - linePointA.x) * (linePointB.x - linePointA.x) +
|
||||||
// This is used to determine the error.
|
(referencePoint.y - linePointA.y) * (linePointB.y - linePointA.y) +
|
||||||
for (int i = 0; i < vectors.length; i++) {
|
(referencePoint.z - linePointA.z) * (linePointB.z - linePointA.z)) / lineDistance;
|
||||||
distance = vectors[i].distance(compare);
|
|
||||||
if ( distance < previous ) {
|
t = Math.max(0, Math.min(1, t));
|
||||||
error = vectors[i];
|
|
||||||
previous = distance;
|
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])
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class MotionProcessor {
|
public class MotionProcessor {
|
||||||
|
|
||||||
private static final int MAX_PATH_LENGTH = 100;
|
|
||||||
public static final String DELIMITER = ";";
|
public static final String DELIMITER = ";";
|
||||||
|
|
||||||
private final List<MotionData> motionData = new ArrayList<>();
|
private final List<MotionData> motionData = new ArrayList<>();
|
||||||
private final List<MotionData.ParsedVector> parsedData = new ArrayList<>();
|
private final List<Vector3> relativePath = new ArrayList<>(); // Relative path of the motion data
|
||||||
private final Vector3[] relativePath = new Vector3[MAX_PATH_LENGTH];
|
|
||||||
private Vector3 ZERO = new Vector3(0, 0, 0);
|
private Vector3 ZERO = new Vector3(0, 0, 0);
|
||||||
private double sampleRate = 1.0D; // samples/second
|
private double sampleRate = 1.0D; // samples/second
|
||||||
|
|
||||||
@@ -76,33 +75,91 @@ public class MotionProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function for calculating the path of the device.
|
* Function for adding motion data to the processor.
|
||||||
* This only works when the ZERO point has been provided.
|
*
|
||||||
|
* @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();
|
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);
|
MotionData data = motionData.get(i);
|
||||||
|
|
||||||
Vector3 relativePosition = data.acceleration
|
Vector3 relativeVector = getRelativeVector(data)
|
||||||
.rotate(data.rotation.negate()) // Rotate acceleration vector with it's negated angle to get the acceleration relative to the vector of gravity
|
.add(previous);
|
||||||
.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 relativeError = path.getError(relativePosition);
|
relativePath.add(relativeVector);
|
||||||
|
previous = relativeVector;
|
||||||
previous = relativePosition;
|
|
||||||
|
|
||||||
// Get relative vector from the ZERO point
|
|
||||||
parsedData.add(new MotionData.ParsedVector(relativePosition, relativeError));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
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.
|
* Get the distance between this vector and another vector.
|
||||||
*
|
*
|
||||||
@@ -182,7 +192,7 @@ public class Vector3 {
|
|||||||
* @return The distance between the two vectors.
|
* @return The distance between the two vectors.
|
||||||
*/
|
*/
|
||||||
public double distance(Vector3 compare) {
|
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) {
|
public Vector3 map(VectorMapFunction function) {
|
||||||
|
Reference in New Issue
Block a user