Appendix - Catapult

Appendix - Catapult

Table 1: Bill of materials for final build

The code does the following:

  • Sets a desired RPM and maintains this using a PID

  • Outputs the current value of acceleration being collected by the IMU

  • Runs the mechanism for one full throwing cycle and records the acceleration data

    • Pauses the code after running one cycle

    • Prints recorded acceleration data during this time

    • Wipes data from memory to improve speed

  • Continues to power the motor and output accel data after data has been collected to throw more balls

#include <Wire.h>

#include <Adafruit_MPU6050.h>

#include <Adafruit_Sensor.h>

#include <PID_v1_bc.h>

#include <Encoder.h>

 

// MPU-6050 setup

Adafruit_MPU6050 mpu;

 

// Motor and encoder setup

const uint8_t motorPinA = 8;

const uint8_t motorPinB = 9;

const uint8_t encoderPinA = 3;

const uint8_t encoderPinB = 2;

const uint8_t motorPWMPin = 5;

Encoder encoder(encoderPinA, encoderPinB);

 

// PID setup for constant motor speed

const int desiredRPM = 400;

const int countsPerRevolution = 950;

double Setpoint, Input, Output;

double Kp = 0.5, Ki = 5, Kd = 0.01;

PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

 

unsigned long previousMillis = 0;

long previousEncoderCount = 0;

uint16_t revolutions = 0;

 

const int maxDataPoints = 50;

const uint8_t maxRevolutions = 20;

const uint8_t samplesPerRev = maxDataPoints/maxRevolutions;

const int samplesPerStep = countsPerRevolution / samplesPerRev;

int revolutionCounter = 0;

long datapreviousEncoderCount =0;

int dataIndex = 0;

bool recording = true;

unsigned long dataTimes[maxDataPoints];

double accelDataX[maxDataPoints];

double accelDataY[maxDataPoints];

double accelDataZ[maxDataPoints];

 

 

void setup() {

  Serial.begin(19200);

  Wire.begin();

 

  // Initialize MPU-6050

  if (!mpu.begin()) {

    Serial.println("Failed to find MPU6050 chip");

    while (1) {

      delay(10);

    }

  }

  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);

  mpu.setGyroRange(MPU6050_RANGE_250_DEG);

  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

 

  // Configure PID

  Setpoint = desiredRPM;

  Serial.print(Setpoint);

  myPID.SetMode(AUTOMATIC);

  myPID.SetSampleTime(100);

  myPID.SetOutputLimits(0, 255);

 

  // Motor pins

  pinMode(motorPinA, OUTPUT);

  pinMode(motorPinB, OUTPUT);

  pinMode(motorPWMPin, OUTPUT);

}

 

void loop() {

  Serial.println();

  unsigned long currentMillis = millis();

  unsigned long deltaTime = currentMillis - previousMillis;

 

  // Read accelerometer and gyroscope data

  sensors_event_t a_event, g_event, temp_event;

  mpu.getEvent(&a_event, &g_event, &temp_event);

 

  // Convert raw values to Gs

  float accel_x = a_event.acceleration.x / 9.81;

  float accel_y = a_event.acceleration.y / 9.81;

  float accel_z = a_event.acceleration.z / 9.81;

 

  // Print accelerometer values in Gs

  Serial.print("Accel X: ");

  Serial.print(accel_x);

  Serial.print(" Accel Y: ");

  Serial.print(accel_y);

  Serial.print(" Accel Z: ");

  Serial.println(accel_z);

 

  // Calculate and print the magnitude of acceleration

  float magnitude = sqrt(pow(accel_x, 2) + pow(accel_y, 2) + pow(accel_z, 2));

  Serial.print("Magnitude: ");

  Serial.println(magnitude);

 

  if (deltaTime >= 100) {

    // Read encoder value and calculate counts per second

    long currentEncoderCount = encoder.read();

    long deltaEncoderCount = currentEncoderCount - previousEncoderCount;

    previousEncoderCount = currentEncoderCount;

    double countsPerSecond = (deltaEncoderCount / (double)deltaTime) * 1000;

 

    // Calculate current RPM

    Input = (countsPerSecond / countsPerRevolution) * 60;

    Serial.print("Input: ");

    Serial.println(Input);

 

    // Update revolution counter

    revolutions = currentEncoderCount / countsPerRevolution;

    Serial.print("Revolutions: ");

    Serial.println(revolutions);

 

    // Record acceleration data

    if (recording && dataIndex < maxDataPoints) {

      int deltaEncoderCount = currentEncoderCount - datapreviousEncoderCount;

      if (deltaEncoderCount >= samplesPerStep) {

        datapreviousEncoderCount = currentEncoderCount;

        dataTimes[dataIndex] = millis();

        accelDataX[dataIndex] = accel_x;

        accelDataY[dataIndex] = accel_y;

        accelDataZ[dataIndex] = accel_z;

        dataIndex++;

      }

    } else if (recording && revolutions >= maxRevolutions) {

      recording = false;

      endRecording();

    }

 

    // Compute PID

    myPID.Compute();

 

    // Control motor

    controlMotor(Output);

    Serial.print("PID OUTPUT (PWM): ");

    Serial.println(Output);

 

    // Print current encoder speed, encoder value, and RPM

    Serial.print("Encoder Speed (counts/s): ");

    Serial.print(countsPerSecond);

    Serial.print(" | Encoder Value: ");

    Serial.print(currentEncoderCount);

    Serial.print(" | RPM: ");

    Serial.println(Input);

 

    // Update revolution counter

    revolutions = currentEncoderCount / countsPerRevolution;

    Serial.print("Revolutions: ");

    Serial.println(revolutions);

    previousMillis = currentMillis;

  }

}

 

void controlMotor(int speed) {

  if (speed > 0) {

    digitalWrite(motorPinA, HIGH);

    digitalWrite(motorPinB, LOW);

    analogWrite(motorPWMPin, speed);

  } else {

    digitalWrite(motorPinA, LOW);

    digitalWrite(motorPinB, LOW);

    analogWrite(motorPWMPin, 0);

  }

}

 

void endRecording() {

  // Print the recorded data

  controlMotor(0);

  Serial.println("Recorded Data:");

  for (int i = 0; i < maxDataPoints; i++) {

    //Serial.print(" Time: ");

    Serial.print(" ");