drone-technolgy:pid-controller
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
drone-technolgy:pid-controller [2022/05/17 15:25] โ ilgarrasulov001 | drone-technolgy:pid-controller [2022/05/18 15:59] (current) โ ilgarrasulov001 | ||
---|---|---|---|
Line 74: | Line 74: | ||
{{: | {{: | ||
+ | |||
+ | <file c arduino_code.ino> | ||
+ | // importing libraries | ||
+ | #include " | ||
+ | #include " | ||
+ | |||
+ | //define pins | ||
+ | #define MOTOR D3 // pin for motor control | ||
+ | #define PIN_POT | ||
+ | |||
+ | MPU9250 mpu; // sensor instance | ||
+ | |||
+ | // initial control values | ||
+ | float kp=6.8; | ||
+ | float ki=0.1; | ||
+ | float kd=1.8; | ||
+ | float multiplier=1; | ||
+ | float error; | ||
+ | float ki_error_range=10; | ||
+ | float desired_roll=38.0; | ||
+ | float pError=0.0; | ||
+ | float current_roll=0.0; | ||
+ | float PID_p, PID_i, PID_d, PID_total; | ||
+ | // time parameters for setting the frequency of reading sensor values | ||
+ | int period = 50; // milliseconds | ||
+ | float tme; | ||
+ | |||
+ | // serial input value | ||
+ | String serialInput; | ||
+ | |||
+ | |||
+ | void setup() { | ||
+ | Serial.begin(115200); | ||
+ | Wire.begin(); | ||
+ | // connection to MPU sensor | ||
+ | if (!mpu.setup(0x68)) { // change to your own address | ||
+ | while (1) { | ||
+ | Serial.println(" | ||
+ | // delay(5000); | ||
+ | if (mpu.setup(0x68)){ | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | | ||
+ | | ||
+ | } | ||
+ | |||
+ | // motor and potentiometer to output and input | ||
+ | pinMode(MOTOR, | ||
+ | pinMode(PIN_POT, | ||
+ | // set desired roll to the value, read from potentiometer | ||
+ | set_desired_roll(); | ||
+ | Serial.println(" | ||
+ | tme=millis(); | ||
+ | } | ||
+ | |||
+ | |||
+ | void set_desired_roll(){ | ||
+ | // -55 lowest, 20 max, 0 center, total 75 | ||
+ | // read potentiometer value, range is [1024-10] | ||
+ | int rot_1024= analogRead(PIN_POT); | ||
+ | // convert to 75 units system | ||
+ | int rot_75 = 75*(1024 - rot_1024)/ | ||
+ | // set desired roll | ||
+ | desired_roll=rot_75-55; | ||
+ | } | ||
+ | |||
+ | void loop() { | ||
+ | // set desired yaw in accordance to the last read from potentiometer | ||
+ | set_desired_roll(); | ||
+ | |||
+ | // read input from serial monitor | ||
+ | // format: < | ||
+ | // example: kp=1.5 | ||
+ | if (Serial.available()> | ||
+ | | ||
+ | serialInput = Serial.readString(); | ||
+ | int index = serialInput.indexOf(' | ||
+ | String variable = serialInput.substring(0, | ||
+ | float value = serialInput.substring(index+1, | ||
+ | // check variable name and assign the value to the corresponding variable | ||
+ | if (variable==" | ||
+ | kp=value; | ||
+ | } | ||
+ | else if (variable==" | ||
+ | ki=value; | ||
+ | } | ||
+ | |||
+ | else if (variable==" | ||
+ | kd=value; | ||
+ | } | ||
+ | else if (variable==" | ||
+ | ki_error_range=value; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // check the sensor data | ||
+ | if (mpu.update()) { | ||
+ | if (millis() > tme + period) { // if more than period seconds passed since last read | ||
+ | | ||
+ | tme=millis(); | ||
+ | |||
+ | // read current roll angle | ||
+ | | ||
+ | |||
+ | // error calculation | ||
+ | error=desired_roll-current_roll; | ||
+ | | ||
+ | // P calculation | ||
+ | PID_p = kp * multiplier* error; | ||
+ | |||
+ | // I calculation | ||
+ | // I component starts to accumulate and hence to affect the PID total only if it | ||
+ | // is in range of ki error range | ||
+ | if(abs(error) < ki_error_range){ | ||
+ | PID_i = PID_i + (ki *multiplier* error); | ||
+ | | ||
+ | } else { // else it is set to zero | ||
+ | PID_i=0; | ||
+ | } | ||
+ | |||
+ | // D calculation | ||
+ | // pError is previous value of error | ||
+ | PID_d = kd*multiplier*((error - pError)/ | ||
+ | |||
+ | // Total PID calculation | ||
+ | PID_total = PID_p + PID_i + PID_d; | ||
+ | | ||
+ | // trim the PID value if it is outside of [0-255] range | ||
+ | | ||
+ | if (PID_total > 255){ | ||
+ | PID_total =255; | ||
+ | } | ||
+ | |||
+ | if (PID_total < 0){ | ||
+ | PID_total =0; | ||
+ | } | ||
+ | |||
+ | // print PID and other variables' | ||
+ | print_pid(); | ||
+ | |||
+ | // send final PID value to motor | ||
+ | analogWrite(MOTOR, | ||
+ | |||
+ | // set pError value to current error value | ||
+ | pError = error; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // print variable values to Serial Monitor | ||
+ | void print_pid() { | ||
+ | Serial.print(" | ||
+ | Serial.println(current_roll, | ||
+ | | ||
+ | Serial.print(" | ||
+ | Serial.println(desired_roll, | ||
+ | Serial.print(" | ||
+ | Serial.println(abs(error), | ||
+ | Serial.print(" | ||
+ | Serial.print(kp); | ||
+ | Serial.print(" | ||
+ | Serial.print(ki); | ||
+ | Serial.print(" | ||
+ | Serial.print(ki_error_range); | ||
+ | Serial.print(" | ||
+ | Serial.println(kd); | ||
+ | | ||
+ | Serial.print(" | ||
+ | Serial.print(PID_total, | ||
+ | Serial.print(", | ||
+ | Serial.print(PID_p, | ||
+ | Serial.print(", | ||
+ | Serial.print(PID_i, | ||
+ | Serial.print(", | ||
+ | Serial.println(PID_d, | ||
+ | } | ||
+ | |||
+ | // not used | ||
+ | // sending values to PC | ||
+ | // may be useful in future | ||
+ | void sendToPC(int* data) | ||
+ | { | ||
+ | byte* byteData = (byte*)(data); | ||
+ | Serial.write(byteData, | ||
+ | } | ||
+ | |||
+ | void sendToPC(float* data) | ||
+ | { | ||
+ | byte* byteData = (byte*)(data); | ||
+ | Serial.write(byteData, | ||
+ | } | ||
+ | </ | ||
+ | |||
Link to the code in GitHub repository | Link to the code in GitHub repository | ||
Line 94: | Line 288: | ||
The commonly accepted way of tuning is following: | The commonly accepted way of tuning is following: | ||
- | First you start changing Kp coefficient, | + | First you start changing Kp coefficient, |
+ | |||
+ | D = Kd*de/dt | ||
+ | |||
+ | D=Kdโde(t)dt | ||
+ | |||
+ | Here, the smaller the period between measurements or the bigger the change in error, the bigger will be the final D component. | ||
+ | |||
+ | And finally, you can tune the I component. It affects the final PID value only when error is within the given range relative to desired yaw value. For example, if the error is too small for proportional P controller and the error didn't change since the last measurement, | ||
+ | |||
+ | To change kp, ki and kd in our experiment without changing the code, you can enter: < | ||
+ | * " | ||
+ | * " | ||
+ | * " | ||
+ | * " | ||
+ | |||
+ | Here the values should be adapted for your device. | ||
{{: | {{: | ||
+ | |||
+ | For more examples of PID controller see nice videos on Youtube in Resources section. | ||
==== Resources ==== | ==== Resources ==== | ||
- | * [[https:// | + | * PID controller on Wikipedia |
- | * [[https:// | + | * PID controller code [[https:// |
- | * [[https:// | + | * PID Tuning |
+ | * PID Tuning [[https:// |
drone-technolgy/pid-controller.1652793904.txt.gz ยท Last modified: 2022/05/17 15:25 by ilgarrasulov001