acmerobotics / road-runner-quickstart

FTC quickstart for https://github.com/acmerobotics/road-runner
BSD 3-Clause Clear License
198 stars 1.14k forks source link

Cannot strafe at a diagonal or to a heading #359

Closed nash-pillai closed 9 months ago

nash-pillai commented 9 months ago

RR FTC Version

0.1.11

Observed Behavior

image image Strafing while turning and strafing at a diagonal goes in the wrong direction, but the robot knows where it is

The code we are running:

drive.actionBuilder(beginPose)
.strafeToSplineHeading(Vector2d(12.0, 38.0), -TAU / 4)
.build()
drive.actionBuilder(beginPose)
.strafeTo(Vector2d(17.0, 38.0))
.build()

Tuning Files

MecanumDrive.java: ```java package org.firstinspires.ftc.teamcode; import androidx.annotation.NonNull; import com.acmerobotics.dashboard.canvas.Canvas; import com.acmerobotics.dashboard.config.Config; import com.acmerobotics.dashboard.telemetry.TelemetryPacket; import com.acmerobotics.roadrunner.AccelConstraint; import com.acmerobotics.roadrunner.Action; import com.acmerobotics.roadrunner.Actions; import com.acmerobotics.roadrunner.AngularVelConstraint; import com.acmerobotics.roadrunner.DualNum; import com.acmerobotics.roadrunner.HolonomicController; import com.acmerobotics.roadrunner.MecanumKinematics; import com.acmerobotics.roadrunner.MinVelConstraint; import com.acmerobotics.roadrunner.MotorFeedforward; import com.acmerobotics.roadrunner.Pose2d; import com.acmerobotics.roadrunner.Pose2dDual; import com.acmerobotics.roadrunner.PoseVelocity2d; import com.acmerobotics.roadrunner.PoseVelocity2dDual; import com.acmerobotics.roadrunner.ProfileAccelConstraint; import com.acmerobotics.roadrunner.Rotation2d; import com.acmerobotics.roadrunner.Time; import com.acmerobotics.roadrunner.TimeTrajectory; import com.acmerobotics.roadrunner.TimeTurn; import com.acmerobotics.roadrunner.TrajectoryActionBuilder; import com.acmerobotics.roadrunner.TurnConstraints; import com.acmerobotics.roadrunner.Twist2dDual; import com.acmerobotics.roadrunner.Vector2d; import com.acmerobotics.roadrunner.VelConstraint; import com.acmerobotics.roadrunner.ftc.DownsampledWriter; import com.acmerobotics.roadrunner.ftc.Encoder; import com.acmerobotics.roadrunner.ftc.FlightRecorder; import com.acmerobotics.roadrunner.ftc.LynxFirmware; import com.acmerobotics.roadrunner.ftc.OverflowEncoder; import com.acmerobotics.roadrunner.ftc.PositionVelocityPair; import com.acmerobotics.roadrunner.ftc.RawEncoder; import com.qualcomm.hardware.lynx.LynxModule; import com.qualcomm.hardware.rev.RevHubOrientationOnRobot; import com.qualcomm.robotcore.hardware.DcMotor; import com.qualcomm.robotcore.hardware.DcMotorEx; import com.qualcomm.robotcore.hardware.DcMotorSimple; import com.qualcomm.robotcore.hardware.HardwareMap; import com.qualcomm.robotcore.hardware.IMU; import com.qualcomm.robotcore.hardware.VoltageSensor; import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit; import org.firstinspires.ftc.teamcode.messages.DriveCommandMessage; import org.firstinspires.ftc.teamcode.messages.MecanumCommandMessage; import org.firstinspires.ftc.teamcode.messages.MecanumEncodersMessage; import org.firstinspires.ftc.teamcode.messages.PoseMessage; import java.util.Arrays; import java.util.LinkedList; import java.util.List; @Config public final class MecanumDrive { public static class Params { // IMU orientation // TODO: fill in these values based on // see https://ftc-docs.firstinspires.org/en/latest/programming_resources/imu/imu.html?highlight=imu#physical-hub-mounting public RevHubOrientationOnRobot.LogoFacingDirection logoFacingDirection = RevHubOrientationOnRobot.LogoFacingDirection.UP; public RevHubOrientationOnRobot.UsbFacingDirection usbFacingDirection = RevHubOrientationOnRobot.UsbFacingDirection.RIGHT; // drive model parameters public double inPerTick = 12 * 5 * 2 / 39820.0; public double lateralInPerTick = 0.00018869762188191035; //inPerTick; public double trackWidthTicks = 4432.299; // feedforward parameters (in tick units) public double kS = 0.9589658474985132; public double kV = 0.0005588422234744558; public double kA = 0.000001; // path profile parameters (in inches) public double maxWheelVel = 40; public double minProfileAccel = -20; public double maxProfileAccel = 20; // turn profile parameters (in radians) public double maxAngVel = Math.PI; // shared with path public double maxAngAccel = Math.PI; // path controller gains public double axialGain = 0.0; public double lateralGain = 0.0; public double headingGain = 4.32; // shared with turn public double axialVelGain = 0.0; public double lateralVelGain = 0.0; public double headingVelGain = 0.59; // shared with turn } public static Params PARAMS = new Params(); public final MecanumKinematics kinematics = new MecanumKinematics( PARAMS.inPerTick * PARAMS.trackWidthTicks, PARAMS.inPerTick / PARAMS.lateralInPerTick); public final TurnConstraints defaultTurnConstraints = new TurnConstraints( PARAMS.maxAngVel, -PARAMS.maxAngAccel, PARAMS.maxAngAccel); public final VelConstraint defaultVelConstraint = new MinVelConstraint(Arrays.asList( kinematics.new WheelVelConstraint(PARAMS.maxWheelVel), new AngularVelConstraint(PARAMS.maxAngVel) )); public final AccelConstraint defaultAccelConstraint = new ProfileAccelConstraint(PARAMS.minProfileAccel, PARAMS.maxProfileAccel); public final DcMotorEx leftFront, leftBack, rightBack, rightFront; public final VoltageSensor voltageSensor; public final IMU imu; public final Localizer localizer; public Pose2d pose; private final LinkedList poseHistory = new LinkedList<>(); private final DownsampledWriter estimatedPoseWriter = new DownsampledWriter("ESTIMATED_POSE", 50_000_000); private final DownsampledWriter targetPoseWriter = new DownsampledWriter("TARGET_POSE", 50_000_000); private final DownsampledWriter driveCommandWriter = new DownsampledWriter("DRIVE_COMMAND", 50_000_000); private final DownsampledWriter mecanumCommandWriter = new DownsampledWriter("MECANUM_COMMAND", 50_000_000); public class DriveLocalizer implements Localizer { public final Encoder leftFront, leftBack, rightBack, rightFront; private int lastLeftFrontPos, lastLeftBackPos, lastRightBackPos, lastRightFrontPos; private Rotation2d lastHeading; public DriveLocalizer() { leftFront = new OverflowEncoder(new RawEncoder(MecanumDrive.this.leftFront)); leftBack = new OverflowEncoder(new RawEncoder(MecanumDrive.this.leftBack)); rightBack = new OverflowEncoder(new RawEncoder(MecanumDrive.this.rightBack)); rightFront = new OverflowEncoder(new RawEncoder(MecanumDrive.this.rightFront)); // TODO: reverse encoders if needed leftBack.setDirection(DcMotorSimple.Direction.REVERSE); lastLeftFrontPos = leftFront.getPositionAndVelocity().position; lastLeftBackPos = leftBack.getPositionAndVelocity().position; lastRightBackPos = rightBack.getPositionAndVelocity().position; lastRightFrontPos = rightFront.getPositionAndVelocity().position; lastHeading = Rotation2d.exp(imu.getRobotYawPitchRollAngles().getYaw(AngleUnit.RADIANS)); } @Override public Twist2dDual
ThreeDeadWheelLocalizer.java: ```java package org.firstinspires.ftc.teamcode; import com.acmerobotics.dashboard.config.Config; import com.acmerobotics.roadrunner.DualNum; import com.acmerobotics.roadrunner.Time; import com.acmerobotics.roadrunner.Twist2dDual; import com.acmerobotics.roadrunner.Vector2dDual; import com.acmerobotics.roadrunner.ftc.Encoder; import com.acmerobotics.roadrunner.ftc.FlightRecorder; import com.acmerobotics.roadrunner.ftc.OverflowEncoder; import com.acmerobotics.roadrunner.ftc.PositionVelocityPair; import com.acmerobotics.roadrunner.ftc.RawEncoder; import com.qualcomm.robotcore.hardware.DcMotorEx; import com.qualcomm.robotcore.hardware.DcMotorSimple; import com.qualcomm.robotcore.hardware.HardwareMap; @Config public final class ThreeDeadWheelLocalizer implements Localizer { public static class Params { public double par0YTicks = -962.0; // y position of the first parallel encoder (in tick units) public double par1YTicks = 912.0; // y position of the second parallel encoder (in tick units) public double perpXTicks = -2133.0; // x position of the perpendicular encoder (in tick units) } public static Params PARAMS = new Params(); public final Encoder par0, par1, perp; public final double inPerTick; private int lastPar0Pos, lastPar1Pos, lastPerpPos; public ThreeDeadWheelLocalizer(HardwareMap hardwareMap, double inPerTick) { // TODO: make sure your config has **motors** with these names (or change them) // the encoders should be plugged into the slot matching the named motor // see https://ftc-docs.firstinspires.org/en/latest/hardware_and_software_configuration/configuring/index.html par0 = new OverflowEncoder(new RawEncoder(hardwareMap.get(DcMotorEx.class, "fl"))); par1 = new OverflowEncoder(new RawEncoder(hardwareMap.get(DcMotorEx.class, "bl"))); perp = new OverflowEncoder(new RawEncoder(hardwareMap.get(DcMotorEx.class, "fr"))); // TODO: reverse encoder directions if needed par0.setDirection(DcMotorSimple.Direction.REVERSE); perp.setDirection(DcMotorSimple.Direction.REVERSE); lastPar0Pos = par0.getPositionAndVelocity().position; lastPar1Pos = par1.getPositionAndVelocity().position; lastPerpPos = perp.getPositionAndVelocity().position; this.inPerTick = inPerTick; FlightRecorder.write("THREE_DEAD_WHEEL_PARAMS", PARAMS); } public Twist2dDual

Robot Logs

No response

rbrott commented 9 months ago

Does the same thing happen if the velocity is much lower? Can you link a robot log?

nash-pillai commented 9 months ago

It continued to happen even when we lowered to:

 public double maxWheelVel = 20;
 public double minProfileAccel = -10;
 public double maxProfileAccel = 10;

I can send robot logs tomorrow

nash-pillai commented 9 months ago

robotControllerLog.txt 2024_01_28__17_30_28_038__MenuAuto.log

I think these are the files you are looking for. Let me know if you want something else

I had to stop the opmode in the middle because it would have hit the edge of the field. If you need it to run all the way, we can remove the trusses and run it again on a clear field

rbrott commented 9 months ago

Yup, the second one is the log I need, thanks. Here's a summary of its contents

THREE_DEAD_WHEEL_PARAMS
  par0YTicks: -962.0
  par1YTicks: 912.0
  perpXTicks: -2133.0

MECANUM_PARAMS
  axialGain: 0.0
  axialVelGain: 0.0
  headingGain: 4.32
  headingVelGain: 0.59
  inPerTick: 0.0030135610246107484
  kA: 1e-06
  kS: 0.9589658474985132
  kV: 0.0005588422234744558
  lateralGain: 0.0
  lateralInPerTick: 0.00018869762188191035
  lateralVelGain: 0.0
  logoFacingDirection: UP
  maxAngAccel: 3.141592653589793
  maxAngVel: 3.141592653589793
  maxProfileAccel: 20.0
  maxWheelVel: 40.0
  minProfileAccel: -20.0
  trackWidthTicks: 4432.299
  usbFacingDirection: RIGHT

Channel                Messages         Hz
TARGET_POSE                  61      20.01
ESTIMATED_POSE               62      20.23
DRIVE_COMMAND                62      20.26
MECANUM_COMMAND              62      20.92

Right off the bat, lateralInPerTick seems very suspicious. It should be at most like 25% less than inPerTick and not an order of magnitude off. Can you try tuning that again and sharing the results here (including the JSON)?

nash-pillai commented 9 months ago

Thank you so much! I'm not how we messed it up in the first place, but re-tuning lateralInPerTick fixed our problems