From 3265ea308e69e5a1116f36d7dfb639f936bdcf9b Mon Sep 17 00:00:00 2001 From: Luca Warmenhoven Date: Wed, 5 Jun 2024 14:23:29 +0200 Subject: [PATCH 1/3] Added error checking --- .../util/processing/InputProcessor.java | 100 +++++++++++------- 1 file changed, 63 insertions(+), 37 deletions(-) diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java index cd81be9..8b0224f 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java @@ -31,7 +31,10 @@ public class InputProcessor { private float exerciseRepetitionDurationInSeconds = 0.0f; private int repetitionsRemaining = 0; private int exercisesRemaining = 0; - private float exerciseScore = 0.0F; + + private float errorCheckInterval_s; + private int checksPerformed = 0; + private int totalChecks = 0; private final FitnessActivity parentActivity; @@ -97,6 +100,29 @@ public class InputProcessor { this.exercisesRemaining = 1; this.nextExercise(exercise); Pepper.say(STARTING_PHRASES[(int) Math.floor(Math.random() * STARTING_PHRASES.length)]); + + // Error checking thread. + (new Thread(() -> { + while (this.exercisesRemaining > 0) + { + boolean isFaulty = this.isFaultyMovement(); + + if ( isFaulty ) { + this.onInadequateRepetition(); + } else this.onAdequateRepetition(); + + this.checksPerformed++; + + if ( this.checksPerformed >= this.totalChecks ) + acquireExercise(); + + try { + Thread.sleep((long) (this.errorCheckInterval_s * 1000)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + })).start(); } /** @@ -107,11 +133,17 @@ public class InputProcessor { private void nextExercise(Exercise exercise) { if (this.exercisesRemaining-- <= 0) { NavigationManager.navigateToActivity(this.parentActivity, EndScreenActivity.class); + return; } ExerciseManager.TOTAL_REPETITIONS_REQUIRED += ExerciseManager.DEFAULT_EXERCISE_REPETITIONS; ExerciseManager.TOTAL_EXERCISES_PREFORMED++; + this.checksPerformed = 0; + this.totalChecks = ExerciseManager.DEFAULT_EXERCISE_REPETITIONS * 6; + + this.errorCheckInterval_s = this.exerciseRepetitionDurationInSeconds * ExerciseManager.DEFAULT_EXERCISE_REPETITIONS / 6.0f; + this.selfRotationVectorPaths = new ArrayList[2]; this.selfRotationVectorPaths[0] = new ArrayList<>(); this.selfRotationVectorPaths[1] = new ArrayList<>(); @@ -194,6 +226,22 @@ public class InputProcessor { } } + /** + * Function for acquiring the next exercise from the database. + * Upon successful retrieval, it will call the nextExercise method. + */ + private void acquireExercise() + { + this.exercisesRemaining--; + + this.parentActivity.fetchExerciseAsync(this::nextExercise, (nil) -> { + + Log.i("MotionProcessor", "Failed to fetch exercise data."); + NavigationManager.navigateToActivity(this.parentActivity, MainActivity.class); + + }); + } + /** * Function for stopping the listening process * of the motion sensor. This function will stop @@ -288,20 +336,13 @@ public class InputProcessor { } // Do something else with the vector - // TODO: Implement !! - Log.i("MotionProcessor", "Rotation vector: " + rotation.toString() + " from device: " + deviceId); // Whenever the exercise has finished and it's not recording, // attempt to move to the next exercise. // If this fails, navigate back to the main activity. - if (this.hasFinished() && !this.recordingMovement) { - this.parentActivity.fetchExerciseAsync(this::nextExercise, (nil) -> { - Log.i("MotionProcessor", "Failed to fetch exercise data."); - NavigationManager.navigateToActivity(this.parentActivity, MainActivity.class); - }); - } - + if (this.hasFinished() && !this.recordingMovement) + acquireExercise(); } } @@ -318,9 +359,7 @@ public class InputProcessor { JsonArray jsonArray = new JsonArray(); /* * Convert to JSON array in the following format: - * [ - * { "deviceId": number, "data": [ [x, y, z], [x, y, z], ... ] }, - * ] + * [ { "deviceId": number, "data": [ [x, y, z], [x, y, z], ... ] }] */ // Iterate over all devices. In the current instance, it's 2. @@ -346,20 +385,9 @@ public class InputProcessor { } /** - * Function for getting the combined (average) error value of both sensors. - public double getCombinedError() - { - - }*/ - - /** - * + * Function for checking whether the last movement was faulty */ - public boolean isFaultyMovement(int sensorId, float time) { - - // Ensure the sensor ID is within the bounds of the array - if (sensorId < 0 || sensorId >= selfRotationVectorPaths.length) - return false; + public boolean isFaultyMovement() { // Calculate the index of the reference rotation vector // This is done by calculating the closest index of the last received vector @@ -367,26 +395,24 @@ public class InputProcessor { // i = | (t % T) / T * N | int i, referenceIndex; + float distance; for ( i = 0; i < selfRotationVectorPaths.length; i++) { referenceIndex = (int) (Math.round( - ((time % this.exerciseRepetitionDurationInSeconds) / + ((this.secondsPassed % this.exerciseRepetitionDurationInSeconds) / (this.exerciseRepetitionDurationInSeconds)) * this.targetRotationVectorPaths[i].length)) % this.targetRotationVectorPaths[i].length; // If distance is greater than the threshold, return true - if (this.selfRotationVectorPaths[i].get(this.selfRotationVectorPaths[i].size() - 1).distance( - this.targetRotationVectorPaths[i][referenceIndex]) > ExerciseManager.EXERCISE_ERROR_MARGIN) + distance = this.selfRotationVectorPaths[i].get(this.selfRotationVectorPaths[i].size() - 1).distance( + this.targetRotationVectorPaths[i][referenceIndex]); + + if (distance > ExerciseManager.EXERCISE_ERROR_MARGIN) + { + Log.i("MotionProcessor", "Faulty movement detected: " + distance + " > " + ExerciseManager.EXERCISE_ERROR_MARGIN); return true; + } } return false; } - - public float secondsPassed() { - return (float) secondsPassed; - } - - public boolean isRecording() { - return this.recordingMovement; - } } From f4cbf50821f65f2d646a4db9c87bb8dc4066128f Mon Sep 17 00:00:00 2001 From: Luca Warmenhoven Date: Wed, 5 Jun 2024 14:32:06 +0200 Subject: [PATCH 2/3] Added error checking --- .../fitbot/ui/activities/FitnessActivity.java | 7 +++++-- .../fitbot/util/processing/InputProcessor.java | 15 +++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java index 1350a78..a890a4c 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java @@ -134,7 +134,7 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall // the canvas properly. this.fetchExerciseAsync((exercise) -> { // Acquire paths from the exercise and provide them to the motion processor - motionProcessor = new InputProcessor(SENSOR_SAMPLE_RATE, this); + motionProcessor = new InputProcessor(this); motionProcessor.useExercise(exercise); /* TODO: Remove if not needed */ motionProcessor.setRecording(true, 10); @@ -162,8 +162,8 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall if (exercise == null) { runOnUiThread(() -> onFailedFetch.handle(null)); } else { + onSuccessfulFetch.handle(exercise); runOnUiThread(() -> { - onSuccessfulFetch.handle(exercise); exerciseNameTextView.setText(exercise.name); exerciseShortDescriptionTextView.setText(exercise.shortDescription); // exerciseDescriptionTextView.setText(exercise.description); @@ -196,6 +196,9 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall Log.e("FitnessActivity", "VideoView is null. Check your layout XML."); } + // Start checking for user movement once the video has loaded + this.motionProcessor.startCheckingUserMovement(); + return true; } return false; diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java index 8b0224f..5905d11 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java @@ -27,9 +27,7 @@ public class InputProcessor { //private Vector3f[][] selfRotationVectorPaths; // Relative path of the motion data private Vector3f[][] targetRotationVectorPaths; // Target path of the motion data - private final float sampleRate; // The sample rate of the motion sensor private float exerciseRepetitionDurationInSeconds = 0.0f; - private int repetitionsRemaining = 0; private int exercisesRemaining = 0; private float errorCheckInterval_s; @@ -75,11 +73,8 @@ public class InputProcessor { /** * Constructor for the motion processor. - * - * @param inputSampleRate The sample rate of the motion sensor. */ - public InputProcessor(float inputSampleRate, FitnessActivity parentActivity) { - this.sampleRate = inputSampleRate; + public InputProcessor(FitnessActivity parentActivity) { this.parentActivity = parentActivity; } @@ -100,7 +95,15 @@ public class InputProcessor { this.exercisesRemaining = 1; this.nextExercise(exercise); Pepper.say(STARTING_PHRASES[(int) Math.floor(Math.random() * STARTING_PHRASES.length)]); + } + /** + * Function that starts checking for user movement. + * This function will start a thread that will check for user movement + * and compare the last rotation vectors to the target rotation vectors. + */ + public void startCheckingUserMovement() + { // Error checking thread. (new Thread(() -> { while (this.exercisesRemaining > 0) From 2b631bd60092723b70f045c1a5af1ca458c28369 Mon Sep 17 00:00:00 2001 From: Luca Warmenhoven Date: Wed, 5 Jun 2024 14:34:04 +0200 Subject: [PATCH 3/3] Added error checking --- .../com/example/fitbot/util/processing/InputProcessor.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java index 5905d11..7b0bb13 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/InputProcessor.java @@ -24,7 +24,6 @@ import java.util.List; public class InputProcessor { private List[] selfRotationVectorPaths; // Relative path of the motion data - //private Vector3f[][] selfRotationVectorPaths; // Relative path of the motion data private Vector3f[][] targetRotationVectorPaths; // Target path of the motion data private float exerciseRepetitionDurationInSeconds = 0.0f; @@ -151,8 +150,6 @@ public class InputProcessor { this.selfRotationVectorPaths[0] = new ArrayList<>(); this.selfRotationVectorPaths[1] = new ArrayList<>(); - this.repetitionsRemaining = ExerciseManager.DEFAULT_EXERCISE_REPETITIONS; - this.targetRotationVectorPaths = new Vector3f[2][exercise.rightPath.getAngleVectors().length]; this.targetRotationVectorPaths[0] = exercise.leftPath.getAngleVectors(); this.targetRotationVectorPaths[1] = exercise.rightPath.getAngleVectors();