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);
}