Swerve motors not aligning because one motor affects the other 3

I tried writing a method to make the swerve wheels align in a certain direction when the swerve is initialized. I calculated the offset of the CANcoders and now three of the wheels align properly. The problem is that when I try to align the fourth (by setting it to inverted in the constants class) and run the code again, all of the swerve wheels change orientation and none of them align.

Somehow it seems that swerve motors are affecting other swerve motors?

I’m very confused and any help would be greatly appreciated.

Here are some snippets of my code if that helps…



I can always provide more.

1 Like

Can you share all of your code, preferably as a link to a GitHub repo?

Can you explain what your resetToAbsoluteAngle() method is supposed to do?

reset method basically aligns all the wheels at the start of the match to the “forward direction”, which I defined as the 0 of the CANcoders by subtracting the offset. It is the method I was just talking about.

@gdefender

Can you explain how you calculated the offsets? I see you have the parameter named steerAngleOffsetRad, which implies that it’s in radians, but CANCoder offsets are configured in rotations. Make sure you have the units right, your variable name may just be misleading.

I think your intention with resetToAbsoluteAngle() was probably to read the position of the CANCoder and tell the motor it is currently at that position. Do you agree?

If so, you need to read the position from the CANCoder, calculate the corresponding motor position with the gear ratio (or configure the gear ratio with SensorToMechanismRatio) and then call setPosition(double). You’re currently trying to command the motor to a position, but this method should be telling the motor it’s position based on the CANCoder’s position.

@gdefender

I forgot to change the name of the steerangleoffset. It is not in radians. I didn’t calculate it, but lined up all the wheels in the forward direction, and set that as the CANcoder 0 by using the values read from the CANcoders and putting their negative into the Magnetic Encoder class.

The reset method takes the value, read by the CANcoder in rotations, and passes it in to the motor divided by the gear ratio to get by how much we want the motor to rotate, using the set
method.

From what I understand, the set method for motors gives the motors a rotation to rotate by, not a position to “go to”.

I was printing values to the Smart Dashboard just for debugging purposes. And some of my code is commented out also for the same reason.

What do you mean by telling a motor “its position?”

Also how does me changing one motor to inverted change the others?

Our swerve also uses TalonFX, though for many reasons we chose to stay with Phoenix 5, rather than new Phoenix 6.
We also use CANCoders for this.
The idea was - we use CANCoders ONLY to determine the absolute position of the wheel when the robot is turned ON. Then we reset the TalonFX built-in encoders to the values that they should have, considering raw-units-per-degree. For instance, if we have 150 ticks per degree, and CANCoder determined that the wheel is at +3 degrees past absolute X-axis direction, we would set internal TalonFX encoder to 450 (there is more math involved, since the CANCoder absolue 0 is not really the absolute X-axis direction, but that’s trivial).
After that basically the internal TalonFX encoders have 0 matching the absolute X-axis, so all wheel turns are done via hardware PID. Some of that math is here, and the rest is in the same class.

Isn’t Phoenix5 deprecated and marked for removal?

I would like to avoid using relative encoders unless there is a solid reason to do so.

Just asking though, if you have both the CANcoder and the internal encoder inside of the motor, couldn’t you fuse the sensors for a more accurate reading?

Yes, Phoenix 5 is marked for removal in 2025. But it still works in 2024.
If you’re annoyed by the VSCode crossing the deprecated statements, use

@SuppressWarnings({ “removal” })

annotation :slight_smile:

Not sure what you mean by “fusing sensors”. We use absolute encoder to calibrate the relative one. Periodic recalibration technically can take place, however, the drift on those is minimal, and you can use hardware PID, which is MUCH more forgiving for somewhat suboptimal PID values (because it corrects every 1ms instead of every 20).
Because of the 1ms Hardware PID loop (which you cannot do if a motor and encoder are not directly connected - meaning CANCoder cannot do that), ANY wheel turns in our case take less than 0.1s time, which makes the actual swerve drive VERY smoothly, and handle sudden trajectory changes well.

I thought that by setting all the motors to the 0 position, I wouldn’t need to tell the motors their position.

But if I did want to just tell the relative encoder where its absolute 0 is, how would I do that?

Is there a specific method to do this?

I think other posters explained this, but the CANCoder is an absolute encoder, that can read the position of the wheel, even between power cycles.
The encoder built in to the motor, on the other hand, is a relative encoder. It starts at zero every time you power cycle it. Besides, it’s geared down, so one rotation of the wheel takes more than one rotation of the motor.
Many teams (including mine) want to take advantage of the Talon FX smart motor controller to control the steering. An easy way to do this is to read the absolute position at startup from the CANCoder, and then seed that position to the motor, and then use the motor’s encoder from then on.

As I said above,

Yes, you can do this, but it’s a Phoenix Pro feature and requires a license.

This is incorrect. Using PositionVoltage commands the motor to go to a specific position, in rotations.

Finally, if you are using CANCoders, Talon FX motor controllers (Falcon 500 or Kraken), and a Pigeon 2 IMU, I highly recommend you use CTRE’s swerve project generator instead of starting from scratch.

How would it know what the specific position is if it is a relative encoder?

So how does the odometry work? Isn’t it based on the position being the amount of rotations moved by the wheel to calculate the displacement in short intervals?

The motor has a relative encoder, but you can set it’s position by calling setPosition(double).

For example, let’s say you start up your robot and the wheel is pointed 90° to the left:

  1. You read the wheel’s absolute position from the CANCoder by calling getAbsolutePosition() and it returns 0.25 rotations.
  2. Assuming you did not configure the SensorToMechanismRatio, you convert 0.25 rotations to the number of motor rotations. You didn’t say what swerve module you have, so let’s assume it’s an SDS Mk4i with a ratio of 150/7:1. Your calculation will be 0.25 CANCoder rotations * (150/7) gear ratio = 5.357 motor rotations.
    var motorPosition = cancoderRotations * gearRatio;
  3. You “seed” your motor’s relative encoder by calling setPosition with 5.357
    setPosition(motorPosition)
  4. Now that you’ve seeded your motor’s starting point, everything is relative from here. If you spin the motor, you can apply the gear ratio in reverse to calculate the wheel’s absolute position.

BTW, I recommend setting SensorToMechanismRatio in the motor’s configuration so you don’t have to deal with the gear ratio conversion in your code.

You are mostly correct. Odometry is based on the position of the drive wheels, but in meters, not in rotations.
You kind of changed lanes here. We’ve been talking about the azimuth (steering) of the swerve modules and I think you’re asking about the drive distance.
For odometry, you will need to provide SwerveModulePosition objects. You’ll have to provide the drive distance in meters and the azimuth angle as a Rotation2d.

Thank you so much for your help! I was really confused with Phoenix 6 :frowning:

Last question: for phoenix 6, the CANcoder has a setting to set it to Clockwise vs Counterclockwise (which I’m assuming is similar to inverting a motor). How do I know what to set it to? Last time I set it to the same direction the motor is set to, but I’m not sure that’s correct.

The CANCoder direction should match the motor. You should set both so when you turn the wheel CCW the value gets larger.

:smiling_face: thank you so much!