PID anti-roll boat stabilizer

Hello everyone,
I’m working on a small engineering project which consist on reducing the roll effect on boats. My system is based on the displacement of a weight to conteract the sea swell ( you can look at the photos attach to see the prototype that i have built).

The system is composed of :

  • Arduino nano
  • Mpu6050
  • L298n
  • DC Motor
  • 1/16 Gearbox
  • Motor
  • Pulley and belt
  • Two end switch

My first objective is simply to obtain a system that when disturb stabilizes itself faster around it’s zero angle then with no enslavement

I have implement a PID controller in my code but my main issue is that i’m unable to determin the correct gain values for the PID. I have tried the famous Ziegler nichols method but with no success, i’m in fact able to obtain oscillations at a certain gain and the oscillation period but the differents value i obtain make the system unstable.

Here is a extract of the code that i’ve written (the entire code is in the files attach)

#include <Wire.h>

 
 
 ////////  Driver Variables ///////
 

const int enb = 9 ; // enb is the pwm control pin

const int in4 = 8 ; // Pin to control motor direction and brake (HIGH or LOW)

const int in3 = 7 ; // Pin to control motor direction and brake (HIGH or LOW)

 



////// Button variables ///////

const int buttonPing = 4; // Left end switch
const int buttonPind = 5; // Right end switch
int buttonStateg = 0;         
int buttonStated = 0;


///// counter variables///////
#define STD_LOOP_TIME 10

int timeGoneBy = STD_LOOP_TIME;

unsigned long loopStartTime = 0;

///// Variables PID/////
float Kp = 1;                   // (P)roportional Tuning Parameter
float Ki = 1;                   // (I)ntegral Tuning Parameter        
float Kd = 1;                  // (D)erivative Tuning Parameter       
float lastpitch;                // Keeps track of error over time
float iTerm;                    // Used to accumalate error (intergral)
float targetAngle = 0;          // desired angle

double thisTime = 0;
double lastTime = 0;

int PWM_sortie = 0;   // PWM output signal



/*______________________________________________________________________________________________________________________________________________*/


void setup() {


///// Calibration of gyro/////  
  Wire.begin();                                                     //Start I2C as master
  Serial.begin(57600);                                              //Use only for debugging
  pinMode(13, OUTPUT);                                              //Set output 13 (LED) as output
  pinMode(buttonPing, INPUT); 
  pinMode(buttonPind, INPUT);
  pinMode(in3,OUTPUT); 
  pinMode(in4,OUTPUT);
  pinMode(enb,OUTPUT);
  TCCR1B = TCCR1B & B11111000 | B00000100;    // set timer 1 divisor to   256 for PWM frequency of   122.55 Hz
  Serial.println("Debut de la Calibration");
  
  setup_mpu_6050_registers();                                          //Setup the registers of the MPU-6050 (500dfs / +/-4g) and start the gyro

  calibration_gyro(); // calibrate the Gyro

  Serial.println("Fin de la Calibration");

  loop_timer = micros();                                               //Reset the loop timer
}


/*______________________________________________________________________________________________________________________________________________________*/



void loop(){
  buttonStateg = digitalRead(buttonPing);
  buttonStated = digitalRead(buttonPind);
  lecture_mpu_6050_data();                                                //Read the raw acc and gyro data from the MPU-6050
  retour_angle_pitch();                                                   // return the angle_pitch
 
  Serial.println(angle_pitch);
  PWM_sortie = PID(angle_pitch);         //PID function return the PWM output signal

  if (buttonStated == HIGH && PWM_sortie > 0 ){   // Stops the motor while the right button is pressed and waits till the pwm output become negative 
digitalWrite(in4,LOW);     
  digitalWrite(in3,LOW);
  analogWrite(enb,0);
  
  
 }
 else if (buttonStateg == HIGH && PWM_sortie < 0){   // Stops the motor while the left button is pressed and waits till the pwm output becomes positive
  digitalWrite(in4,LOW);
  digitalWrite(in3,LOW);
  analogWrite(enb,0);
 }
 
  else{

  
  if(PWM_sortie < 0){      // If the PWM output is inferior to 0 move the weight in a direction
    
  digitalWrite(in4,HIGH);
  digitalWrite(in3,LOW);
  analogWrite(enb,abs(PWM_sortie));
  }
  if(PWM_sortie > 0){      // If the PWM output is above 0 move the weight in the other direction
    
  digitalWrite(in4,LOW);
  digitalWrite(in3,HIGH);
  analogWrite(enb,abs(PWM_sortie));
  }
 
  }
  clock_counter();// function for a 10 ms loop

}
///////////////////////////////////////////////////////////////////////////////////
////////////////////////////// Correcteur PID//////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////

