Ping Pong Ball Level

  uint8_t range = vl.readRange();
  error = goal - range;
  sum_error = sum_error + error*2;
  D_error = error - last_error;
  servo = servo_setpoint - balance.PID_P(Kp, error) - balance.PID_I(Ki, sum_error) - balance.PID_D(Kd, D_error);
  last_error = error;
  if (servo > 110)servo = 110;
  if (servo < 53)servo = 50;
  myservo.write(servo);

In the setup block, the serial monitor was initialized for debugging purposes. Then the LCD, time-of-flight sensor, and servo modules were initialized. In the loop function, the first thing that happens is a reading from the distance sensor to get the current location of the ping pong ball. Then the PID error calculations are done before passing them into the library to return the servo output values. A delay is put in place to pause the program after each servo write to keep the PID control loop in sync with the output. The figure shows the state of the system at rest and with an impulse where the ball is struck to see the response. Two spikes at about one third and two-thirds of the way across the graph represent these impulses in the blue line. The red graph represents the servo’s response to the impulse to correct the track and get the ball back into the middle.

Ping Pong Ball Level – Code

Because this was an Arduino-based project, the team had to write a custom Arduino library including a .cpp and .h file. This library is called Balance.

Main Arduino Code (.ino):

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include "Balance.h"
#include "Adafruit_VL6180X.h"
#include <Servo.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo myservo;
Adafruit_VL6180X vl = Adafruit_VL6180X();
int goal = 55;
float Kp = .08; //.9  
float Ki = .04; //.4
float Kd = .04; //.4
int servo_setpoint = 85; //1450
int error;
int sum_error;
float last_error = 0;
float D_error;
int servo;
float angle;
float last_angle = 0;
float gain = .05;
Balance balance;
unsigned long lcd_timer;
char buffer [100];
void setup() {
  Serial.begin(115200);

  // wait for serial port to open on native usb devices
  while (!Serial) {
    delay(1);
  }
  Serial.println("Adafruit VL6180x test!");
  if (! vl.begin()) {
    Serial.println("Failed to find sensor");
    while (1);
  }
  Serial.println("Sensor found!");
  Serial.println("range\tservo\terror\terror*Kp\tsum_error\tsum_error*Ki*.1");
  lcd.begin();
  lcd.backlight();
  myservo.attach(9);
  lcd_timer = millis();
}

void loop() {
  uint8_t range = vl.readRange();
  error = goal - range;
  sum_error = sum_error + error*2;
  D_error = error - last_error;
  servo = servo_setpoint - balance.PID_P(Kp, error) - balance.PID_I(Ki, sum_error) - balance.PID_D(Kd, D_error);
  last_error = error;
  if (servo > 110)servo = 110;
  if (servo < 53)servo = 50;
  myservo.write(servo);
  delay(100);
  angle = (servo_setpoint - servo); //low pass filter
  angle = angle*gain + (1-gain)*last_angle;
  last_angle = angle;
  angle = .95*angle -3.12;
  Serial.println(angle);
  //Serial.print("\t");
  //Serial.println(servo);
  //sprintf(buffer,"%d\t%d\t%d\t%.2f\n",range, servo, goal);
  //Serial.print(buffer);
  if (millis() - lcd_timer > 500) {
    sprintf(buffer, "%d", angle);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(angle);
    lcd_timer = millis();
  }
  
}

Custom Code Library Balance.cpp:

#include "Arduino.h"
#include "Balance.h"

  
Balance::Balance()
{

}

int Balance::PID_P(float Kp, int error)
{
  int PError;
  PError = error*Kp;
  return PError;
}

int Balance::PID_I(float Ki, int sum_error)
{
  int IError;
  IError = sum_error*Ki*.1;
  return IError;
}

int Balance::PID_D(float Kd, int D_error)
{
  int DError;
  DError = (D_error * Kd) / .1;
  return DError;
}

Header File for the Custom Library: Balance.h

/*
  Balance.h - Library for 412 teeter totter project.
  Created by Mitchell A. Stoltz, December 8, 2019.
  Released into the public domain.
*/
#ifndef Balance_h
#define Balance_h

#include "Arduino.h"

class Balance
{
  public:
    Balance();
    int PID_P(float Kp, int error);
    int PID_I(float Ki, int sum_error);
    int PID_D(float Kd, int D_error);
    float angle(int servo_ms, int setpoint);
  private:
};

#endif

Ping Pong Ball Level

The goal of this final project was to create something that would be unique as well as a practical use. This ping pong ball level utilizes a ping pong ball, a track, a distance sensor, a servo, and arduino uno, and an LCD display. Using these components, the distance sensor will detect where the ping pong ball is located on the track and will tell the servo to rotate the track to keep the ball in the middle. There is a set point of 0 degrees while the platform is on a level surface. While the servo is correcting the track to keep the ball in the middle, the angle of the track off of 0 degrees will be displayed on the LCD screen to show the angle of the surface this device is on.