Why are my autons so inconsistient?

Howdy, y’all. So my team and I tuned our PID through Lemlib the other day, followed the guide and everything. Now, we’ve moved on to writing autonomous programs, and we’ve been working on one for three days now. We’ll make a change, and it’ll work for a few runs, and then it won’t for some reason. We’ve verified that the odometry pods are okay, and we’ve cleaned the field twice now. So, why are my autos so inconsistient? And, in addendum, how do those of y’all that have super accurate and consistient autos get to that point? do I just need to keep working, should I re-tune PID, or what? Any help would be greatly appreciated. Code for PID Setup is below.

Cheers, y’all!

-Luke

// Sensor Definitions
Rotation lateralTracker(14);
Rotation headingTracker(11);
Imu imu(8);

//LemLib setup

//drivetrain setup
lemlib::Drivetrain drivetrain(&leftMotors, // left motor group
                              &rightMotors, // right motor group
                              12.25, // 12.25 inch track width (Distance between centerline of wheels)
                              lemlib::Omniwheel::NEW_325, // using new 3.25" omnis
                              450, // drivetrain rpm is 450
                              2 // horizontal drift is 2 (for now)
);

// horizontal tracking wheel
lemlib::TrackingWheel horizontal_tracking_wheel(&headingTracker, lemlib::Omniwheel::NEW_2, -6.5); //offsets will change later
// vertical tracking wheel
lemlib::TrackingWheel vertical_tracking_wheel(&lateralTracker, lemlib::Omniwheel::NEW_2, -2.5); //offsets will change later

//configure odom sensors
lemlib::OdomSensors sensors(&vertical_tracking_wheel, // vertical tracking wheel 1, set to null
                            nullptr, // vertical tracking wheel 2, set to nullptr as we are using IMEs
                            &horizontal_tracking_wheel, // horizontal tracking wheel 1
                            nullptr, // horizontal tracking wheel 2, set to nullptr as we don't have a second one
                            &imu // inertial sensor
);

// lateral PID controller (not tuned yet)
lemlib::ControllerSettings lateral_controller(11, // proportional gain (kP)
                                              0, // integral gain (kI)
                                              7, // derivative gain (kD)
                                              1.083, // anti windup
                                              1, // small error range, in inches
                                              200, // small error range timeout, in milliseconds
                                              3, // large error range, in inches
                                              500, // large error range timeout, in milliseconds
                                              70 // maximum acceleration (slew)
);

// angular PID controller (not tuned yet)
lemlib::ControllerSettings angular_controller(2, // proportional gain (kP)
                                              0, // integral gain (kI)
                                              10, // derivative gain (kD)
                                              2.487, // anti windup
                                              5, // small error range, in degrees
                                              200, // small error range timeout, in milliseconds
                                              10, // large error range, in degrees
                                              500, // large error range timeout, in milliseconds
                                              117 // maximum acceleration (slew)
);

Do you have any videos of the auton movements and the code for the autons?

void blueParkLeft()
{

    chassis.setPose(0,0,0);
    hoodPiston.set_value(false);
    chassis.moveToPoint(0,15,2000,{},true);
    firstStage.move_voltage(12000);
    secondStage.move_voltage(12000);
    thirdStage.move_voltage(12000);
    chassis.moveToPoint(-25,43,3000,{.maxSpeed = 70},false);
    littleWillPiston.set_value(true);
    pros::delay(500);
    chassis.moveToPoint(0,12,2000,{.forwards = false});
    chassis.turnToHeading(-90,1000);
    chassis.moveToPose(-19.7,-6,180,4000,{.minSpeed = 90});
    chassis.moveToPoint(-19.7,-19.85,1000,{.maxSpeed = 90 },false);
    pros::delay(1000);
    chassis.moveToPoint(-19.5,-6,1000,{.forwards = false},false);
    littleWillPiston.set_value(false);
    chassis.moveToPoint(-19.5,13,1000,{.forwards = false, .maxSpeed = 100},false);
    hoodPiston.set_value(true);
    firstStage.move_voltage(-12000);
    secondStage.move_voltage(-12000);
    thirdStage.move_voltage(-12000);
    pros::delay(500);
    firstStage.move_voltage(12000);
    secondStage.move_voltage(12000);
    thirdStage.move_voltage(12000);

}

