Skip to content

Commit 450f940

Browse files
committed
commit from last week, got swerve set up with roadrunner and running with LocalizationTest, so it should be able to be tuned now, in theory at least
1 parent 60deb63 commit 450f940

File tree

7 files changed

+394
-23
lines changed

7 files changed

+394
-23
lines changed

Diff for: TeamCode/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
// Include common definitions from above.
1515
apply from: '../build.common.gradle'
1616
apply from: '../build.dependencies.gradle'
17+
apply plugin: 'kotlin-android'
1718

1819
android {
1920
namespace = 'org.firstinspires.ftc.teamcode'

Diff for: TeamCode/src/main/java/org/firstinspires/ftc/teamcode/drivetrains/CoaxialSwerveDrive.java

+10-8
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,16 @@ public enum WheelState {
1616

1717
WheelState wheelState = WheelState.DRIVE;
1818
AxonSwervePod[] swervePods = new AxonSwervePod[4];
19+
public static final double[] encoderOffsets = { 3.47, 4.24, 0.94, 0.04 };
1920
double wheelbase;
2021
double trackwidth;
2122
double[] wheelSpeeds;
2223
double[] wheelAngles;
2324

2425
public CoaxialSwerveDrive( HardwareMap hw ) {
25-
this( hw, new String[]{ "FLM", "BLM", "FRM", "BRM" }, new boolean[]{ false, false, false, false },
26+
this( hw, new String[]{ "FLM/perp", "BLM", "FRM", "BRM/para" }, new boolean[]{ false, false, false, false },
2627
new String[]{ "FLS", "BLS", "FRS", "BRS" }, new boolean[]{ false, false, false, false },
27-
new String[]{ "FLE", "BLE", "FRE", "BRE" }, new double[]{ 5.96, 0.92, 4.71, 4.84 },
28+
new String[]{ "FLE", "BLE", "FRE", "BRE" }, encoderOffsets,
2829
3.3, 11.3125, 11.3125, new double[]{ 0.6, 0.02 }, 28 * 8 );
2930
}
3031

@@ -57,11 +58,11 @@ public CoaxialSwerveDrive( HardwareMap hw, String[] motorNames, boolean[] motorR
5758
* @param rotatePower power to turn the robot (right)
5859
*/
5960
public void drive( double drivePower, double strafePower, double rotatePower ) {
60-
boolean rotatePods = Math.abs(drivePower) > 0.02 ||
61-
Math.abs(strafePower) > 0.02 ||
62-
Math.abs(rotatePower) > 0.02;
61+
boolean rotatePods = Math.abs( drivePower ) > 0.02 ||
62+
Math.abs( strafePower ) > 0.02 ||
63+
Math.abs( rotatePower ) > 0.02;
6364

64-
switch (wheelState) {
65+
switch( wheelState ) {
6566
case DRIVE:
6667
// distance between opposite wheels
6768
double R = Math.sqrt( wheelbase * wheelbase + trackwidth * trackwidth );
@@ -111,17 +112,18 @@ public void drive( double drivePower, double strafePower, double rotatePower ) {
111112
}
112113
}
113114

114-
public void fieldCentricDrive ( double drivePower, double strafePower, double rotatePower, double heading ) {
115+
public void fieldCentricDrive( double drivePower, double strafePower, double rotatePower, double heading ) {
115116
double temp = drivePower * Math.cos( heading ) + strafePower * Math.sin( heading );
116117
strafePower = -drivePower * Math.sin( heading ) + strafePower * Math.cos( heading );
117118
drivePower = -temp;
118119

119-
drive(drivePower, strafePower, rotatePower);
120+
drive( drivePower, strafePower, rotatePower );
120121
}
121122

122123
public void setWheelState( WheelState state ) {
123124
wheelState = state;
124125
}
126+
125127
public WheelState getWheelState( ) {
126128
return wheelState;
127129
}

Diff for: TeamCode/src/main/java/org/firstinspires/ftc/teamcode/roadrunner/SampleSwerveDrive.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
import static org.firstinspires.ftc.teamcode.roadrunner.DriveConstants.kA;
1010
import static org.firstinspires.ftc.teamcode.roadrunner.DriveConstants.kStatic;
1111
import static org.firstinspires.ftc.teamcode.roadrunner.DriveConstants.kV;
12+
import static org.firstinspires.ftc.teamcode.drivetrains.CoaxialSwerveDrive.encoderOffsets;
1213

1314
import androidx.annotation.NonNull;
1415

1516
import com.acmerobotics.roadrunner.control.PIDCoefficients;
1617
import com.acmerobotics.roadrunner.drive.DriveSignal;
17-
import com.acmerobotics.roadrunner.drive.SwerveDrive;
1818
import com.acmerobotics.roadrunner.followers.HolonomicPIDVAFollower;
1919
import com.acmerobotics.roadrunner.followers.TrajectoryFollower;
2020
import com.acmerobotics.roadrunner.geometry.Pose2d;
@@ -29,7 +29,6 @@
2929
import com.qualcomm.hardware.lynx.LynxModule;
3030
import com.qualcomm.hardware.rev.RevHubOrientationOnRobot;
3131
import com.qualcomm.robotcore.hardware.DcMotor;
32-
import com.qualcomm.robotcore.hardware.DcMotorEx;
3332
import com.qualcomm.robotcore.hardware.HardwareMap;
3433
import com.qualcomm.robotcore.hardware.IMU;
3534
import com.qualcomm.robotcore.hardware.VoltageSensor;
@@ -41,6 +40,7 @@
4140
import org.firstinspires.ftc.teamcode.roadrunner.trajectorysequence.TrajectorySequenceRunner;
4241
import org.firstinspires.ftc.teamcode.roadrunner.util.LynxModuleUtil;
4342
import org.firstinspires.ftc.teamcode.subsystems.AxonSwervePod;
43+
import org.firstinspires.ftc.teamcode.roadrunner.kinematics.SwerveDrive;
4444

4545
import java.util.ArrayList;
4646
import java.util.Arrays;
@@ -91,13 +91,13 @@ public SampleSwerveDrive( HardwareMap hardwareMap ) {
9191
imu.initialize( parameters );
9292

9393
frontLeft = new AxonSwervePod( hardwareMap,"FLM/perp", false, "FLS", false,
94-
"FLE", 3.47, 3.3, new double[]{ 0.6, 0.02 }, 28 * 8 );
94+
"FLE", encoderOffsets[0], 3.3, new double[]{ 0.6, 0.02 }, 28 * 8 );
9595
backLeft = new AxonSwervePod( hardwareMap,"BLM", false, "BLS", false,
96-
"BlE", 4.24, 3.3, new double[]{ 0.6, 0.02 }, 28 * 8 );
96+
"BLE", encoderOffsets[1], 3.3, new double[]{ 0.6, 0.02 }, 28 * 8 );
9797
frontRight = new AxonSwervePod( hardwareMap,"FRM", false, "FRS", false,
98-
"FRE", 0.94, 3.3, new double[]{ 0.6, 0.02 }, 28 * 8 );
98+
"FRE", encoderOffsets[2], 3.3, new double[]{ 0.6, 0.02 }, 28 * 8 );
9999
backRight = new AxonSwervePod( hardwareMap,"BRM/para", false, "BRS", false,
100-
"BRE", 0.04, 3.3, new double[]{ 0.6, 0.02 }, 28 * 8 );
100+
"BRE", encoderOffsets[3], 3.3, new double[]{ 0.6, 0.02 }, 28 * 8 );
101101

102102
pods = Arrays.asList( frontLeft, backLeft, frontRight, backRight );
103103

Diff for: TeamCode/src/main/java/org/firstinspires/ftc/teamcode/roadrunner/SwerveTwoDeadWheelLocalizer.java

+9-9
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@
3535
*/
3636
public class SwerveTwoDeadWheelLocalizer extends TwoTrackingWheelLocalizer {
3737

38-
public static double TICKS_PER_REV = 0;
39-
public static double WHEEL_RADIUS = 2; // in
38+
public static double TICKS_PER_REV = 8192;
39+
public static double WHEEL_RADIUS = 0.748; // in
4040
public static double GEAR_RATIO = 1; // output (wheel) speed / input (encoder) speed
4141

42-
public static double PARALLEL_X = 0; // X is the up and down direction
43-
public static double PARALLEL_Y = 0; // Y is the strafe direction
42+
public static double PARALLEL_X = 0.51; // X is the up and down direction
43+
public static double PARALLEL_Y = 6.74; // Y is the strafe direction
4444

45-
public static double PERPENDICULAR_X = 0;
46-
public static double PERPENDICULAR_Y = 0;
45+
public static double PERPENDICULAR_X = 0.23;
46+
public static double PERPENDICULAR_Y = -6.65;
4747

4848
// Parallel/Perpendicular to the forward axis
4949
// Parallel wheel is parallel to the forward axis
@@ -61,7 +61,7 @@ public SwerveTwoDeadWheelLocalizer( HardwareMap hardwareMap, SampleSwerveDrive d
6161
this.drive = drive;
6262

6363
parallelEncoder = new Encoder(hardwareMap.get( DcMotorEx.class, "BRM/para"));
64-
perpendicularEncoder = new Encoder(hardwareMap.get(DcMotorEx.class, "FLM/perp"));
64+
perpendicularEncoder = new Encoder(hardwareMap.get( DcMotorEx.class, "FLM/perp"));
6565

6666
// TODO: reverse any encoders using Encoder.setDirection(Encoder.Direction.REVERSE)
6767
}
@@ -97,8 +97,8 @@ public List<Double> getWheelVelocities( ) {
9797
// compensation method
9898

9999
return Arrays.asList(
100-
encoderTicksToInches( parallelEncoder.getRawVelocity( ) ),
101-
encoderTicksToInches( perpendicularEncoder.getRawVelocity( ) )
100+
encoderTicksToInches( parallelEncoder.getCorrectedVelocity( ) ),
101+
encoderTicksToInches( perpendicularEncoder.getCorrectedVelocity( ) )
102102
);
103103
}
104104
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package org.firstinspires.ftc.teamcode.roadrunner.kinematics
2+
3+
import com.acmerobotics.roadrunner.drive.Drive
4+
import com.acmerobotics.roadrunner.drive.DriveSignal
5+
import com.acmerobotics.roadrunner.geometry.Pose2d
6+
import com.acmerobotics.roadrunner.kinematics.Kinematics
7+
import com.acmerobotics.roadrunner.localization.Localizer
8+
import com.acmerobotics.roadrunner.util.Angle
9+
import java.lang.Math.PI
10+
11+
/**
12+
* This class provides the basic functionality of a swerve drive using [SwerveKinematics].
13+
*
14+
* @param kV velocity feedforward
15+
* @param kA acceleration feedforward
16+
* @param kStatic additive constant feedforward
17+
* @param trackWidth lateral distance between pairs of wheels on different sides of the robot
18+
* @param wheelBase distance between pairs of wheels on the same side of the robot
19+
*/
20+
abstract class SwerveDrive @JvmOverloads constructor(
21+
private val kV: Double,
22+
private val kA: Double,
23+
private val kStatic: Double,
24+
private val trackWidth: Double,
25+
private val wheelBase: Double = trackWidth
26+
) : Drive() {
27+
28+
/**
29+
* Default localizer for swerve drives based on the drive encoder positions, module orientations, and (optionally) a
30+
* heading sensor.
31+
*
32+
* @param drive drive
33+
* @param useExternalHeading use external heading provided by an external sensor (e.g., IMU, gyroscope)
34+
*/
35+
class SwerveLocalizer @JvmOverloads constructor(
36+
private val drive: SwerveDrive,
37+
private val useExternalHeading: Boolean = true
38+
) : Localizer {
39+
private var _poseEstimate = Pose2d()
40+
override var poseEstimate: Pose2d
41+
get() = _poseEstimate
42+
set(value) {
43+
lastWheelPositions = emptyList()
44+
lastExtHeading = Double.NaN
45+
if (useExternalHeading) drive.externalHeading = value.heading
46+
_poseEstimate = value
47+
}
48+
override var poseVelocity: Pose2d? = null
49+
private set
50+
private var lastWheelPositions = emptyList<Double>()
51+
private var lastExtHeading = Double.NaN
52+
53+
override fun update() {
54+
val wheelPositions = drive.getWheelPositions()
55+
val moduleOrientations = drive.getModuleOrientations()
56+
val extHeading = if (useExternalHeading) drive.externalHeading else Double.NaN
57+
if (lastWheelPositions.isNotEmpty()) {
58+
val wheelDeltas = wheelPositions
59+
.zip(lastWheelPositions)
60+
.map { it.first - it.second }
61+
val robotPoseDelta = SwerveKinematics.wheelToRobotVelocities(
62+
wheelDeltas,
63+
moduleOrientations,
64+
drive.wheelBase,
65+
drive.trackWidth
66+
)
67+
val finalHeadingDelta = if (useExternalHeading) {
68+
Angle.normDelta(extHeading - lastExtHeading)
69+
} else {
70+
robotPoseDelta.heading
71+
}
72+
_poseEstimate = Kinematics.relativeOdometryUpdate(
73+
_poseEstimate,
74+
Pose2d(robotPoseDelta.vec(), finalHeadingDelta)
75+
)
76+
}
77+
78+
val wheelVelocities = drive.getWheelVelocities()
79+
val extHeadingVel = drive.getExternalHeadingVelocity()
80+
if (wheelVelocities != null) {
81+
poseVelocity = SwerveKinematics.wheelToRobotVelocities(
82+
wheelVelocities,
83+
moduleOrientations,
84+
drive.wheelBase,
85+
drive.trackWidth
86+
)
87+
if (useExternalHeading && extHeadingVel != null) {
88+
poseVelocity = Pose2d(poseVelocity!!.vec(), extHeadingVel)
89+
}
90+
}
91+
92+
lastWheelPositions = wheelPositions
93+
lastExtHeading = extHeading
94+
}
95+
}
96+
97+
override var localizer: Localizer = SwerveLocalizer(this)
98+
99+
override fun setDriveSignal(driveSignal: DriveSignal) {
100+
val velocities = SwerveKinematics.robotToWheelVelocities(
101+
driveSignal.vel,
102+
trackWidth,
103+
wheelBase
104+
)
105+
val accelerations = SwerveKinematics.robotToWheelAccelerations(
106+
driveSignal.vel,
107+
driveSignal.accel,
108+
trackWidth,
109+
wheelBase
110+
)
111+
val powers = Kinematics.calculateMotorFeedforward(velocities, accelerations, kV, kA, kStatic)
112+
val orientations = SwerveKinematics.robotToModuleOrientations(
113+
driveSignal.vel,
114+
trackWidth,
115+
wheelBase
116+
)
117+
setMotorPowers(powers[0], powers[1], powers[2], powers[3])
118+
setModuleOrientations(orientations[0], orientations[1], orientations[2], orientations[3])
119+
}
120+
121+
override fun setDrivePower(drivePower: Pose2d) {
122+
val avg = (trackWidth + wheelBase) / 2.0
123+
val powers = SwerveKinematics.robotToWheelVelocities(drivePower, trackWidth / avg, wheelBase / avg)
124+
val orientations = SwerveKinematics.robotToModuleOrientations(drivePower, trackWidth / avg, wheelBase / avg)
125+
setMotorPowers(powers[0], powers[1], powers[2], powers[3])
126+
setModuleOrientations(orientations[0] - PI / 2, orientations[1] - PI / 2, orientations[2] - PI / 2, orientations[3] - PI / 2)
127+
}
128+
129+
/**
130+
* Sets the following motor powers (normalized voltages). All arguments are on the interval `[-1.0, 1.0]`.
131+
*/
132+
abstract fun setMotorPowers(frontLeft: Double, rearLeft: Double, rearRight: Double, frontRight: Double)
133+
134+
/**
135+
* Sets the module orientations. All values are in radians.
136+
*/
137+
abstract fun setModuleOrientations(frontLeft: Double, rearLeft: Double, rearRight: Double, frontRight: Double)
138+
139+
/**
140+
* Returns the positions of the wheels in linear distance units. Positions should exactly match the ordering in
141+
* [setMotorPowers].
142+
*/
143+
abstract fun getWheelPositions(): List<Double>
144+
145+
/**
146+
* Returns the velocities of the wheels in linear distance units. Positions should exactly match the ordering in
147+
* [setMotorPowers].
148+
*/
149+
open fun getWheelVelocities(): List<Double>? = null
150+
151+
/**
152+
* Returns the current module orientations in radians. Orientations should exactly match the order in
153+
* [setModuleOrientations].
154+
*/
155+
abstract fun getModuleOrientations(): List<Double>
156+
}

0 commit comments

Comments
 (0)