PID Balancer

by Jchen in Circuits > Arduino

22 Views, 0 Favorites, 0 Comments

PID Balancer

WhatsApp Image 2025-01-14 at 00.59.31_d314ac17.jpg
Screenshot 2025-01-13 141906.png

In this project, we set out to use a PID algorithm in order to balance a rolling object in the middle of a seesaw style bar. This involved taking a reading from an IR distance sensor and calculating the error as distance from the midpoint of the chute. Using this error, we were able to calculate the proportion error, derivative of the error with respect to time (an approximation for the velocity), and the integral of the error with respect to time (to account for accumulated error over time). These could then be multiplied by the PID constants (proportional gain, integral gain, and derivative gain) and added together to produce an output that considers the distance of the object from the setpoint, velocity of the object, and the accumulated error. The consideration of object velocity helps to prevent overshooting the midpoint of the bar and the use of the integral of error means that even small errors which are too small to significantly affect the proportion value will grow over time and hence be corrected.

PID is an extensively used technique, and we believe this project is a great way to start learning about closed loop control algorithms, an essential part of any smart system that interacts with its environment.

Supplies


  1. Arduino Nano (other models can be used but the 3d model of the case may need to be adjusted)
  2. SG90 Servo motor
  3. PLA
  4. Sharp GP2YOA41SKOF distance sensor
  5. 35mm bore 27mm outer diameter bearing
  6. 6V battery pack (4x AA)
  7. 3D printer
  8. Soldering Iron
  9. 4x 3M heat set inserts
  10. 6x 3M bolts
  11. 2x 3M nuts

3D Design

designPicPID.jpg
Assembly_2025-Jan-13_11-45-10PM-000_CustomizedView7399434791_jpg.jpg

We first designed a 3d model on Fusion 360. It involves a mount for the distance sensor that points down the see-saw bar. The bar is driven by the SG90 with a 3:1 reduction, which reduces the range from 360 to 120 degrees, but triples the torque, reducing the strain on the motor.

We used a bearing in the design in order to produce a smooth turning motion, however these are fairly expensive so as an alternative, a PLA bearing can be printed, with the downside of being slightly less smooth spinning.

3D Print

WhatsApp Image 2025-01-13 at 13.49.22_f37ae1b3.jpg
Screenshot 2025-01-14 013030.png

Choose the parts from the 3d model that you wish to 3d print and export to your slicer. For me, it took about 250g of PLA, using 15% infill. I would recommend using higher infill for the "seesaw" component, as this part is the most prone to snapping.

Programming

Screenshot 2025-01-13 233025.png
Screenshot 2025-01-13 233110.png

We used the datasheet of the Sharp GP2YOA41SKOF distance sensor to write a code which calculates the distance of the rolling cylinder from the midpoint.

An essential part of any PID algorithm is the choice of the PID constants. It took us many hours of trial and error to arrive at the values in the code below. However, these might not be right for your own application, depending on factors such as the density of your PLA or the friction coefficient of your specific roll of filament. Therefore, It is essential for you to test and adjust these values, which will be unique to your particular set of circumstances.

You can edit and upload this code using the Arduino IDE, either with the app or the cloud editor.

#include <Servo.h>
#define sensor A1
Servo myservo;
unsigned long lastTime;
double Input, Output, Setpoint;
double errSum, lastErr;
double kp, ki, kd;

void setup() {
 // put your setup code here, to run once:
 myservo.attach(9);
 Serial.begin(9600);
  SetTunings(35, 0.3, 500);
}

void loop() {
 // put your main code here, to run repeatedly:

 int reading = analogRead(A1);

 Compute();

 Serial.print("distance: ");
 Serial.print(distanceRead());
 Serial.print(" ");
 double temp = map(Output, 1000, -1000, 180, 0);
 Serial.print("Output: "); 
 Serial.print(Output);
 Serial.print(" ");
 double print = constrain(temp, 0, 180);
 Serial.print("angle: ");
 Serial.println(print);
 Serial.print(" ");

 
 myservo.write(print);

 

}

void Compute()
{

unsigned long now = millis();
double timeChange = (double)(now - lastTime);

double Setpoint = 8.31;
double Input = distanceRead();

double error = Setpoint - Input;
 Serial.print("error: ");
Serial.print(error);

errSum += (error * timeChange);
double dErr = (error - lastErr) / timeChange;
 

Output = kp * error + ki * errSum + kd * dErr;
 Serial.print("output1: ");
Serial.print(Output);

lastErr = error;
lastTime = now;
}
 
void SetTunings(double Kp, double Ki, double Kd)
{
kp = Kp;
ki = Ki;
kd = Kd;
}

float distanceRead(){
 float temp = analogRead(A1)*5;
 if(temp < 400){
  temp += 3000;
 }
  Serial.print("Analog read: ");
 Serial.print(temp);
 Serial.print(" ");
 double distance = 12.94*1024/temp -0.42;
 return distance;

}

Assemble

WhatsApp Image 2025-01-14 at 00.59.31_12d79f1b.jpg
WhatsApp Image 2025-01-14 at 00.59.31_d314ac17.jpg

We then used a soldering iron to place the heat set inserts. After this, the servo, batteries and arduino could be placed into the case, before sealing it shut with the 4 bolts on the back.

Test the Finished Product

Remember to adjust the PID constants - greater derivative gain if the cylinder overshoots, less derivative gain if vibrations are too violent, greater integral gain if the cylinder gets stuck just before or beyond the midpoint, greater proportional gain if the Servo output is too small relative to the distance from the midpoint.

https://youtu.be/rVNB0sQd4Lg (Here's a video of the balancer working.)


Thanks for reading - I hope you were able to learn something about PID control!