Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encoders! #9

Merged
merged 36 commits into from
Jan 21, 2022
Merged

Encoders! #9

merged 36 commits into from
Jan 21, 2022

Conversation

@ky28059 ky28059 marked this pull request as draft November 14, 2021 02:20
public static final int bLeftMotorPort = 14;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra line?

@ky28059
Copy link
Member Author

ky28059 commented Nov 20, 2021

Resources for the odometry portion of this PR

@ky28059 ky28059 marked this pull request as ready for review December 2, 2021 02:33
@@ -42,6 +43,7 @@

// Controllers
private XboxController controlXbox = new XboxController(0);
private JoystickButton xboxAButton = new JoystickButton(controlXbox, XboxController.Button.kA.value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not use the controller directly, is that not possible

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.wpilib.org/en/stable/docs/software/commandbased/binding-commands-to-triggers.html
https://first.wpi.edu/FRC/roborio/beta/docs/java/edu/wpi/first/wpilibj/XboxController.html#getAButtonPressed--
I think the JoystickButton class just wraps the XboxController's button methods so you can do event based "when pressed" stuff instead of having to check periodically whether the button was pressed using getAButtonPressed() and then going from there. Je crois que setting up button bindings via JoystickButton is the reason for the controllerBindings method.

// for the third.
tankSubsystem.zeroPosition();

tankSubsystem.setCarDrivePowers(0.5, 0.0);
}

