*** The original issue with PID not centralizing rudder is solved, as stated in a later post in this thread. The thread has become more of a discussion about the implementation as an autopilot in general. I will create a new thread about it to keep this clean. ***
/Tobbera, 2015-03-09
New thread is HERE.
/Tobbera, 2015-07-27
"After a couple of test runs tonight, I can now see that I was wrong in my first statement when creating this thread. The PID function does indeed actually "center" the rudder, at least a little bit. According to the graphs, the PID output decreases when error decreases, even if there still is an error. Good! Thank you all for your input! "
Original post:
Hi all, hope you can bring some clarity into my life.
The project is a autopilot for a sailing boat. PID Input is wind direction, PID output is a servo connected to the rudder.
Issue: The PID controller does not centralize the rudder when closing in on the right cource.
For example, the boat is out of course by 20 degrees, the PID function increases PID Output which turns the rudder, more and more. After a while the boat is going to react to the rudder and start closing in in the desired course/setpoint (error decreasing), but the rudder angle (PID output) is not going back (PID output decreasing). Actually, its still increasing. The result is going to be the boat swinging way pass the setpoint. Not until the boat has actually swung pass the setpoint the PID start to decrease PID output.
Basically, the PID need to decrease Output when error is decreasing. Maybe PID is not the way to go here at all?
Bellow is my very first simple code I have done to try out the PID functionality.
#include <PID_v1.h>
#include <Servo.h>
// HARDWARE
const int WindVanePotPin = A5; // Analog input pin that the potentiometer in the Wind Vane is attached to
const int RudderServoPin = 9; // Digital output to the Rudder Servo.
Servo RudderServo; // Create servo object to control a servo
// PID
double Setpoint, Input, Output; // Define Variables we'll be connecting to
double varKp=4, varKi=0.2, varKd=1; // Define the Default Tuning Parameters
PID myPID(&Input, &Output, &Setpoint, varKp, varKi, varKd, DIRECT);
// Work vars
long PreviousSerialWriteMillis = 0; // will store last time LED was updated
long SerialWritInterval = 500; // interval at which to blink (milliseconds)
void setup()
{
Setpoint = 500; // Example
myPID.SetMode(AUTOMATIC); // Turn the PID on
myPID.SetOutputLimits(1, 179); // min: Low end of the range. must be < max (double). max: High end of the range. must be > min (double)
RudderServo.attach(RudderServoPin);
RudderServo.write(90); // sets starting servo position to Center.
Serial.begin(9600); // Debug logging
}
void loop()
{
/*
Kp: Determines how aggressively the PID reacts to the current amount of error (Proportional) (double >=0)
Ki: Determines how aggressively the PID reacts to error over time (Integral) (double>=0)
Kd: Determines how aggressively the PID reacts to the change in error (Derivative) (double>=0)
*/
// Read Wind Vane
Input = analogRead(WindVanePotPin);
// Input = map(Input, 0, 1023, 0, 360); // Commented out because PID compute expect 0-1023 value.
// DEBUG LOGGING
unsigned long currentMillis = millis();
if(currentMillis - PreviousSerialWriteMillis > SerialWritInterval) {
PreviousSerialWriteMillis = currentMillis;
Serial.print("Wind Vane Value: ");
Serial.println(Input);
Serial.print("Setpoint Value: ");
Serial.println(Setpoint);
Serial.print("Servo Write Value: ");
Serial.println(Output);
}
myPID.Compute();
RudderServo.write(Output);
}