Does anybody know of any good tutorials or explanations of how to use the PID Tuner? Specifically what the values mean (units?) and the execution of writing the code. Any help would be greatly appreciated.
The values are multipliers, how effective each part of the PID is. kP is the proportional multiplier, kI is the integral multiplier, and kD is the derivative multiplier. Not sure what kF is, but in the example code, they have it set to 0, so I doubt it’s essential to getting it to work. If you don’t quite understand PID, or don’t know how to get the multipliers, both are outlined in this excellent guide.
Not 100% sure this is correct, but I THINK that when you setup the PID profile through Motor::set_pos_pid()
or motor_set_pos_pid()
, the Motor::move_velocity()
or the motor_move_velocity()
commands operate through that profile. You can also use the full PID setup for more control, but I don’t know what most of the extra values mean, so be careful I guess.
See this.
I would avoid adjusting the motor PID constants on V5 for the time being.
I think the OP is referring to this. Which is about tuning user level pid loops not firmware level loops. (at least I think it is)
https://pros.cs.purdue.edu/v5/okapi/api/control/util/pid-tuner.html
Just to be clear, if the constants are being multiplied by the encoder units, how does this then get translated into motor power?
Yes, sorry I forgot to specify that I was talking about the PID Tuner built-in to the Okapi Library.
I would not spend too much time with the okapi PID Tuner.
While cool, it is mostly a proof-of-concept and not really practical.
It is better to tune yourself, because then you can have the performance you want.
If you want to understand what the values do, you need to read the paper linked in the docs.
I read the paper linked in the docs, and I think I understand what the constants mean, but, in the paper, there is no mention of what the specific values are for PROS. For example, in RobotC, the input was encoder ticks and the ouput was the motor power [-127, 127], but I don’t know what values PROS is using. Do the values get multiplied by raw encoder ticks or the user defined encoderUnits, and what unit are these numbers used for in the motors (e.g. voltage, percent power, watts). I would normally just put in values and determine it from there, but I have heard that too high values may damage motors.
Its only setting the motor firmware’s internal PID (ie. set_pos_pid()
) that can break the motor, as the motor has access to much higher refresh rates.
That command should not be available as it is, without some protection.
You should use the okapi PID controllers, or make your own. Both are user-based loops that have slower access to the motors. The tuner will give you constants according to the ControllerInput
and ControllerOutput
.
The way things like PID interface in okapi is via Controller Inputs and Outputs (please read). These are basically standardized ways of passing information between controls. All okapi controllers call these controller methods behind the scenes.
Say you were planning on using the PID Tuner on a Motor for use with AsyncPosPIDController. Your ControllerInput might be the Motor’s Integrated Encoder. Here is how you would get the Integrated ControllerInput from the Motor.
Motor myMotor(1);
std::shared_ptr<ContinuousRotarySensor> myEnc = myMotor.getEncoder();
//you will just need to pass getEncoder() to the Tuner
//then, behind the scenes, the Tuner will call:
myEnc->controllerGet();
Looking at the source, it looks like we are reading whatever gearset and encoder units the motor is set to:
Just to elaborate, this ControllerInput is what all of okapi’s controllers will use. So you can expect the AsyncPosPIDController to use the exact same value as its input as the Tuner will.
Next, for the ControllerOutput. We want to output to the motor. To do this, we will need to get a pointer to the motor object.
Motor myMotor(1);
std::shared_ptr<Motor> myMotorPtr = std::make_shared<Motor>(myMotor);
//again, you will just need to pass the make_shared command to the Tuner
//if you wanted to go straight to the shared pointer, you could do this
std::shared_ptr<Motor> myMotorPtr = std::make_shared<Motor>(1);
Per the ControllerOutput rules, the output is scaled [-1, 1]
. In the case with Motors, a value of 1 is 100% power.
Again, you can be assured that this output will be the same that the controllers call.
So a final program could look like
Motor myMotor(1);
PIDTuner tuner(
myMotor.getEncoder(),
std::make_shared<Motor>(myMotor),
//...
);
tuner.autotune();
You could then give the AsyncPosPIDController those gains and it should work the same.
Hope this makes sense.