My team is using the CTRE Swerve Generator Code to control our SDS MK4i Kraken Swerve drive. While driving, our robot is encountering significant drift—driving straight forward for 30 feet results in a rotation of about 15 degrees. This still happens when the gyro is removed and we drive robot-oriented. We have calibrated the absolute encoders numerous times, and even when they are perfectly aligned we still get drift.
My first thought was that our turning PIDs are not strong enough, and that they don’t converge to the setpoint quick enough. I have not yet had time to fully retune the PIDs, so that could still be a possibility.
My other thought, after doing research, is that we might need Phoenix Pro (which we don’t have right now). I have read that Phoenix Pro is recommended for CTRE Swerve, and that it might “improve odometry”, and “decrease jitter”. Our swerve generally operates smoothly, but we do have jitter in the turning motors when they are turning. Does CTRE just paywall quality results? Does anyone have other ideas of what the issue could be?
Thanks for any help you can give
100 has also had issues with rotational drift while translating, so I’m curious what other folks say.
I suspect the key issue is simply that, in the simplest manual-driving control scheme, rotation is “uncontrolled” in the sense that there’s nothing in the code that’s trying to keep it the same. We’ve experimented with active rotation control (“sticky heading”), and it does make the drifting problem go away, though it introduces other issues, like complicating the response to impact. I know many other teams have done something similar.
Another issue is that, because of the off-center driveshaft, each steering axis gets a little “push” from the driving axis; if your steering controllers are on the soft side, and all the modules are facing the same way, then this little push can nudge all the wheels off track. Increasing the gain, and using outboard positional control, helps with that. Steering gains can be made shockingly high without oscillating, I guess because the tread/carpet interaction is so sticky. We also tried feeding the drive torque into the steering controller, but it didn’t seem to help as much as just firming up the steering feedback.
For us, since our 2024 center of mass is nowhere near the geometric center, but because the drive motors are all current (i.e. force)-limited the same, there’s a resulting torque, depending on the robot-relative course. It’s most noticeable when changing course fast, with constant heading. We’ve made no attempt to understand or compensate for this effect.
Hope that helps. We do still struggle with the issue.
I would try to divide the problem space into “is it mechanical or control/electrical?” by aligning the modules as best you can (perhaps to the zero point you’ve currently defined), disabling all steering inputs (perhaps even locking the steering mechanism with a clamp), and driving straight forward/backward.
If it still turns, you could fix it with software, but IMO, you’re better off trying to fix a mechanical problem with mechanics wherever you can.
(I know you said you aligned the encoders multiple times, but it doesn’t take much toe-left on the front and toe-right on the rear to cause a turn. I’d want to be sure whether had solid mechanics or not.)
Another thing to look out for is if your controllers have stick drift-try different controllers and see if you have the same problem
A small misalignment of one of the wheels can cause this, especially if you have very grippy treads.
Are you running the wheel speeds open-loop or closed-loop? I think the CTRE generator allows it to be configured both ways, although I’ve never used it. If your wheel speeds are running open-loop, it’s possible that your wheel speeds are slightly different even if the drive motors are all provided the same voltage. Different wheel speeds could contribute to chassis rotation. Are you logging wheel speeds? If so, you could look for persistent offsets while driving straight.
One update:
In the “Constants.java” file generated by Phoenix, there is a method of the “SwerveModuleConstantsFactory” class called “.withFeedbackSource()” By default this is passed “FusedCANCoder”, but the documentation says that “FusedCANCoder” requires Phoenix Pro to work. I changed the input to “RemoteCANCoder”, which the documentation says is the correct option for non-Phoenix Pro users. I cannot test it until Monday, but does anyone know what this parameter would change, and if this modification will help me?
Complex and obtuse functionality like this makes me want to write our own in-house swerve code. I’m getting really tired of being given a vendor’s archaic 40-paremeter factory classes and just having to deal with it as a finicky black box.
As far as I can tell, we are using Voltage closed-loop control with a kP of 3 and everything else at 0. This was what Phoenix Tuner spat out, and it’s seemed to work fine.
When not using Pro, FusedCANcoder
falls back to RemoteCANcoder
automatically, so this change will only remove warnings from the Driver Station.
When the robot has a bit of rotational drift while driving straight, there are typically two possible issues:
- One or more of the modules are not zeroed quite right. Make sure to use any built-in module zero-position locking (such as in Swerve X) or something like a long block of wood to ensure all modules are facing straight forward when you zero the modules during project generation.
- One of the drive motors are running slower than the others (in open-loop control). Typically the drive motor needs to be around 10%+ slower for this to make a difference. This is often caused by a mechanical issue (such as gears needing to be re-greased), but switching to closed-loop control using the SwerveRequest’s DriveRequestType also fixes the issue. Note that the robot will drive differently using closed-loop control, and you will need to tune the drive gains (namely kP, kS, and kV).
Just a note on this, our Java swerve API is source-visible through our Java API docs. On the SwerveDrivetrain
page, if you click on the “SwerveDrivetrain” in public class SwerveDrivetrain
near the top of the page, you will be taken to its source code.
Thanks, this is helpful. I hadn’t realized, but in our drive command we were passing in “OpenLoopVoltage”. Would changing it to “Velocity” solve our issue?
If the issue is with one of the drive motors running slower than the others, yes, that would avoid the issue. However, you will need to tune the drive PID, as I believe the default values are still a kP of 3 and everything else 0, which will not work well for closed-loop VelocityVoltage control.
You can use SysId for this (our SwerveWithPathPlanner example demonstrates how to set up our SysIdSwerveTranslation request), or you can manually tune the gains. For reference, our defaults for the 2025 season will be a kP of around 0.1 and a kV of 0.12.
re: writing your own swerve code, i wholeheartedly recommend this path! the whole point of FRC is learning, and you’ll learn a lot about how the whole system works by making your own. it’s not that complicated, you can start with the WPILib example. imho every line of source code should be part of your build, so you can experiment with it.
re: control types and P values, one of our students just set up positional closed loop (the “PositionVoltage” control type) for steering, which made a big improvement over velocity control using an external encoder: the external encoder is bound to be noisy, slow, and delayed, relative to the internal one, so i’d recommend that. incidentally i’m surprised that a P value of 3 works at all using velocity voltage control: when we tried that, we saw oscillation in the outboard control.
i’d recommend spending a lot of time playing with the various gains in the system (and other systems): don’t treat “sysid” as a black box that spits out numbers, set all the gains to zero and then play with them one at a time. you’ll learn a lot about what various failure modes look like.
Um.
Up to a year ago, the idea of being able to push a couple buttons and have a swerve drive be completely configured for you was insane. Much less swerve drive code that works the way CTRE swerve does.
I’ll go on record as saying we’ve run into a few items that could have been documented better - but in general CTRE does a very good job of documenting their features and functions.
Swerve IS complex. Don’t let anyone tell you otherwise. Before the self-configuring swerve drive, WPI-lib swerve code, and swerve templates, teams were told that pursuing swerve was a multi-year project.
Even now, most teams are told ‘don’t try it if you haven’t done it in off season’.
I’m just saying - I don’t think ‘archaic’ is a good descriptor of CTRE’s swerve code. “Complex” might be accurate, but frankly any decent swerve code, especially one that implements the features they do with timing, odometry etc, is by necessity going to be complex under the hood.
If you have concrete examples of what they can improve, they are pretty darn responsive (as evidenced by their responding to this discussion).
I don’t think ‘obtuse’ and ‘archaic’ are the words that describe what they’ve put together and maintain.
In addition to what everyone else said:
- Plot your (desired and actual) module speeds/angles in advantage scope. If your speeds are significantly off you might want to switch to closed loop
- Bind a button to a “drive straight at constant speed” request, take out joysticks from the equation. I think the default command swerve generated code has it as a DPad button
FWIW I count 97 (21 per module, 13 overall) different points of configuration or calibration in our codebase. Not including anything vision or path-following related.
Getting it down to 40 is impressive.
As a summer project, we are also trying CTRE generated code, and interestingly, we also encountered a rotational drift.
Last season we used the code that we developed in house from scratch, and it actually worked well. However, it was LONG (1500 or so lines for the drivetrain and auto), and was essentially based on the duty cycle rather than velocity PID.
We were hoping to use the library code to see whether that will work. The CTR code is about 3 times smaller. The CTR code seems to drive more “smooth” with less jerk. However, the drift is a bit concerning. In our case we thought (and still think) that it’s related to improper zero. But I am no longer sure about that after reading this thread.
Will do more checking and comparing to our old code. Our “original” last -year code was done with Phoenix 5. But we retrofitted it for Phoenix 6 this summer, leaving the logic intact, and it is working as well. However, we use CANCoders only for the initial zero-positioning, and the rest of the rotation determination is done purely on built-in falcon encoders, once their zero offsets are “remembered”.
Our team may be having the same issue, but we are using the Rev Robotics motors (neo 550 and the neo). Here are the symptoms and troubleshooting data so far:
- After 50 feet of trying to drive straight, the frame is turned more like 30 degrees (yaw, clockwise from above). The centroid of the bot does indeed move straight forward, it’s just a yaw.
- Interesting, all four turning motors agree to turn 30 degrees in synchronized fashion. We don’t think an individual motor is causing the behavior . . . they are all in collusion!
- When we then try to drive (straight) backward 50 feet, the rotation is opposite (30 degree yaw, but counterclockwise from above). It stops in the exact spot and rotation it started.
- When we put the bot on blocks and let the wheels run without load on them, they go straight! (no rotation, no yaw obviously).
- When we try to drive straight with a very high velocity, the yaw is only about 5 degrees (CW) then 5 degrees (CCW) when coming back with high velocity.
- We are driving on thin carpet (similar to competition).
- We’ve adjusted the P and the I constants, but don’t feel like it helps.
We encountered a very similar problem on our CTRE swerve. We found that the best solution is that once the driver stops giving a rotation input store the value of the gyro, then use PID to keep the swerve at that angle. There is even a swerve drive request which does it for you. I think it is called fieldcentricfacingangle.
Here is my step by step idea for a swerve design that would fix this issue. I don’t know how much of this is implemented in CTRE Swerve currently.
Procedure
- The driver inputs a desired velocity and rotational rate. If the driver is not pressing the rotation stick, this is recorded.
- The driver inputs are transformed, mapped, and scaled to a desired robot velocity and robot rotational rate (if applicable)
- If the driver pressed the rotation stick, their wanted rotational velocity is passed on. Otherwise, the previous robot orientation from when the driver last released the joystick is used as a setpoint, and a PID controller is used to keep the robot at that setpoint. This controller is fed the error between the wanted orientation and the current orientation (reported by pose estimation), and returns a velocity. If the driver is not pressing the rotation stick, this returned velocity is passed on.
- The wanted velocity and angular velocity are converted into module speeds and orientations.
- Each module is fed its desired velocity and orientation.
- The drive motors run a feedforward model and a feedback PID controller which is given the error between the wanted velocity and the current velocity and returns a voltage. Their source of feedback is the motor encoder.
- The turn motors run a feedforward model and a feedback PID controller which is given the error between the wanted position and the current position and returns a voltage. Their source of feedback is the absolute encoder on top of each swerve module.
Questions
Should the absolute encoder be used as a constant source of turn feedback, or should the enteral motor encoder be used as a constant source and the absolute encoder only be used for initial alignment?
Should the robot try to maintain orientation when the turn stick isn’t held? Currently we are aligning with the source by running into it. If the robot tried to maintain its orientation, this would not work. Is it worth it anyway? Is there a better way to do this?
Do you have a gyro and AprilTag vision?
If you have both, why not offer (and default?) to a driver aid automatically aligning to the speaker when near it?