PathPlannerTrajectory: Robot Travels 3x too far

We created a simple Command to drive 1 meter using PathPlannerTrajectory and DrivetrainSubsystem.followTrajectoryCommand. The robot always travels 3x the desired distance and (if supplied) rotates 3x as far, too.

Any suggestions, please?

are you sure that all of your gear ratios and stuff are correct? this sounds like an issue where the encoder values are being modified to distance incorrectly. are you accedentally converting meters to meters using a feet to meters command? that seems like around a 3x change. did you just multiply by the wheel diameter and forget to multiply by pi as well? just listing things that could cause about that much error


Can you share your code? I had the same problem and I fixed it.

1 Like

Are you in meters or feet?

1 Like

Some good suggestions, but I checked most of them. The error is exactly 3x and not pi. I did find an error in the gear ratios, but it was the difference between 8.33:1 and 8.31:1. That error is about 1/8" over 1 meter.

We believe everything is in meters. A mixup there would result in an error factor of 3.28 (1 meter / 1 foot), not that pesky factor of exactly 3.

Thanks for the rapid responses!

Michael A Goldsmith
Lead Mentor
Lake Monsters 2635

// Command creation

PathPlannerTrajectory traj = PathPlanner.generatePath(
    new PathConstraints(0.5, 0.1), 
    new PathPoint(Translation2d(0, 0), Rotation2d.fromRadians(0), Rotation2d.fromRadians(0)),
    new PathPoint(Translation2d(1.0, 0.0), Rotation2d.fromRadians(0), Rotation2d.fromRadians(0))
m_driveStraightTestCommand = m_driveTrainSubsystem.followTrajectoryCommand(traj, true);

// In SwerveModule Constructor:


// In Constants

public static final double kWheelDiameterMeters = util.inchesToMeters(4.0);
public static final double kDriveEncoderDistancePerPulse =
    (kWheelDiameterMeters * Math.PI) * (1.0 / (60.0 / 15.0) / (18.0 / 26.0) / (42.0 / 14.0));

if its exactly 3 it seems like the easy workaround would be to just divide all of the poses in the path by 3 and just run if from there. the fact that your robot is rotating 3x as much as well makes me think that it is pose related as the gyro shouldn’t be going through the same issues with the numbers being off as the encoders.

do not do this.

It is never okay to “fudge” the units with a scalar unless you know exactly what role that scalar is playing in the dynamics. For example, an empirical trackwidth is fine in place of a theoretical one, because it’s clear what the factor is doing. “Multiply by 3 to make the distances work” is not okay; it’s not clear what that’s fixing and it introduces a huge amount of tech debt that will kill you later.



Double check this line

(1.0 / (60.0 / 15.0) / (18.0 / 26.0) / (42.0 / 14.0));
and see if you’re off by a factor of 3 in one of these ratios, that seems way more likely.

Most path following (even for holonomic rotation) is going to be dominated by the feedforward terms, so unless you have a crazy high P gain, it’s most likely the rotation and translation issues have the same underlying cause.

1 Like

Post the distance traveled by the wheels to Smartdashboard from the drive subsystem periodic (so that it updates while disabled). Then turn each drive wheel exactly one rotation and see how far the robot thinks it went.

That’ll let you know if you encoder units are off.

Even better: break that line up into self-documenting constant declarations and compose them into the final conversion ratio. It’s very very hard to debug otherwise.

1 Like

And a nice side benefit, if one of the constants you declare is encoderTicksPerWheelRotation, that is much easier to manually check by turning a wheel while it’s disabled.

1 Like

Also, don’t calculate the wheel circumference, measure it. An error of 1mm in diameter measurement is 3.14 mm of error in the circumference. My method is wrap the wheel in painters tape, then cut it off and measure the linear length. This won’t fix your x3 problem obviously, but will help your 5% problem after you fix your x3 problem.

Thanks for all the suggestions. We’ll try them tonight in the lab. Specific comments:

I agree that fudging the constants without understanding the “why” is a new problem waiting to pop out at competition :slight_smile:

1.0 / (60.0 / 15.0) / (18.0 / 26.0) / (42.0 / 14.0)) evaluates to 1/8.31 which is exactly the SDS-specified gear ratio for our modules. This is what I corrected from the original that used the wrong geartrain (1/8.33). However, that’s only an error of about 3mm over 1 meter of travel.

Is it possible the error is in followTrajectoryCommand? @JaiCodes : what problem did you fix, please?

Unlike the translation, the rotation was not exactly 3x. But I found a cause for that. The module locations were specified as the corners of the robot frame instead of where the wheels touch the ground. I believe that this error would cause a slight overrotation (which, combined with the 3x, is what we are seeing). If the motion is calculated based on the module driving in a circle that is larger than actual, then the module should travel an arc length that is larger than needed.

Also good suggestions that I will follow up on: measuring actual ticks/rev and measuring wheel circumference. I like the painters tape idea! We usually roll out a spare wheel on carpet.


I had my robot go 2x everything. Turns out I had my encoder pulses per full revolution that was double then it was supposed to be.

Could this be related:

There’s only 1 constant in the code (that I can find) that describes the relationship between the module and distance travelled: kDriveEncoderDistancePerPulse. But that only captures the ratio of distance travelled to motor rotations. The actual encoder (SparkMAX plus NEO 500) produces 42 counts/rev of the motor. So, it is not clear to me why we’re not off by a factor of 42! I see no place where the 42 CPR is ever used.

In further reading of the REV docs, should we have called setCountsPerRevolution(42) on the RelativeEncoder somewhere? I don’t see a call in the code.

I just noticed: there is NO setCountsPerRevolution in the REV docs. Only a getCountsPerRevolution. I am now more confused than ever…

REV scales encoder data from 0 to 1 based off the counts per revolution given in the .getEncoder() method. By default it assumes that .getEncoder() is refering to a hall encoder on a Neo. You do not need to take into account the ticks per rotation when using a spark max because it will always be effectively 1.

1 Like