void blueParkRight()
{

    chassis.setPose(0,0,0);
    hoodPiston.set_value(false);
    chassis.moveToPoint(0,15,2000,{},true);
    firstStage.move_voltage(12000);
    secondStage.move_voltage(12000);
    thirdStage.move_voltage(12000);
    chassis.moveToPoint(24,33,3000,{.maxSpeed = 70},false);
    littleWillPiston.set_value(true);
    pros::delay(500);
    chassis.moveToPoint(0,6,2000,{.forwards = false});
    chassis.turnToHeading(90,1000);
    chassis.moveToPose(19.7,-6,180,4000,{.minSpeed = 90});
    chassis.moveToPoint(19.7,-19.85,1000,{.maxSpeed = 90 },false);
    pros::delay(1000);
    chassis.moveToPoint(19.5,-6,1000,{.forwards = false},false);
    littleWillPiston.set_value(false);
    chassis.moveToPoint(19.5,13,1000,{.forwards = false, .maxSpeed = 100},false);
    hoodPiston.set_value(true);
    firstStage.move_voltage(-12000);
    secondStage.move_voltage(-12000);
    thirdStage.move_voltage(-12000);
    pros::delay(500);
    firstStage.move_voltage(12000);
    secondStage.move_voltage(12000);
    thirdStage.move_voltage(12000);

}

There is the auton code, I don’t have any videos at the moment, but should be able to get some later. Also, for the sake of quicker communication than on the forum, you can reach me (the programmer) on Discord at steeltron0419. I’m a bit quicker to respond there, especially after school.

Cheers,

Luke

16756A BRICCC

It is possible that your PID isn’t tuned as well as you thought, it is also possible that you tuned your auto “too-well” so that it can’t adapt for small errors. Maybe you set up the bot slightly differently (do you have a line-up tool?) Sometimes autos just don’t fail. Maybe your 2 inch Omnis aren’t offset properly. Or maybe your omnis just aren’t consistent enough and make the robot drift too much.

You might also maybe need to optimize your path, basically while you are moving forward, less sharp turns generally could be more accurate (following a curve as opposed to simply turning in place). Maybe some movements are still too fast.

Add ~5 second pauses between each step of the movement. Display all appropriate variable values, motor degrees, motor temps, sensor readings, etc to the brain. Check/compare between runs. This should find the problem quickly.

This would be an excellent notebook entry.

Entirely within the realm of possibility, i might need to rework the error ranges and whatnot.

Possibly, we don’t have a line-up tool, we use the park barrier. working on that though since the park barrier can move a bit. The omnis might also be the problem, but we’ve double and triple checked that they contact the field properly. offsets might need to be reworked, though.

This is the most likely, I’d bet. I know that Lemlib has a swingToHeading command, so I’ll check that out. Speed might need to be tuned, but I can’t slow it down too much or the auton won’t hit everything we need it to. might be a pathing problem though. working on it.

Thanks!

okay, so we found a few issues that we had to fix and are now re-tuning PID. one of them was the offsets on the encoders btw. Anyways, we’re re-tuning PID, and the guide says to repeat until no amount of kD stops the robot from oscillating. One thing though, we’re at a kP of 10 and a kD of 77 and we’re still having to increase kP. is this normal? I feel like it isn’t.

not super familiar with lemlib but that doesn’t seem too far off to me, i’ve had similar values on other implementations before

okay, PID is re-tuned, but now we’ve got another problem. When we tell the robot to move to a point, and then turn 90 degrees and move to another coordinate with the same x-value but a different y-value, rather than driving in a straight line to the next value, if turns 45 degrees and drives to a different point. why would this be happening? I’ve never had this problem before.

