Introduction: Ball and Beam W/LabVIEW & Arduino
This was a school project, the assignment was to construct a ball and beam control system.
A ping pong ball sits on top of the beam rolling forwards and backwards according to the pitch of the beam. The pitch is controlled by a servo that is connected to an Arduino. The position of the ball is measured by a distance sensor mounted at the end of the beam.
An PID controller is used to control the position of the ball on the beam.
Step 1: Parts
Electronics
- Distance sensor - SHARP 2Y0A21
- Parallax Standard Servo (#900-00005)
- Arduino
- 5V powersupply
3D printed parts
Infill - 20%
Resolution - 0.15mm
Filament Material - PLA
- Arm
- Servo arm
- Servo holder
- Endplate with sensor
- Endplate without sensor
- Hinge
Mechanicall
- 6x - M4x5mm
- 1x - M3x45mm
- 1x - M3x35mm
- 1x - M3x10mm
- 4x - 1,5x5mm wood screw
- 4x - 3,5x15mm woood screw
- Aluminum profile angle 520mm 15x15x2mm
- Flanged bearings - 4mm Radial Ball Bearing 8mm O.D
- 650x95x30mm wood board
- 145x30x47mm wood stud
- Ping pong ball
Step 2: Assembling
The design was 3D modelled with Fusion 360
Step 3: Electrical
You need to use an external 5 volt power supply to supply power the servo and distance sensor.
Attachments
Step 4: PID Controller
LabVIEW
Using the LINX in LabVIEW to connect to the arduino.
Arduino
Using a modified version of SharpIR.h library, originaly by Dr. Marcal Casas-Cartagena
Step 5: LabVIEW Code
Select the COM port to the arduino and the servo and sensor pin.
Attachments
Step 6: Arduino Code
// Libraries
#include <SharpIR.h> #include <Servo.h>// Defines #define PIN_SERVO 3 #define PIN_SENSOR 0 #define PIN_LED 13 #define DEBUG_ACTIVE 1 #define IR_MODEL 1080
// Running average const int numReadings = 3; int readings[numReadings]; // The readings from the analog input int readIndex = 0; // The index of the current reading double total = 0; // The running total int sensorRunningAverage = 0;
// Constants const double Kp = 1; const double Ki = 0.1; const double Kd = 15; const double sampleTime = 100; // ms const double setpoint = 20; // cm const int sensorMax = 57; // cm
// Floating variables double error = 0; double oldError = 0; double diffError = 0; double sumError = 0; unsigned long lastTime = 0; unsigned long standbyTime = 0; unsigned long runningTime = 0; double u = 0; // Output of the PID controller int servoOutput = 0; // Output to the servo double dis = 0;
// Functrions Servo servo; SharpIR SharpIR(sensorRunningAverage, IR_MODEL);
void setup() { servo.attach(PIN_SERVO); pinMode(OUTPUT, PIN_LED);
startup();
for (int thisReading = 0; thisReading < numReadings; thisReading++) { readings[thisReading] = 0; } lastTime = millis();
if (DEBUG_ACTIVE) { Serial.begin(9600); }
}
void loop() { sensorRunningAverage = getSensorRunningAverage(analogRead(PIN_SENSOR)); dis = SharpIR.distance(); // this returns the distance to the object you're measuring
/*How long since we last calculated*/ unsigned long now = millis(); double timeChange = (double)(now - lastTime); lastTime = now;
if (dis >= sensorMax) { error = 0; sumError = 0; diffError = 0;
standbyTime = millis() + 2000; } else if (standbyTime < now) { error = dis - setpoint; sumError += (error * (timeChange / 1000)); diffError = error - oldError; oldError = error; }
if (sampleTime + runningTime < now) { if (!(error <= 1 && error >= -1 && servoOutput == 26 && diffError <=0.5 && diffError >=-0.5)) { //tweaking of the regulator so that it stands still if the ball is within +-1 Cm. u = (Kp * error) + (Ki * sumError) + (Kd * diffError); } servoOutput = constrain(-u + 26, 0, 65); servo.write(servoOutput);
runningTime = millis(); } if (DEBUG_ACTIVE) { printStatus(); } }
void startup() { for (int i = 75; i >= 15; i--) { servo.write(i);
if ((i / 2 & 1) == 0) { ledPinOn(PIN_LED); } else { ledPinOff(PIN_LED); } delay(50); } }
int getSensorRunningAverage(int sensorValue) { total = total - readings[readIndex]; readings[readIndex] = sensorValue; // read from the sensor: total = total + readings[readIndex]; // add the reading to the total: readIndex = readIndex + 1; // advance to the next position in the array: if (readIndex >= numReadings) { // if we're at the end of the array... readIndex = 0; // ...wrap around to the beginning: } float average = total / numReadings; // calculate the average: return average; }
void printStatus() { Serial.print("Distance: "); Serial.print(dis); Serial.print("\t");
Serial.print("Error: "); Serial.print(error); Serial.print("\t");
Serial.print("Sum error: "); Serial.print(sumError); Serial.print("\t");
Serial.print("Diff error: "); Serial.print(diffError); Serial.print("\t");
Serial.print("u(t): "); Serial.print(u); Serial.print("\t");
Serial.print("Output: "); Serial.println(servoOutput); }
void ledPinOn(int led) { digitalWrite(led, HIGH); }
void ledPinOff(int led) { digitalWrite(led, LOW); }