@Override
public boolean isFinished() {
return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

big lemon

@@ -20,6 +30,14 @@
private final WPI_TalonSRX rightMain;
private final WPI_TalonSRX rightFollow;

private final AHRS ahrs;

private final DifferentialDrivePoseEstimator odometry;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in another comment I mentioned "all localization stuff"... we should take all the localization logic and put it in its own class and run it on its own thread. logically and from my experience, dead reckoning is more accurate the less time each loop is -- no matter your odometry method (e.g. circular), you're making an assumption about what happens between each tick, so reducing the tick interval reduces the fudginess of your guess.

if we use the periodic() method, the commandbased system actively waits until an interval is hit before calling periodics, it's something like 20ms by default? i can't remember

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I second the localization system approach.

rightMain.setSelectedSensorPosition(0);

// https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj/estimator/DifferentialDrivePoseEstimator.html#resetPosition(edu.wpi.first.wpilibj.geometry.Pose2d,edu.wpi.first.wpilibj.geometry.Rotation2d)
odometry.resetPosition(new Pose2d(), Rotation2d.fromDegrees(ahrs.getAngle()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not reset the gyro

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj/estimator/DifferentialDrivePoseEstimator.html#resetPosition(edu.wpi.first.wpilibj.geometry.Pose2d,edu.wpi.first.wpilibj.geometry.Rotation2d)

The gyroscope angle does not need to be reset here on the user's robot code. The library automatically takes care of offsetting the gyro angle.

And the angle was reset to 0 during testing so it seems to work.

@@ -70,7 +72,7 @@ public RobotContainer() {
tankSubsystem = new TankSubsystem();

// Instantiate commands
tankCommand = new DriveTankCommand(tankSubsystem, 0, 0);
tankCommand = new DriveTankCommand(tankSubsystem, 12);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this "12" related to the "12 inches" comment on L117?
I don't love magic numbers, but a comment here would be helpful if you don't want to make this a well-named constant.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the idea was for it to drive forward "12 inches" in autonomous to then measure that distance and check if the constant was working properly, but the different speeds on each side prevented that from really happening. I'll add a comment to this.


private final DifferentialDrivePoseEstimator odometry;

public static final double ENCODER_TICKS_TO_INCHES = 32 / 142.40;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fortunately you have the .40 in this equation, otherwise the result is 0. Ask me how I know and how many hours I spent hunting for this bug.

If you have integer division, you get an integer result.

double leftDistance = leftMain.getSelectedSensorPosition() * ENCODER_TICKS_TO_INCHES;
double rightDistance = rightMain.getSelectedSensorPosition() * ENCODER_TICKS_TO_INCHES;

odometry.update(gyroAngle, wheelVelocities, leftDistance, rightDistance);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this interface isn't great. We're passing in a DifferentialDriveWheelSpeeds object and then separate positions for left and right wheels. These two interfaces should match. I know this isn't your interface, but keep this in mind.


odometry.update(gyroAngle, wheelVelocities, leftDistance, rightDistance);

System.out.println(odometry.getEstimatedPosition());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe print out a little text for context in the logs/terminal?

@ky28059
Copy link
Member Author

ky28059 commented Dec 17, 2021

CANSparkMax has a similar API to WPI_TalonSRX, but some notable differences:


image
Followers automatically inherit their leader's inversion (and can invert their leader's inversion if needed).

image
Percent output motor setting is the default.

image
The type of motor (brushed or brushless) must be given in the constructor.

image
Brushless hall encoders can be accessed via getEncoder() (but this is only for temporary testing, as on the build robot we'll use external magencoders)

public void setMotorVoltages(double left, double right) {
// TODO: stub on purpose, see comment in FollowPathCommand
// This doesn't really match our current interface, but neither does the other option;
// Should a state on this subsystem be added to switch between voltage and percent output?
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, instead of tankSubsystem containing states and continually setting the motor controller output in periodic, the motor controllers can be the ones to store the state and methods can just set their values. Instead of

private int leftPower;
private int rightPower;

public void setTankDrivePowers(double leftScale, double rightScale) {
  this.leftPower = leftScale;
  this.rightPower = rightScale;
}

@Override
public void periodic() {
  // These must be in percent output mode
  leftMain.set(leftPower);
  rightMain.set(rightPower);
}

why not have

public void setTankDrivePowers(double leftScale, double rightScale) {
  leftMain.set(leftScale);
  rightMain.set(rightScale);
}

? This would also allow for more flexibility with the output mode, such as

public void setTankDrivePowers(double leftScale, double rightScale) {
  leftMain.set(leftScale);
  rightMain.set(rightScale);
}

public void setTankDriveVoltages(double leftVoltage, double rightVoltage) {
  leftMain.setVoltage(leftVoltage);
  rightMain.setVoltage(rightVoltage);
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allowing voltages would also resolve 1/2 of the current problems with the path following command.

@ky28059
Copy link
Member Author

ky28059 commented Dec 20, 2021

Resources for the pathfinding portion of this PR

@ky28059
Copy link
Member Author

ky28059 commented Dec 20, 2021

It seems like WPILib's path/trajectory things must be in meters (see PathWeaver export unit and all the WPILib trajectory docs) so I will convert the odometry to that as well. It seems like PathWeaver can have its interface be in feet or inches while exporting paths as meters, which should make the forced meterization of everything less painful.

ky28059 and others added 12 commits January 13, 2022 20:04
See comment in FollowPathCommand
These will probably have to be remeasured with the Robot Characterization tool. When testing pathfinding with these, the robot immediately swerves sharply to the left (which it shouldn't be doing). It might have something to do with the right encoder readings being inverted during characterization (left reports 4 meters, right reports -4), but I've tried setting various options in the config to try and fix that to no avail. Will definitely look into it further at some point.
Recharacterized using process described in https://www.chiefdelphi.com/t/the-future-of-pathweaver/393014/82. `Kp` might need to be tuned, as does `ENCODER_TICKS_TO_METERS` most likely.
Also push experimental changes to PID constants and auton testing, which still doesn't quite work
With the new REV version, CANSparkMaxes with the same can ID cannot be created (it throws errors which kill the robot program)
Hopefully these ones work better!
@ky28059
Copy link
Member Author

ky28059 commented Jan 20, 2022

Closing(ish) thoughts

A lot of things made sense when I realized what RamseteCommand and associated WPILib path following things actually did -- given a trajectory, the path following generates a list of left and right velocities for the drivetrain to follow the trajectory. My confusion from the PID oscillation stemmed from the assumption that the PID loop in the command was for position, so an oscillation would surely cause the robot to move back and forth; it never did because the PID loop was for velocity instead (this is why kI and kD were set to 0 in the example; accelerating to a target velocity is a use case for just a P controller). Oscillations due to a high kP were between 1.9m/s and 2.1m/s and not 1.9m and 2.1m so there was no back and forth driving. Furthermore, the confusion on why the robot stopped before the end position when constants were misconfigured can also be explained by realizing that the list of velocities probably ended, with the constants being incorrect leading to the robot not following the velocities properly and ending in an incorrect spot.

A lot of these confusions could probably have been resolved sooner had I read the docs for RamseteController a little more carefully.

Copy link
Contributor

@eggaskin eggaskin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some constants to be fine tuned/changed, but Kevin's computer is not working so first merge.

@eggaskin eggaskin merged commit cf73d04 into develop Jan 21, 2022
@ky28059
Copy link
Member Author

ky28059 commented Jan 21, 2022

Some constants to be fine tuned/changed, but Kevin's computer is not working so first merge.

I don't think constants need to be changed, I just wanted to test shuffleboard (which wasn't cooperating). I think launching it from driver station as opposed to vscode fixed the issue, so the branch can probably be deleted.

@ky28059 ky28059 deleted the encoders branch January 21, 2022 05:53
e3l pushed a commit that referenced this pull request Mar 5, 2022
KEVIN: Closing thoughts. A lot of things made sense when I realized what RamseteCommand and associated WPILib path following things actually did -- given a trajectory, the path following generates a list of left and right velocities for the drivetrain to follow the trajectory. My confusion from the PID oscillation stemmed from the assumption that the PID loop in the command was for position, so an oscillation would surely cause the robot to move back and forth; it never did because the PID loop was for velocity instead (this is why kI and kD were set to 0 in the example; accelerating to a target velocity is a use case for just a P controller). Oscillations due to a high kP were between 1.9m/s and 2.1m/s and not 1.9m and 2.1m so there was no back and forth driving. Furthermore, the confusion on why the robot stopped before the end position when constants were misconfigured can also be explained by realizing that the list of velocities probably ended, with the constants being incorrect leading to the robot not following the velocities properly and ending in an incorrect spot.

A lot of these confusions could probably have been resolved sooner had I read the docs for RamseteController a little more carefully.

* Initial testing

* Add wheel circumference to hopefully calculate distance

* Add position getting/setting, fix constants

* Slow drive speed, zero sensors

* Start odometry threading

* Odometry package and class

This commit will be overridden by using WPI's builtin odometry classes, but I am pushing it to keep it in history.

* Use WPI odometry class

* Small changes

* navX AHRS

* Merge left and right sensor methods to use odometry class

* Change `new Pose2d()` to `getRobotPosition()`

* Measure the constant!

This can be tested and tuned over time

* Bind A button to zero position

* Change constant to inches, remeasure

* Add better documentation

* Remove joysticks from encoder branch

* Readd odometry thread

Wasn't able to test this that much because the drivetrain became possessed by ghosts, but can test next week on the actual drivetrain.

* Talon -> Spark Max

* Implement preliminary path following command

See comment in FollowPathCommand

* Remove states from `tankSubsystem`

* Inches -> meters

* Add secondary constructor to FollowPathCommand

* Fix turning direction; remove CAN ID 0

* Edit to functional drivetrain state

* Measure some constants

Prepared to do https://docs.wpilib.org/en/stable/docs/software/wpilib-tools/robot-characterization/characterization-routine.html#running-the-characterization-routine and measure the rest on Sunday, then perhaps test finally!

* Measure more constants!

These will probably have to be remeasured with the Robot Characterization tool. When testing pathfinding with these, the robot immediately swerves sharply to the left (which it shouldn't be doing). It might have something to do with the right encoder readings being inverted during characterization (left reports 4 meters, right reports -4), but I've tried setting various options in the config to try and fix that to no avail. Will definitely look into it further at some point.

* Revert constants, invert gyro, invert left motors

* Fix inverted gyro error in resetPosition

* New constants!

Recharacterized using process described in https://www.chiefdelphi.com/t/the-future-of-pathweaver/393014/82. `Kp` might need to be tuned, as does `ENCODER_TICKS_TO_METERS` most likely.

* Update WPILib and vendor packages to 2022

Also push experimental changes to PID constants and auton testing, which still doesn't quite work

* Add preliminary shuffleboard code

* Move odometry to `tankSubsystem`

With the new REV version, CANSparkMaxes with the same can ID cannot be created (it throws errors which kill the robot program)

* Measure new constants with sysid

Hopefully these ones work better!

* `Field2d`, new constants

* Cleanup, remove elevator subsystem

* Change angle entry to use a gyro complex widget

Co-authored-by: pinkbluesky <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants