What are the details of your problem? I am controlling a miniature gantry crane head using PID controllers. I am able to move the crane head along the x- and y-axis to their desired position. My issue is that both the x-axis controller and my angle controller write to the pin_pwm_x which is the motor in the x-axis. How am I supposed to make sure that the angle of the crane head is kept to zero while also being able to move in the x-axis to a desired position?
If the trolley was to move from x = 0 to x = 10, then when x == 10 and the crane head attached to the trolley would be swinging like a pendulum. This is where the angle controller should kick in and move the trolley in order to counteract the swing of the crane head until its angle is zero.
Illustration of the gantry crane.
CODE explained Arduino sketch for controlling a miniature gantry crane head using PID controllers. Here's a breakdown of the code:
- The code begins by including the necessary libraries and defining variables for connecting to the system.
- PID tuning parameters are specified for the y-axis, x-axis, and theta (angle) controllers.
- PID objects are created for each controller using the specified tuning parameters.
- The getAngleFromHead() function reads the angle data from a serial connection (from a sensor on the crane head).
- The readInput() function reads the inputs to the system, including the position values from the x-axis and y-axis sensors and the angle value from the crane head.
- The setup() function initializes the serial communication, sets the pin modes, initializes the variables, sets the setpoints for each controller, and configures the PID objects.
- The loop() function is the main execution loop of the program.
- It calls the readInput() function to update the input values.
- It computes the PID outputs for each controller using the Compute() method.
- It sets the enable pins for the motors to high to enable movement.
- It writes the computed outputs to the respective PWM pins for controlling the motors.
- It prints out relevant information for debugging purposes.
Code itself
// Include libraries
#include <Arduino.h>
#include "pinDefinitions.h"
#include <PID_v1.h>
// Define Variables we'll be connecting to
double Setpoint_y, Input_y, Output_y,
Setpoint_x, Input_x, Output_x,
Setpoint_theta, Input_theta, Output_theta;
// Specify the links and initial tuning parameters
double Kp_y = 32.4,
Ki_y = 0,
Kd_y = 12.96,
kg_y = 1,
Kp_x = 2.4,
Ki_x = 0,
Kd_x = 1.92,
kg_x = 1,
Kp_theta = 22.6,
Ki_theta = 0,
Kd_theta = 11.3,
kg_theta = 1;
PID_v1 yPID(&Input_y, &Output_y, &Setpoint_y, Kp_y *kg_y, Ki_y *kg_y, Kd_y *kg_y, DIRECT);
PID_v1 xPID(&Input_x, &Output_x, &Setpoint_x, Kp_x *kg_x, Ki_x *kg_x, Kd_x *kg_x, DIRECT);
PID_v1 thetaPID(&Input_theta, &Output_theta, &Setpoint_theta, Kp_theta *kg_theta, Ki_theta *kg_theta, Kd_theta *kg_theta, DIRECT);
float getAngleFromHead()
{
float angle;
if (Serial3.available() > 0)
{
String angleData = Serial3.readStringUntil('\n');
angle = angleData.toFloat();
// Serial.println(angle);
}
return angle;
}
// Function that reads the inputs to the system
void readInput()
{
Input_y = analogRead(pin_pos_y);
Input_x = analogRead(pin_pos_x);
Input_theta = getAngleFromHead();
// Sanity check angle data
while (Input_theta > 90 || Input_theta < -90)
{
digitalWrite(pin_enable_x, LOW); // Stop motors!
digitalWrite(pin_enable_y, LOW);
Serial.println("//Insane angle data");
Input_theta = getAngleFromHead();
}
}
void setup()
{
Serial.begin(115200);
Serial3.begin(9600);
; // Set input pinMode
pinMode(pin_pos_x, INPUT);
pinMode(pin_pos_y, INPUT);
// Set output pinMode
pinMode(pin_enable_x, OUTPUT);
pinMode(pin_enable_y, OUTPUT);
pinMode(pin_pwm_x, OUTPUT);
pinMode(pin_pwm_y, OUTPUT);
// Initialize the variables we're linked to
Input_y = analogRead(pin_pos_y);
Input_x = analogRead(pin_pos_x);
Setpoint_y = 400;
Setpoint_x = 300;
Setpoint_theta = 0;
yPID.SetSampleTime(10); //Sample rate expressed in ms, 10ms = 100 Hz
xPID.SetSampleTime(10);
thetaPID.SetSampleTime(1); //Angle sampling rate is 10x times faster than xPID
xPID.SetOutputLimits(255 * 0.1, 255 * 0.9); // PWM Range for the specific motors
yPID.SetOutputLimits(255 * 0.1, 255 * 0.9);
thetaPID.SetOutputLimits(255 * 0.1, 255 * 0.9);
// turn the PID on
yPID.SetMode(AUTOMATIC);
xPID.SetMode(AUTOMATIC);
thetaPID.SetMode(AUTOMATIC);
Serial.println("Setup done!");
}
void loop()
{
readInput();
yPID.Compute();
xPID.Compute();
thetaPID.Compute();
digitalWrite(pin_enable_y, HIGH); //Turns on motor
digitalWrite(pin_enable_x, HIGH);
analogWrite(pin_pwm_y, Output_y);
analogWrite(pin_pwm_x, Output_x);
analogWrite(pin_pwm_x, Output_theta); // Pin_pwm_x is being overwritten, not so good.
Serial.println("PID Input_x: " + String(Input_x) + ", PID Setpoint_x: " + String(Setpoint_x) + ", PID Output_x: " + String(Output_x) + ", Angle: " + String(Input_theta) + ", Kp_y: " + String(Kp_y));
}
What have I tried? I tried subtracting the output of the angle controller and the x-controller and vice versa, but to no avail.
analogWrite(pin_pwm_x, Output_x-Output_theta);
and analogWrite(pin_pwm_x, Output_theta-Output_x);
Here is a video of the gantry crane that I am working on. In this video it is seen working as intended, fast and with basically no oscillation. I don't know how they did it but this is the goal: The working gantry crane setup
Unrelated to this but but nice to see how it reduces the angle. Pendulum setup