int PID(float pitch) {            

    
    thisTime = millis();  // Calculate time since last time PID was called (~10ms)
    double timeChange = double(thisTime - lastTime);

    
    float error = targetAngle - pitch;  // Calculate Error

    
    float pTerm = Kp * error; // Calculate our PID terms
    iTerm += Ki * error * timeChange;
    
    if(iTerm > 255) iTerm = 255;                                       //Limit the Integrale to 255
    else if(iTerm < -255)iTerm = -255;
    
    float dTerm = Kd * (pitch - lastpitch) / timeChange; 

    lastpitch = pitch;
    lastTime = thisTime;

    
    float PIDValue = pTerm + iTerm - dTerm; // Set PWM Value

 
    if (PIDValue > 255) PIDValue = 255; // Limits PID to max motor speed
    else if (PIDValue < -255) PIDValue = -255; 



   
    return int(PIDValue);    // Return PID Output
}


///////////////////////////////////////////////////////////////////////////////////
////////////////////////////// counter clock ~100Hz ///////////////////////////
///////////////////////////////////////////////////////////////////////////////////

void clock_counter() {

   timeGoneBy = millis() - loopStartTime; // Calculate time since loop began
   if (timeGoneBy < STD_LOOP_TIME) {

        delay(STD_LOOP_TIME - timeGoneBy);


   
    } 

    // Update loop timer variables
    loopStartTime = millis();   
}

My problem might come from the code itself, i’m open to any fix, suggestions or informations. I also wondering if the Ziegler Nichols method is suited for this type of system.

Here is a photo of the setup:

Thank for your help and time.

Code_int_grale.ino (13.8 KB)

Your PID code appears to have issues and since there is no need to reinvent the wheel, start here: https://playground.arduino.cc/Code/PIDLibrary

Are you sure the L298 is able to drive that motor? Leo..

Perhaps PID is not the best regulator for your problem. A state regulator should perform better, but requires a valid physical model for proper operation.

Hi,
One of the OPs pics to show setup.
IMG_20180606_140558-min (1).jpg
Tom… :slight_smile:

Thank you for your reponse.

avr_fred: Your PID code appears to have issues and since there is no need to reinvent the wheel, start here: https://playground.arduino.cc/Code/PIDLibrary

Hello avr_fred, i know brett beauregard PID library. I have based my code on his explanations on his websites and from others. I chose not to use the library because i'm going to present my work to a jury and i need to be able to explain every line of code but since there is a issue with my system or code i will implement the PID library as you suggest to compare the results.

Wawa: Are you sure the L298 is able to drive that motor? Leo..

Hello Leo, the l298 motor driver seems to be sufficient for my project i have done some tests on full load and the motor draws 1.2 Amps which the L298 should theoretically be able to deliver.

DrDiettrich: Perhaps PID is not the best regulator for your problem. A state regulator should perform better, but requires a valid physical model for proper operation.

Hello, DrDiettrich a state regulator was the real objective in this system,however me and my friend who works with me weren't able to obtain a correct physical model, during our studies we just verified that some analogue system with state regulation respect the various specifications. We never built from scratch an entire state regulator. Since our deadline is approching i suggested that we tried the PID controller, i used it in a small self-balacing robot previously and thought that given the similarities (we need a position regulation but on the opposite of the self-balacing robot we have a steady one.

