Pot range is now dynamic

This commit is contained in:
Eric Ratliff
2026-02-04 00:17:18 -06:00
parent b97838252f
commit 840b901506
6 changed files with 110 additions and 17 deletions

View File

@@ -22,6 +22,9 @@ public class ChuteOpMode extends LinearOpMode {
private Chute chute; private Chute chute;
// Pot configuration - change this if your pot has different range
private static final double POT_WRAP_AMOUNT = 2 * Math.PI;
// Position presets (in radians) // Position presets (in radians)
private static final double HOME = 0.0; private static final double HOME = 0.0;
private static final double LOW = Math.PI; private static final double LOW = Math.PI;
@@ -37,13 +40,14 @@ public class ChuteOpMode extends LinearOpMode {
// Create hardware adapters // Create hardware adapters
FtcMotor motor = new FtcMotor(chuteMotor); FtcMotor motor = new FtcMotor(chuteMotor);
FtcPotentiometer pot = new FtcPotentiometer(chutePot); FtcPotentiometer pot = new FtcPotentiometer(chutePot, POT_WRAP_AMOUNT);
// Create chute controller with real hardware // Create chute controller with real hardware
ChuteController controller = new ChuteController(motor, pot, MAX); ChuteController controller = new ChuteController(motor, pot, MAX);
chute = new Chute(controller, motor, pot); chute = new Chute(controller, motor, pot);
telemetry.addLine("Chute initialized"); telemetry.addLine("Chute initialized");
telemetry.addData("Pot wrap", "%.2f rad", POT_WRAP_AMOUNT);
telemetry.addLine("Press A to home"); telemetry.addLine("Press A to home");
telemetry.update(); telemetry.update();

View File

@@ -9,11 +9,21 @@ public class Chute {
private final MockPotentiometer pot; private final MockPotentiometer pot;
/** /**
* Create chute with mock hardware (for testing). * Create chute with mock hardware (for testing) - default 2pi wraparound.
* @param maxExtension Maximum extension in radians
*/ */
public Chute(double maxExtension) { public Chute(double maxExtension) {
this(maxExtension, 2 * Math.PI);
}
/**
* Create chute with mock hardware (for testing) - custom wraparound.
* @param maxExtension Maximum extension in radians
* @param wrapAmount Potentiometer wrap amount in radians
*/
public Chute(double maxExtension, double wrapAmount) {
this.motor = new MockMotor(); this.motor = new MockMotor();
this.pot = new MockPotentiometer(); this.pot = new MockPotentiometer(wrapAmount);
this.controller = new ChuteController(motor, pot, maxExtension); this.controller = new ChuteController(motor, pot, maxExtension);
} }

View File

@@ -7,6 +7,7 @@ public class ChuteController {
private final MockMotor motor; private final MockMotor motor;
private final MockPotentiometer pot; private final MockPotentiometer pot;
private final double maxExtension; private final double maxExtension;
private final double wrapAmount;
private boolean homed = false; private boolean homed = false;
private boolean homing = false; private boolean homing = false;
@@ -32,6 +33,7 @@ public class ChuteController {
this.motor = motor; this.motor = motor;
this.pot = pot; this.pot = pot;
this.maxExtension = maxExtension; this.maxExtension = maxExtension;
this.wrapAmount = pot.getWrapAmount();
} }
/** /**
@@ -129,13 +131,14 @@ public class ChuteController {
double delta = voltage - lastVoltage; double delta = voltage - lastVoltage;
// Detect wraps and accumulate to unwrapped voltage // Detect wraps and accumulate to unwrapped voltage
if (Math.abs(delta) > Math.PI) { // Threshold is half the wrap amount
if (Math.abs(delta) > wrapAmount / 2) {
if (delta < 0) { if (delta < 0) {
// Forward wrap: voltage dropped, add back 2pi // Forward wrap: voltage dropped, add back wrap amount
unwrappedVoltage += (delta + 2 * Math.PI); unwrappedVoltage += (delta + wrapAmount);
} else { } else {
// Backward wrap: voltage jumped, subtract 2pi // Backward wrap: voltage jumped, subtract wrap amount
unwrappedVoltage += (delta - 2 * Math.PI); unwrappedVoltage += (delta - wrapAmount);
} }
} else { } else {
// Normal movement // Normal movement
@@ -185,12 +188,20 @@ public class ChuteController {
/** /**
* Get the home voltage reference captured during homing. * Get the home voltage reference captured during homing.
* @return Home voltage in range [0, 2pi] * @return Home voltage in range [0, wrapAmount]
*/ */
public double getHomeVoltage() { public double getHomeVoltage() {
return homeVoltage; return homeVoltage;
} }
/**
* Get the pot wraparound amount.
* @return Wrap amount in radians
*/
public double getWrapAmount() {
return wrapAmount;
}
/** /**
* Emergency stop - immediately stops motor and cancels movement. * Emergency stop - immediately stops motor and cancels movement.
*/ */

View File

@@ -11,11 +11,20 @@ public class FtcPotentiometer extends MockPotentiometer {
private final double maxVoltage; private final double maxVoltage;
/** /**
* Create potentiometer adapter. * Create potentiometer adapter with default 2pi wraparound.
* @param pot The real FTC analog input from hardwareMap * @param pot The real FTC analog input from hardwareMap
*/ */
public FtcPotentiometer(AnalogInput pot) { public FtcPotentiometer(AnalogInput pot) {
super(); this(pot, 2 * Math.PI);
}
/**
* Create potentiometer adapter with custom wraparound.
* @param pot The real FTC analog input from hardwareMap
* @param wrapAmount Voltage value at which pot wraps (in radians)
*/
public FtcPotentiometer(AnalogInput pot, double wrapAmount) {
super(wrapAmount);
this.pot = pot; this.pot = pot;
this.maxVoltage = pot.getMaxVoltage(); // Usually 3.3V this.maxVoltage = pot.getMaxVoltage(); // Usually 3.3V
} }
@@ -25,9 +34,9 @@ public class FtcPotentiometer extends MockPotentiometer {
// Read actual voltage from hardware // Read actual voltage from hardware
double rawVoltage = pot.getVoltage(); double rawVoltage = pot.getVoltage();
// Scale to [0, 2pi] range // Scale to [0, wrapAmount] range
// Assumes pot uses full voltage range (0 to maxVoltage) // Assumes pot uses full voltage range (0 to maxVoltage)
return (rawVoltage / maxVoltage) * 2 * Math.PI; return (rawVoltage / maxVoltage) * getWrapAmount();
} }
@Override @Override

View File

@@ -1,19 +1,32 @@
package org.firstinspires.ftc.teamcode.subsystems.chute; package org.firstinspires.ftc.teamcode.subsystems.chute;
/** /**
* Simple mock potentiometer with wraparound at 2pi. * Simple mock potentiometer with configurable wraparound.
*/ */
public class MockPotentiometer { public class MockPotentiometer {
private double position; private double position;
private final double wrapAmount;
/**
* Create potentiometer with default 2pi wraparound.
*/
public MockPotentiometer() { public MockPotentiometer() {
this(2 * Math.PI);
}
/**
* Create potentiometer with custom wraparound amount.
* @param wrapAmount Voltage at which pot wraps back to 0
*/
public MockPotentiometer(double wrapAmount) {
this.position = 0.0; this.position = 0.0;
this.wrapAmount = wrapAmount;
} }
public double getVoltage() { public double getVoltage() {
// Wrap to [0, 2pi] // Wrap to [0, wrapAmount]
double wrapped = position % (2 * Math.PI); double wrapped = position % wrapAmount;
if (wrapped < 0) wrapped += 2 * Math.PI; if (wrapped < 0) wrapped += wrapAmount;
return wrapped; return wrapped;
} }
@@ -24,4 +37,8 @@ public class MockPotentiometer {
public void updatePosition(double delta) { public void updatePosition(double delta) {
this.position += delta; this.position += delta;
} }
public double getWrapAmount() {
return wrapAmount;
}
} }

View File

@@ -105,6 +105,8 @@ public class ChuteTest {
chute.getMotor().setPosition(0.2); chute.getMotor().setPosition(0.2);
chute.getPot().setPosition(0.2); chute.getPot().setPosition(0.2);
chute.home(); chute.home();
// Wait for homing to complete (200 iterations x 0.02s = 4 second timeout)
for (int i = 0; i < 200; i++) { for (int i = 0; i < 200; i++) {
chute.update(DT); chute.update(DT);
if (chute.isHomed()) break; if (chute.isHomed()) break;
@@ -226,4 +228,44 @@ public class ChuteTest {
assertTrue(downPos < 1.5, "Should move back down"); assertTrue(downPos < 1.5, "Should move back down");
assertTrue(downPos < upPos, "Down position should be less than up"); assertTrue(downPos < upPos, "Down position should be less than up");
} }
/**
* Test custom wraparound amount (3.0 radians instead of 2pi).
* Verifies controller correctly handles non-standard pot configurations.
*/
@Test
public void testCustomWrapAmount() {
// Create chute with 3.0 radian wraparound (instead of default 2pi)
Chute chute = new Chute(20.0, 3.0);
// Home
chute.getMotor().setPosition(0.2);
chute.getPot().setPosition(0.2);
chute.home();
for (int i = 0; i < 200; i++) {
chute.update(DT);
if (chute.isHomed()) break;
}
assertTrue(chute.isHomed(), "Should be homed");
// Move to 2.5 wraps (2.5 * 3.0 = 7.5 radians)
double target = 2.5 * 3.0;
chute.setTargetPosition(target);
for (int i = 0; i < 2000; i++) {
chute.update(DT);
if (chute.isAtTarget()) break;
}
// Should track through wraps correctly
assertTrue(chute.getPosition() > 2.0 * 3.0,
"Position should be > 2 wraps (6.0), got: " + chute.getPosition());
assertTrue(Math.abs(chute.getPosition() - target) < 1.0,
"Position should be near target, expected: " + target + ", got: " + chute.getPosition());
// Verify pot actually wraps at 3.0
assertEquals(3.0, chute.getPot().getWrapAmount(), EPSILON,
"Pot should wrap at 3.0 radians");
}
} }