I continue to suffer with PID control.
After starting the autopilot, I noticed that my boat hardly turns left at all. If the target is to the right, it turns over it, then only very slowly, after a very long time, turns back to the left, and only then reaches the target heading.
So there is a problem with the left side.
The servo that moves the rudder is not installed perfectly, so although the centre point is at the 1500 PWM value, it reaches the final rudder position to the left sooner, so I had to lower the limit.
int rudderleft=1300;
int rudderright=1900;
int ruddercenter=1500;
So, with a centre position of 1500, you only need to move 200 to the left of the centre position, and 400 to the right, to turn left and right at the same angle.
Outputtemp=map(Output,-180,180,rudderleft,rudderright);
But here the value returned from the PID is evenly split between 1300 and 1900, so I assume that if I should go straight it will be 1600, and if I should turn a few degrees to port it will still read almost 1600, which would still turn the boat to starboard?
The interesting thing is that sometimes it works perfectly.
#include <NMEAGPS.h>
#include <GPSport.h>
#include <PID_v1.h>
#include <Servo.h>
const byte RudderServoPin = 10;
Servo RudderServo;
int rudderleft=1300;
int rudderright=1900;
int ruddercenter=1500;
static NMEAGPS gps;
static gps_fix fix;
double Setpoint, Input, Output;
double OutputTemp;
double Kp = 1.0, Ki = 0.0, Kd = 0.4;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
const float targetlan = 42.683336;
const float targetlon = 12.082045;
const NeoGPS::Location_t base(targetlan, targetlon);
void setup() {
RudderServo.attach(RudderServoPin);
gpsPort.begin(38400);
Setpoint = 0; // Desired bearing is 0° off bearing to base
myPID.SetOutputLimits(-180, 180);
myPID.SetMode(AUTOMATIC);
}
void loop() {
servocontrol();
}
void servocontrol() {
int bearing = fix.location.BearingToDegrees(base);
int headingDegrees = fix.heading();
// Calculate a heading error in the range -180 to +180 and reverse the heading
Input = ((bearing - headingDegrees + 540) % 360) - 180;
myPID.Compute();
Outputtemp=map(Output,-180,180,rudderleft,rudderright);
RudderServo.writeMicroseconds(Outputtemp);
}