TomGeorge: Hi, One of the OPs pics to show setup. Tom... :)

Ooops i posted it late yesterday and didn't see the image insert button..... Thanks Tom i'll insert one in the first post

You could do this with a spinning gyro. Cool project though!

fall-apart-dave: You could do this with a spinning gyro. Cool project though!

Hello fall-apart-dave, the gyro was one option, there is also fin stabilizers, but for the fins i don't have enough knowledge in fluid mecanics and the setup to test it. For the gyro i didn't really want to deal with all the maths and equations that lies behind considering the small amount of time we have for this project. The weight displacement system seemed to my eyes as the simplest solution between the three, it was actually used in a french aircraft carrier (the charles de Gaulle) with the combination of the other technologies. |500x296

Nice! I'm afraid I can't offer a great deal of advice. But I love stuff like this! There's someone developing gyroscopic gloves for people with uncontrollable hand tremors (Parkinson's etc) that analyse the shake of the hand and then spins up a gyro to counter the tremors. I watched a demo with an woman who couldn't do anyhting with her hands because of the tremors, they got her to try and draw a spiral then put the glove on and try again, it was amazing what it did for her. She cried her eyes out at being able to just do that one simple thing herself. Really impressive technology.

Given the limited time until the due date, you should make the most of what you have. Your report should 1) describe what was done (which is already very impressive), 2) analyze the various failures of the system, and 3) explain what should be done differently.

Some hints how to tune a PID controller experimentally

  • set I and D to zero
  • increase P until the system oscillates. Also check in the extreme ends of the range. (In your case, strong starboard and strong port side)
  • decrease P until oscillations disappear completely
  • increase I until oscillations start again
  • decrease I and/or P
  • same procedure with D

If avoiding big deviations very fast is the main goal, then give P more weight. If achieving perfect level is the main goal, and speed is not so important, then give I more weight. D is really only useful if you have a very clean input signal. If your input signal or our output adjustment is not very precise, then D can do more harm than good.

The PID control loop assumes that your system has constant mass and time properties across the whole operating range. Often this is not the case. E.g. the system works fine in the middle of the range, but on the ends of the range it oscillates. Either you tune for the edges, and accept a slower / not so precise regulation in the middle, or you introduce adaptive PID values, which change over the range.

Also your actor (the weight) needs to be fast enough. If the boat rolls faster than the weight can move, then the best PID in the world is powerless. In this case, increase weight and/or speed.

Thank you all for your response, i have manage to get some improvement by applying your method hydrocontrol and after serveral hours of changing the gain .... i have found the following to work best : - Kp = 10; - Ki = 0.005; - Kd = 0;

My system isn't unstable anymore it stabilizes it self after a few seconds but it seems that with the active control the system actualy takes longer(1or 2s) to stabilize than without any control.....

I will try to get better values tomorrow and change a component of the system that i think will positively change it's behavior. If i'm unable to get anything better i will do as jremington suggested and try to make a beneficial critism of my system and it's limits.

I could imagine that a higher order PID regulator will yield better results. The sensor measures angular speed, from which an angle is determined by integration. As we all know, integration sums up offset errors continuously. The PWM output is somewhat equivalent to a speed, which also integrates into a position of the weight, again with offset integration errors.

Did you ever try another control loop for the displacement of the weight? I.e. a first PID determines a displacement for the weight, and a second regulator makes the motor move the weight to that position. Then you'd start with configuring the second (PWM) regulator, before you configure the first (angle to position) controller.

Also consider that the displacement of the weight depends on the roll angle, i.e. the the weight does not move horizontally, but instead has to be moved up against gravity. This can be taken into account in the configuration of the second regulator, where the set point (position) is determined from the inclination of the weight trajectory. Thus the second PID implements the static part of the weight position control, whereas the first PID governs the dynamic (anti roll) behaviour.