From 45d65f7ba5a56d39718b91caa9cfea9685185b59 Mon Sep 17 00:00:00 2001 From: j5155 <54331556+j5155@users.noreply.github.com> Date: Sat, 19 Oct 2024 13:37:40 -0800 Subject: [PATCH 01/10] implement IMU with velocity low pass filter --- .../canandgyro/ReduxAnalogCanandgyro.java | 184 ++++++++++++++---- 1 file changed, 150 insertions(+), 34 deletions(-) diff --git a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java index 3d088b7..4b2a09a 100644 --- a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java +++ b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java @@ -25,21 +25,14 @@ */ package com.reduxrobotics.ftc.sensors.canandgyro; -import com.qualcomm.robotcore.hardware.AnalogInputController; -import com.qualcomm.robotcore.hardware.AnalogSensor; -import com.qualcomm.robotcore.hardware.ControlSystem; -import com.qualcomm.robotcore.hardware.HardwareDevice; -import com.qualcomm.robotcore.hardware.OrientationSensor; +import com.qualcomm.robotcore.hardware.*; import com.qualcomm.robotcore.hardware.configuration.annotations.AnalogSensorType; import com.qualcomm.robotcore.hardware.configuration.annotations.DeviceProperties; +import com.qualcomm.robotcore.util.ElapsedTime; import org.firstinspires.ftc.robotcore.external.ExportClassToBlocks; import org.firstinspires.ftc.robotcore.external.ExportToBlocks; -import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit; -import org.firstinspires.ftc.robotcore.external.navigation.AxesOrder; -import org.firstinspires.ftc.robotcore.external.navigation.AxesReference; -import org.firstinspires.ftc.robotcore.external.navigation.Axis; -import org.firstinspires.ftc.robotcore.external.navigation.Orientation; +import org.firstinspires.ftc.robotcore.external.navigation.*; import java.util.HashSet; import java.util.Set; @@ -57,7 +50,7 @@ compatibleControlSystems = {ControlSystem.REV_HUB}, description = "a Redux Robotics Canandgyro connected via the analog port" ) -public class ReduxAnalogCanandgyro implements AnalogSensor, OrientationSensor, HardwareDevice { +public class ReduxAnalogCanandgyro implements AnalogSensor, OrientationSensor, HardwareDevice, IMU { private AnalogInputController controller = null; private int channel = -1; private double zeroOffset = 0; @@ -65,6 +58,15 @@ public class ReduxAnalogCanandgyro implements AnalogSensor, OrientationSensor, H // The canandgyro itself will only output a maximum of MAX_VOLTAGE volts. private static final double MAX_VOLTAGE = 3.3; + private double oldYaw = 0; // radians + private final ElapsedTime oldYawAge = new ElapsedTime(); + private double filteredYawVel = 0; // radians/sec + private final ElapsedTime filteredYawVelAge = new ElapsedTime(); + + private static final double lowpassSmoothing = 10.0; // https://phrogz.net/js/framerate-independent-low-pass-filter.html + + + /** * Constructor * @@ -90,13 +92,9 @@ public ReduxAnalogCanandgyro(AnalogInputController controller, int channel) { parameterLabels = { "angleUnit" } ) public double getYaw(AngleUnit unit) { - switch (unit) { - case DEGREES: - return AngleUnit.normalizeDegrees(readZeroedVoltage() / MAX_VOLTAGE * 360.0); - case RADIANS: - return AngleUnit.normalizeRadians(readZeroedVoltage() / MAX_VOLTAGE * (Math.PI * 2)); - } - return 0; + double yaw = AngleUnit.normalizeRadians(readZeroedVoltage() / MAX_VOLTAGE * (Math.PI * 2)); + updateLowpassFilter(yaw); + return unit.fromRadians(yaw); } /** @@ -118,12 +116,10 @@ public double getYaw(AngleUnit unit) { parameterLabels = { "newAngle", "angleUnit" } ) public void setYaw(double newAngle, AngleUnit unit) { - if (unit == AngleUnit.RADIANS) { - newAngle = AngleUnit.RADIANS.toDegrees(newAngle); - } else { - newAngle = AngleUnit.normalizeDegrees(newAngle); - } - newAngle *= MAX_VOLTAGE / 360.0; + newAngle = unit.toRadians(newAngle); + resetLowpassFilter(newAngle); + + newAngle *= MAX_VOLTAGE / (Math.PI * 2); zeroOffset = readRawVoltage() - newAngle; } @@ -171,11 +167,7 @@ public void zero() { parameterLabels = { "angleUnit" } ) public double getZeroOffset(AngleUnit unit) { - if (unit == AngleUnit.DEGREES) { - return AngleUnit.normalizeDegrees(zeroOffset / MAX_VOLTAGE * 360); - } else { - return AngleUnit.normalizeRadians(zeroOffset / MAX_VOLTAGE * 2 * Math.PI); - } + return unit.fromRadians(zeroOffset / MAX_VOLTAGE * 2 * Math.PI); } /** @@ -199,11 +191,7 @@ public double getZeroOffset(AngleUnit unit) { parameterLabels = { "newOffset", "angleUnit" } ) public void setZeroOffset(double newOffset, AngleUnit unit) { - if (unit == AngleUnit.DEGREES) { - newOffset = AngleUnit.normalizeDegrees(newOffset) / 360; - } else { - newOffset = AngleUnit.normalizeRadians(newOffset) / (2 * Math.PI); - } + newOffset = unit.toRadians(newOffset) / (2 * Math.PI); zeroOffset = newOffset * MAX_VOLTAGE; } @@ -287,4 +275,132 @@ public void resetDeviceConfigurationForOpMode() { @Override public void close() { } + + /** + * Initializes the IMU with non-default settings. + * + * @param parameters + * @return Whether initialization succeeded. + */ + @Override + public boolean initialize(Parameters parameters) { + return true; + } + + /** + * Resets the robot's yaw angle to 0. After calling this method, the reported orientation will + * be relative to the robot's position when this method was called, as if the robot was perfectly + * level right then. That is to say, the pitch and yaw will be ignored when this method is + * called. + *

+ * Unlike yaw, pitch and roll are always relative to gravity, and never need to be reset. + */ + @Override + public void resetYaw() { + zero(); + } + + /** + * @return A {@link YawPitchRollAngles} object representing the current orientation of the robot + * in the Robot Coordinate System, relative to the robot's position the last time that + * {@link #resetYaw()} was called, as if the robot was perfectly level at that time. + */ + @Override + public YawPitchRollAngles getRobotYawPitchRollAngles() { + // Blindly using System.nanoTime() here isn't ideal, but no better option, I think + return new YawPitchRollAngles(AngleUnit.RADIANS,getYaw(AngleUnit.RADIANS),0.0,0.0,System.nanoTime()); + } + + /** + * @param reference + * @param order + * @param angleUnit + * @return An {@link Orientation} object representing the current orientation of the robot + * in the Robot Coordinate System, relative to the robot's position the last time + * that {@link #resetYaw()} was called, as if the robot was perfectly level at that time. + *

+ * The {@link Orientation} class provides many ways to represent the robot's orientation, + * which is helpful for advanced use cases. Most teams should use {@link #getRobotYawPitchRollAngles()}. + */ + @Override + public Orientation getRobotOrientation(AxesReference reference, AxesOrder order, AngleUnit angleUnit) { + // Blindly using System.nanoTime() here isn't ideal, but no better option, I think + return new Orientation(AxesReference.EXTRINSIC, AxesOrder.ZYX, AngleUnit.RADIANS,(float) getYaw(AngleUnit.RADIANS), 0f , 0f, System.nanoTime()) + .toAxesReference(reference) + .toAxesOrder(order) + .toAngleUnit(angleUnit); + } + + /** + * @return A {@link Quaternion} object representing the current orientation of the robot + * in the Robot Coordinate System, relative to the robot's position the last time + * that {@link #resetYaw()} was called, as if the robot was perfectly level at that time. + *

+ * Quaternions provide an advanced way to access orientation data that will work well + * for any orientation of the robot, even where other types of orientation data would + * encounter issues such as gimbal lock. + */ + @Override + public Quaternion getRobotOrientationAsQuaternion() { + // is this math right? + double yaw = getYaw(AngleUnit.RADIANS); + float qx = (float) (Math.cos(yaw/2) - Math.sin(yaw/2)); + float qy = (float) (Math.cos(yaw/2) + Math.sin(yaw/2)); + float qz = (float) (Math.sin(yaw/2) - Math.cos(yaw/2)); + float qw = (float) (Math.cos(yaw/2) + Math.sin(yaw/2)); + return new Quaternion(qw, qx, qy, qz, System.nanoTime()); + } + + /** + * @param unit the unit to return the velocity in + * @return The angular velocity of the robot (how fast it's turning around the three axes) in units per second + */ + @Override + public AngularVelocity getRobotAngularVelocity(AngleUnit unit) { + + return new AngularVelocity(unit,0f,0f,(float) getYawVelocity(unit),System.nanoTime()); + } + + /** + * @param unit the unit to return the velocity in + * @return The yaw velocity of the robot in units per second + */ + public double getYawVelocity(AngleUnit unit) { + if (oldYawAge.milliseconds() > 1) { // only read if necessary (is this value big enough?) + getYaw(AngleUnit.RADIANS); // output value doesn't matter, just doing this to trigger the filter + } + double yawVelocity = filteredYawVel; + return unit.fromRadians(yawVelocity); + } + + /** + * @param yaw current yaw in radians + * @return yaw velocity in radians/sec + */ + private double getRawYawVelocity(double yaw) { + double yawVel = (yaw - oldYaw) / oldYawAge.seconds(); + oldYaw = yaw; + oldYawAge.reset(); + return yawVel; + } + + /** + * Updates the lowpass filter based on an input in radians + * @param yaw input yaw in radians + */ + private void updateLowpassFilter(double yaw) { + if (yaw != oldYaw) { // if this is a different loop with different bulk read data + // update the low-pass filter + // is milliseconds the right unit? + filteredYawVel += filteredYawVelAge.milliseconds() * (getRawYawVelocity(yaw) - filteredYawVel) / lowpassSmoothing; + filteredYawVelAge.reset(); + } + } + + private void resetLowpassFilter(double setValue) { + filteredYawVel = 0; + oldYaw = setValue; + filteredYawVelAge.reset(); + oldYawAge.reset(); + } } From f8c32f8d0bf64c4b18c2302574d36c79aafecb24 Mon Sep 17 00:00:00 2001 From: j5155 <54331556+j5155@users.noreply.github.com> Date: Sat, 19 Oct 2024 13:43:59 -0800 Subject: [PATCH 02/10] fix warnings --- .../canandgyro/ReduxAnalogCanandgyro.java | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java index 4b2a09a..82fff3b 100644 --- a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java +++ b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java @@ -46,7 +46,6 @@ @DeviceProperties( name = "Redux Canandgyro (Analog)", xmlTag = "ReduxAnalogCanandgyro", - builtIn = false, compatibleControlSystems = {ControlSystem.REV_HUB}, description = "a Redux Robotics Canandgyro connected via the analog port" ) @@ -146,7 +145,7 @@ public void zero() { } /** - * Fetches the zero offset that is subtracted from the analog reading to produce a re-zeroed yaw. + * Fetches the zero offset subtracted from the analog reading to produce a re-zeroed yaw. *

* You can think of {@link #getYaw} being computed as *

@@ -171,7 +170,7 @@ public double getZeroOffset(AngleUnit unit) {
   }
 
   /**
-   * Sets the zero offset that is subtracted from the analog reading.
+   * Sets the zero offset subtracted from the analog reading.
    * 

* You can think of {@link #getYaw} being computed as *

@@ -277,10 +276,7 @@ public void close() {
   }
 
   /**
-   * Initializes the IMU with non-default settings.
-   *
-   * @param parameters
-   * @return Whether initialization succeeded.
+   * This does nothing with the Canandgyro; it's only here to allow existing IMU uses to work.
    */
   @Override
   public boolean initialize(Parameters parameters) {
@@ -288,12 +284,7 @@ public boolean initialize(Parameters parameters) {
   }
 
   /**
-   * Resets the robot's yaw angle to 0. After calling this method, the reported orientation will
-   * be relative to the robot's position when this method was called, as if the robot was perfectly
-   * level right then. That is to say, the pitch and yaw will be ignored when this method is
-   * called.
-   * 

- * Unlike yaw, pitch and roll are always relative to gravity, and never need to be reset. + * Resets the robot's yaw angle to 0 */ @Override public void resetYaw() { @@ -312,9 +303,6 @@ public YawPitchRollAngles getRobotYawPitchRollAngles() { } /** - * @param reference - * @param order - * @param angleUnit * @return An {@link Orientation} object representing the current orientation of the robot * in the Robot Coordinate System, relative to the robot's position the last time * that {@link #resetYaw()} was called, as if the robot was perfectly level at that time. @@ -367,7 +355,7 @@ public AngularVelocity getRobotAngularVelocity(AngleUnit unit) { */ public double getYawVelocity(AngleUnit unit) { if (oldYawAge.milliseconds() > 1) { // only read if necessary (is this value big enough?) - getYaw(AngleUnit.RADIANS); // output value doesn't matter, just doing this to trigger the filter + getYaw(AngleUnit.RADIANS); // the output value doesn't matter, just doing this to trigger the filter } double yawVelocity = filteredYawVel; return unit.fromRadians(yawVelocity); @@ -389,9 +377,9 @@ private double getRawYawVelocity(double yaw) { * @param yaw input yaw in radians */ private void updateLowpassFilter(double yaw) { - if (yaw != oldYaw) { // if this is a different loop with different bulk read data - // update the low-pass filter - // is milliseconds the right unit? + if (yaw != oldYaw) { // ensure this is a different loop with different bulk read data, + // then update the low-pass filter + //(is milliseconds the right unit?) filteredYawVel += filteredYawVelAge.milliseconds() * (getRawYawVelocity(yaw) - filteredYawVel) / lowpassSmoothing; filteredYawVelAge.reset(); } From 03a40416644154210e7f9560af4fc00dac01c5bf Mon Sep 17 00:00:00 2001 From: j5155 <54331556+j5155@users.noreply.github.com> Date: Sat, 19 Oct 2024 13:49:40 -0800 Subject: [PATCH 03/10] add deadband detection --- .../ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java index 82fff3b..63421d5 100644 --- a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java +++ b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java @@ -206,7 +206,14 @@ public void setZeroOffset(double newOffset, AngleUnit unit) { tooltip = "Read the raw voltage with the zero offset applied." ) public double readZeroedVoltage() { - double zeroed = readRawVoltage() - zeroOffset; + // deadband detection + double rawVoltage = readRawVoltage(); + if (rawVoltage <= 0.005) { + rawVoltage = 0; + } else if (rawVoltage >= 3.295) { + rawVoltage = 3.3; + } + double zeroed = rawVoltage - zeroOffset; while (zeroed < 0.0) { zeroed += MAX_VOLTAGE; } From 484b9400221f2866e8682864a791815d25392de5 Mon Sep 17 00:00:00 2001 From: j5155 <54331556+j5155@users.noreply.github.com> Date: Sat, 19 Oct 2024 19:14:26 -0800 Subject: [PATCH 04/10] remove unnecessary initializers --- .../ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java index 63421d5..21a50ec 100644 --- a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java +++ b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java @@ -50,9 +50,9 @@ description = "a Redux Robotics Canandgyro connected via the analog port" ) public class ReduxAnalogCanandgyro implements AnalogSensor, OrientationSensor, HardwareDevice, IMU { - private AnalogInputController controller = null; - private int channel = -1; - private double zeroOffset = 0; + private final AnalogInputController controller; + private final int channel; + private double zeroOffset; // The canandgyro itself will only output a maximum of MAX_VOLTAGE volts. private static final double MAX_VOLTAGE = 3.3; From d18c1c97e3eb035282d0815bbde704b7a5e35208 Mon Sep 17 00:00:00 2001 From: j5155 <54331556+j5155@users.noreply.github.com> Date: Sat, 19 Oct 2024 19:27:31 -0800 Subject: [PATCH 05/10] switch radians to degrees --- .../canandgyro/ReduxAnalogCanandgyro.java | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java index 21a50ec..f45be46 100644 --- a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java +++ b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java @@ -57,9 +57,9 @@ public class ReduxAnalogCanandgyro implements AnalogSensor, OrientationSensor, H // The canandgyro itself will only output a maximum of MAX_VOLTAGE volts. private static final double MAX_VOLTAGE = 3.3; - private double oldYaw = 0; // radians + private double oldYaw = 0; // degrees private final ElapsedTime oldYawAge = new ElapsedTime(); - private double filteredYawVel = 0; // radians/sec + private double filteredYawVel = 0; // degrees/sec private final ElapsedTime filteredYawVelAge = new ElapsedTime(); private static final double lowpassSmoothing = 10.0; // https://phrogz.net/js/framerate-independent-low-pass-filter.html @@ -91,9 +91,9 @@ public ReduxAnalogCanandgyro(AnalogInputController controller, int channel) { parameterLabels = { "angleUnit" } ) public double getYaw(AngleUnit unit) { - double yaw = AngleUnit.normalizeRadians(readZeroedVoltage() / MAX_VOLTAGE * (Math.PI * 2)); + double yaw = AngleUnit.normalizeDegrees(readZeroedVoltage() / MAX_VOLTAGE * 360.0); updateLowpassFilter(yaw); - return unit.fromRadians(yaw); + return unit.fromDegrees(yaw); } /** @@ -115,10 +115,10 @@ public double getYaw(AngleUnit unit) { parameterLabels = { "newAngle", "angleUnit" } ) public void setYaw(double newAngle, AngleUnit unit) { - newAngle = unit.toRadians(newAngle); + newAngle = unit.toDegrees(newAngle); resetLowpassFilter(newAngle); - newAngle *= MAX_VOLTAGE / (Math.PI * 2); + newAngle *= MAX_VOLTAGE / 360.0; zeroOffset = readRawVoltage() - newAngle; } @@ -166,7 +166,7 @@ public void zero() { parameterLabels = { "angleUnit" } ) public double getZeroOffset(AngleUnit unit) { - return unit.fromRadians(zeroOffset / MAX_VOLTAGE * 2 * Math.PI); + return unit.fromDegrees(zeroOffset / MAX_VOLTAGE * 360); } /** @@ -190,7 +190,7 @@ public double getZeroOffset(AngleUnit unit) { parameterLabels = { "newOffset", "angleUnit" } ) public void setZeroOffset(double newOffset, AngleUnit unit) { - newOffset = unit.toRadians(newOffset) / (2 * Math.PI); + newOffset = unit.toDegrees(newOffset) / 360; zeroOffset = newOffset * MAX_VOLTAGE; } @@ -306,7 +306,7 @@ public void resetYaw() { @Override public YawPitchRollAngles getRobotYawPitchRollAngles() { // Blindly using System.nanoTime() here isn't ideal, but no better option, I think - return new YawPitchRollAngles(AngleUnit.RADIANS,getYaw(AngleUnit.RADIANS),0.0,0.0,System.nanoTime()); + return new YawPitchRollAngles(AngleUnit.DEGREES,getYaw(AngleUnit.DEGREES),0.0,0.0,System.nanoTime()); } /** @@ -320,7 +320,7 @@ public YawPitchRollAngles getRobotYawPitchRollAngles() { @Override public Orientation getRobotOrientation(AxesReference reference, AxesOrder order, AngleUnit angleUnit) { // Blindly using System.nanoTime() here isn't ideal, but no better option, I think - return new Orientation(AxesReference.EXTRINSIC, AxesOrder.ZYX, AngleUnit.RADIANS,(float) getYaw(AngleUnit.RADIANS), 0f , 0f, System.nanoTime()) + return new Orientation(AxesReference.EXTRINSIC, AxesOrder.ZYX, AngleUnit.DEGREES,(float) getYaw(AngleUnit.DEGREES), 0f , 0f, System.nanoTime()) .toAxesReference(reference) .toAxesOrder(order) .toAngleUnit(angleUnit); @@ -338,7 +338,7 @@ public Orientation getRobotOrientation(AxesReference reference, AxesOrder order, @Override public Quaternion getRobotOrientationAsQuaternion() { // is this math right? - double yaw = getYaw(AngleUnit.RADIANS); + double yaw = getYaw(AngleUnit.RADIANS); // this has to be radians float qx = (float) (Math.cos(yaw/2) - Math.sin(yaw/2)); float qy = (float) (Math.cos(yaw/2) + Math.sin(yaw/2)); float qz = (float) (Math.sin(yaw/2) - Math.cos(yaw/2)); @@ -352,7 +352,6 @@ public Quaternion getRobotOrientationAsQuaternion() { */ @Override public AngularVelocity getRobotAngularVelocity(AngleUnit unit) { - return new AngularVelocity(unit,0f,0f,(float) getYawVelocity(unit),System.nanoTime()); } @@ -362,15 +361,15 @@ public AngularVelocity getRobotAngularVelocity(AngleUnit unit) { */ public double getYawVelocity(AngleUnit unit) { if (oldYawAge.milliseconds() > 1) { // only read if necessary (is this value big enough?) - getYaw(AngleUnit.RADIANS); // the output value doesn't matter, just doing this to trigger the filter + getYaw(AngleUnit.DEGREES); // the output value doesn't matter, just doing this to trigger the filter } double yawVelocity = filteredYawVel; - return unit.fromRadians(yawVelocity); + return unit.fromDegrees(yawVelocity); } /** - * @param yaw current yaw in radians - * @return yaw velocity in radians/sec + * @param yaw current yaw in degrees + * @return yaw velocity in degrees/sec */ private double getRawYawVelocity(double yaw) { double yawVel = (yaw - oldYaw) / oldYawAge.seconds(); @@ -380,8 +379,8 @@ private double getRawYawVelocity(double yaw) { } /** - * Updates the lowpass filter based on an input in radians - * @param yaw input yaw in radians + * Updates the lowpass filter based on an input in degrees + * @param yaw input yaw in degrees */ private void updateLowpassFilter(double yaw) { if (yaw != oldYaw) { // ensure this is a different loop with different bulk read data, From d977de6d033428b105ff8e2dcac1beb1763e3b00 Mon Sep 17 00:00:00 2001 From: j5155 <54331556+j5155@users.noreply.github.com> Date: Sat, 19 Oct 2024 19:28:09 -0800 Subject: [PATCH 06/10] add credit --- .../ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java | 1 + 1 file changed, 1 insertion(+) diff --git a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java index f45be46..d742cee 100644 --- a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java +++ b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java @@ -40,6 +40,7 @@ /** * Device class for a Redux Robotics Canandgyro connected via an analog port. * @author guineawheek@reduxrobotics.com + * @author j5155 ftcjdhs@gmail.com */ @ExportClassToBlocks @AnalogSensorType From 435c344cea0e87840699cc91a6e67423b7090713 Mon Sep 17 00:00:00 2001 From: j5155 <54331556+j5155@users.noreply.github.com> Date: Sat, 19 Oct 2024 20:03:13 -0800 Subject: [PATCH 07/10] Add error calibration by changing zeroOffset to degrees instead of voltage --- .../canandgyro/ReduxAnalogCanandgyro.java | 56 +++++++------------ 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java index d742cee..36807b0 100644 --- a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java +++ b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java @@ -92,11 +92,28 @@ public ReduxAnalogCanandgyro(AnalogInputController controller, int channel) { parameterLabels = { "angleUnit" } ) public double getYaw(AngleUnit unit) { - double yaw = AngleUnit.normalizeDegrees(readZeroedVoltage() / MAX_VOLTAGE * 360.0); + double yaw = getRawYaw() - zeroOffset; updateLowpassFilter(yaw); return unit.fromDegrees(yaw); } + /** + * Get yaw directly from the voltage without the zero correction + * @return raw yaw in degrees + */ + private double getRawYaw() { + double rawVoltage = readRawVoltage(); + // deadband detection + if (rawVoltage <= 0.005) { + return 0; + } else if (rawVoltage >= 3.295) { + return 360; + } else { + // corrects for error and nonlinearity in voltage read by the hub + return rawVoltage * 108.95105635 - 179.64903577; + } + } + /** * Sets the yaw angle to be the specified angle. *

@@ -118,9 +135,7 @@ public double getYaw(AngleUnit unit) { public void setYaw(double newAngle, AngleUnit unit) { newAngle = unit.toDegrees(newAngle); resetLowpassFilter(newAngle); - - newAngle *= MAX_VOLTAGE / 360.0; - zeroOffset = readRawVoltage() - newAngle; + zeroOffset = getRawYaw() - newAngle; } /** @@ -167,7 +182,7 @@ public void zero() { parameterLabels = { "angleUnit" } ) public double getZeroOffset(AngleUnit unit) { - return unit.fromDegrees(zeroOffset / MAX_VOLTAGE * 360); + return unit.fromDegrees(zeroOffset); } /** @@ -191,38 +206,9 @@ public double getZeroOffset(AngleUnit unit) { parameterLabels = { "newOffset", "angleUnit" } ) public void setZeroOffset(double newOffset, AngleUnit unit) { - newOffset = unit.toDegrees(newOffset) / 360; - zeroOffset = newOffset * MAX_VOLTAGE; - + zeroOffset = unit.toDegrees(newOffset); } - /** - * Reads the raw voltage output with the zero offset applied. - * - * @return voltage from [0, 3.3) volts - */ - @ExportToBlocks( - heading = "read zeroed voltage", - comment = "Read the raw voltage with the zero offset applied.", - tooltip = "Read the raw voltage with the zero offset applied." - ) - public double readZeroedVoltage() { - // deadband detection - double rawVoltage = readRawVoltage(); - if (rawVoltage <= 0.005) { - rawVoltage = 0; - } else if (rawVoltage >= 3.295) { - rawVoltage = 3.3; - } - double zeroed = rawVoltage - zeroOffset; - while (zeroed < 0.0) { - zeroed += MAX_VOLTAGE; - } - while (zeroed >= MAX_VOLTAGE) { - zeroed -= MAX_VOLTAGE; - } - return zeroed; - } /** * Reads the raw voltage output straight from the control/expansion hub. From a87a2db65c911ffcca72021af7d87bd32f59cde3 Mon Sep 17 00:00:00 2001 From: j5155 <54331556+j5155@users.noreply.github.com> Date: Sat, 19 Oct 2024 20:05:18 -0800 Subject: [PATCH 08/10] use getter and setter for zeroOffset, fixing two warnings --- .../ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java index 36807b0..11109c2 100644 --- a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java +++ b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java @@ -92,7 +92,7 @@ public ReduxAnalogCanandgyro(AnalogInputController controller, int channel) { parameterLabels = { "angleUnit" } ) public double getYaw(AngleUnit unit) { - double yaw = getRawYaw() - zeroOffset; + double yaw = getRawYaw() - getZeroOffset(AngleUnit.DEGREES); updateLowpassFilter(yaw); return unit.fromDegrees(yaw); } @@ -135,7 +135,7 @@ private double getRawYaw() { public void setYaw(double newAngle, AngleUnit unit) { newAngle = unit.toDegrees(newAngle); resetLowpassFilter(newAngle); - zeroOffset = getRawYaw() - newAngle; + setZeroOffset(getRawYaw() - newAngle, AngleUnit.DEGREES); } /** From 4b0903aec540ca80d493e46f95fc2b2aecda15e7 Mon Sep 17 00:00:00 2001 From: j5155 <54331556+j5155@users.noreply.github.com> Date: Sat, 19 Oct 2024 20:10:29 -0800 Subject: [PATCH 09/10] Fix the last two warnings --- .../ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java index 11109c2..0199c17 100644 --- a/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java +++ b/reduxftc/src/main/java/com/reduxrobotics/ftc/sensors/canandgyro/ReduxAnalogCanandgyro.java @@ -98,7 +98,7 @@ public double getYaw(AngleUnit unit) { } /** - * Get yaw directly from the voltage without the zero correction + * Get yaw directly from the voltage without the zero offset * @return raw yaw in degrees */ private double getRawYaw() { @@ -322,6 +322,10 @@ public Orientation getRobotOrientation(AxesReference reference, AxesOrder order, * for any orientation of the robot, even where other types of orientation data would * encounter issues such as gimbal lock. */ + // qy and qw being the same is perfectly fine + // and extracing it into a variable would make this unnecessarily complicated + // also for some reason IntelliJ keeps forgetting this warning exists so also supress RedundantSuppression + @SuppressWarnings({"DuplicateExpressions", "RedundantSuppression"}) @Override public Quaternion getRobotOrientationAsQuaternion() { // is this math right? From 0e562a69894b0bd3269b075baed0ff06b895e240 Mon Sep 17 00:00:00 2001 From: j5155 <54331556+j5155@users.noreply.github.com> Date: Sun, 20 Oct 2024 12:30:34 -0800 Subject: [PATCH 10/10] Fix typos in example --- .../teamcode/SensorReduxAnalogCanandgyro.java | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/examples/src/main/java/org/firstinspires/ftc/teamcode/SensorReduxAnalogCanandgyro.java b/examples/src/main/java/org/firstinspires/ftc/teamcode/SensorReduxAnalogCanandgyro.java index 157a059..b13dac5 100644 --- a/examples/src/main/java/org/firstinspires/ftc/teamcode/SensorReduxAnalogCanandgyro.java +++ b/examples/src/main/java/org/firstinspires/ftc/teamcode/SensorReduxAnalogCanandgyro.java @@ -26,11 +26,9 @@ package org.firstinspires.ftc.teamcode; -import com.qualcomm.robotcore.eventloop.opmode.Disabled; import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode; import com.qualcomm.robotcore.eventloop.opmode.TeleOp; import com.reduxrobotics.ftc.sensors.canandgyro.ReduxAnalogCanandgyro; - import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit; @@ -41,9 +39,9 @@ *

It assumes that the sensor is configured with a name of "canandgyro". * *

The theory of operation is that the Canandgyro will output its current absolute yaw angle - * through the analog port as a voltage between 0 to 3.3 volts. On boot it will assume a starting + * through the analog port as a voltage between 0 and 3.3 volts. On boot, it will assume a starting * value of 1.65 volts to represent a yaw of 0 degrees. Rotating the device clockwise will raise the - * voltage towards 3.3 volts (positive 180 degrees) and rotating it counter-clockwise will lower + * voltage towards 3.3 volts (positive 180 degrees), and rotating it counter-clockwise will lower * the voltage towards 0 volts (negative 180 degrees). *

* @@ -51,10 +49,10 @@ * {@link ReduxAnalogCanandgyro} class wraps this raw readng and applies a software-defined zero * offset that can be set on opmode start via * {@link ReduxAnalogCanandgyro#setYaw(double, AngleUnit)} or {@link ReduxAnalogCanandgyro#zero()} - * as during match setup the robot's heading can be assumed to be a specific angle. + * as during match set up the robot's heading can be assumed to be a specific angle. *

* - * Use Android Studio to Copy this Class, and Paste it into your team's code folder with a new name. + * Use Android Studio to Copy this Class and Paste it into your team's code folder with a new name. * Remove or comment out the @Disabled line to add this OpMode to the Driver Station OpMode list */ //@Disabled @@ -69,7 +67,7 @@ public void runOpMode() { // get a reference to our canandgyro analogCanandgyro = hardwareMap.get(ReduxAnalogCanandgyro.class, "canandgyro"); - // Updates the zero point right as the opmode starts and before the robot may move. + // Updates the zero offset right as the opmode starts and before the robot may move. // This is recommended at the start of every opmode to clear the effect of any drift or shift in // robot pose accumulated from before-match setup. analogCanandgyro.zero(); @@ -79,19 +77,19 @@ public void runOpMode() { boolean xPrevPressed = false; boolean yPrevPressed = false; - // while the OpMode is active, loop and read the yaw angle. - // Note we use opModeIsActive() as our loop condition because it is an interruptible method. + // While the OpMode is active, loop and read the yaw angle. + // Note: we use opModeIsActive() as our loop condition because it is an interruptible method. while (opModeIsActive()) { if (!xPrevPressed && gamepad1.x) { - // Using setYaw we can set the offset of the gyro to an absolute angle. + // Using setYaw, we can set the offset of the gyro to an absolute angle. // When the X button is pressed, this will set our current yaw to 30 degrees. analogCanandgyro.setYaw(30, AngleUnit.DEGREES); } xPrevPressed = gamepad1.x; if (!yPrevPressed && gamepad1.y) { - // We can also adjust the angle relative to itself as well. + // We can also adjust the angle relative to itself. // When the Y button is pressed, we offset the zero point by 15 degrees. analogCanandgyro.setYaw(analogCanandgyro.getYaw(AngleUnit.DEGREES) + 15, AngleUnit.DEGREES); }