Do you understand the coding? Would you be able to code this without the help of LemLib?

Things to consider:

  1. The better measurement of your odometry pods, the better the results. If you measure incorrectly, even by a quarter of an inch, you will experience the robot’s tracking location to be off by ± a quarter of an inch (so the span of the error is half an inch).
  2. The IMU from VEX are quite precise (small margin of error) but the accuracy varies per IMU when they leave the factory (it typically consistently measures a degree with a scalar error). I would suggest spinning your robot 10 times full 360, and divide the result by 10. This may give something like 3611/10 = 361.1. Then, divide 360 by the result: 360/361.1 = a multiplier. Then, multiply the IMU by that multiplier. This will improve tracking accuracy drastically by improving the trig functions in the back-end.

PID is very important, however if you have odometry pods it is not that difficult to get working to be precise, albeit may be clunky if not done correctly.

I don’t know what LemLib does but I think the best way for them to do is as follows:

  1. A PID for forward backward movement
  2. A PID for turning on the spot (-PID result for left wheels, +PID for right wheels)
  3. A PID for steering (when moving forward in a pure pursuit, only subtract from one side based on if the PID result if positive (subtract left wheels) or negative (subtract from right wheels

When running the pure pursuit when going to a point, I would run (PID1 - max(PID3, 0)) for left motors and (PID1 - max(-PID3, 0)) for right motors. And if the robot goes beyond that point, to do the inverse of that when backing up.

For a point turn, only PID 2 Is necessary such that PID2 for left and -PID2 for right wheels.

For each PID I hope LemLib has a kI activation value you can set such that kI activates when abs(error) is within like 5 degrees for a turn. This let’s you have kI be much higher and reactive to quickly correct for deviations for the tighter values as the robot approaches a position.

Therefore, when tuning a specific PID, for this case turning, I would set kP, kI, and kD, and anti-windup to 0 to start from. Note that the other variables seem decent enough to keep.

I would tune kP such that the robot oscillates a bit (should overshoot a bit and oscillate around 3-4 times before settling)
Then I tune kD which is a dampener. Make it such that the robot doesn’t oscillate and let it undershoot. This would result in an error within 2-3 degrees the robot settles at.

I would then increase kI and increase it such that the robot would essentially correct itself. Note that kI accumulates over time and the robot should essentially flip flop indefinitely. Don’t care if kI oscillates. Just make sure that kI is enough to not be overpowering but keeps making the robot pass the desired point. Then, increase anti windup so that the robot now just snaps to the desired point.

After tuning, the robot should now be able to turn 90 degrees within a quarter of a second and can do micro turns too.

Repeat similarly for steering and forward motion too.

When tuning PID’s, a digit-based method of tuning is highly recommended and let’s you tune relatively quickly:

I don’t know how I haven’t found this yet, but it should help. Thanks!

Why yes, I do understand the coding. I understand how Lemlib works, and I could accomplish this using the standard VexCode suite, but that would be an incredibly inefficient way to go about such a task. I’d be willing to bet that a lot of my issues are coming from a new odom pod design I created for this year, so i’m working on getting the kinks out of that. Most of the issues I am having this year are not issues I have had before, and if I could solve them by not asking any questions, i would.

I get what you’re saying, but I’ve done that all before, and I’ve read through the documentation quite a few times. You’re correct on all points, that’s exactly what Lemlib does, but the issues I’m having haven’t occured before, and I’d bet that it’s a compound issue of a few things on the robot. Just trying to see if anybody else has had the same problem’s I’m having for some reason, and what they did to solve them.

I assume you’re reading rotation from the motors. If so, then be sure to ROLL the bot back into the wall when placing on the field. This will push all the drivetrain slack on BOTH side to the SAME “end”. Depending on how tight your build is… there can be significant slack to deal with.

we double checked the api and found a LemLib::init(); function, that might be why our odom isn’t working right, we never call that function. we just call chassis.calibrate(). Looking into it monday, will post results