Or make your servo to rudder connection symmetric...
With handles in same direction when both are in middle position.
Maybe handles of same length as well.
Nice topic and rich discussion. Some 50 years ago I used to sail small sailboats and ocean-going boats, and even some small remote controled ones. Later I spent 20 years implementing and tunning a lot of PID controllers applied to industrial processes, so the topic immediatelly catched my attention.
A key concept to PID controllers is that of the SETPOINT. This is the value of the variable we wish to stick to. Usual theory assumes this value to be a constant, from wich we can derive the error subtracting the SETPOINT from the Process Value (present value). This error is used in correction calculations using the PID algorithm to obtain the Output to be sent to PID controller.
From the very first posts I presume your objective is a target location defined by its coordinates (latitude and longitude), wich along with your present location define a bearing to reach objective (SETPOINT bearing or simply SP).
If our boat were sailing in calm weather with no currents, this bearing would get us to the objective if we manage to keep it all the way. The Process Value (PV bearing) would be computed every so often and compared to the SP to get the right or left error to feed into PID algorithm and we could rest assured to reach our objective.
Unfortunately real boats, big and small, have to sail in somewhat troubled waters. Cross winds and currents can, and will deviate our route, so we can not afford to keep a constant compass bearing, otherwise we'll miss our target. Ground track and bearing are not the same thing.
If those perturbations are random, that is, they came from both sides with direction and magnitude aproximately even, their net effects would cancel out and we could neglect them.
We could use a variable SP obtained at every new measurement updating the SP to compensate drift, but should the wind, current or both be predominantly from one side of the boat, our navigation would end up looking more like a curved line than a straight course to destination.
The only way to do it correctly is using the navigational skills that I learned in my sailorman past experience. There are two ways to do it. If you know the direction and magnitude of the interfering wind or current, you can use vectorial math to compute the corrected bearing to follow and use it as SP. This is the way airliner pilots and professional sailors do it.
The other way is to use points of the straight line from the first waypoint to destination as a series of linearly variable SP. Both work but require constant attention to be aware of unanticipated variations.
Although this thread started with a simple problem, to steer a boat towards a fixed waypoint, that simplicity only works in ideal conditions. The single PID is good enough to cope with small random deviations.
In more complex situations, with stronger perturbations that can introduce biased deviations, it may be needed a second PID loop in a configuration known as a cascaded loop.
I asked chatGPT to describe the arrangement and this is the answer received:
"To control the steering of a boat using a servo motor, a two-loop cascaded control system can be implemented. The two loops are the outer loop and the inner loop. The outer loop controls the boat's course, while the inner loop controls the position of the servo motor.
The two-loop cascaded controller consists of the following components:
Course Controller: The outer loop controller is responsible for controlling the boat's course. This controller receives inputs from a compass or a GPS system and calculates the course error, which is the difference between the desired course and the actual course. The controller then generates a control signal that is proportional to the course error. This signal is sent to the inner loop controller.
Servo Position Controller: The inner loop controller receives the control signal from the outer loop controller and generates a signal that is proportional to the position error of the servo motor. The position error is the difference between the desired position of the servo motor and the actual position. The controller then sends the control signal to the servo motor.
Servo Motor: The servo motor receives the control signal from the inner loop controller and adjusts its position accordingly to steer the boat in the desired direction.
The two-loop cascaded controller ensures that the boat's course is maintained even in the presence of external disturbances such as waves or wind. The outer loop controller provides the necessary feedback to adjust the course of the boat, while the inner loop controller ensures that the servo motor is positioned accurately to steer the boat in the desired direction.
Overall, the two-loop cascaded controller provides a robust and effective control system for steering a boat's course using a servo motor."
I think it is a useful experience to start using just the course PID to get a sense of its pros e cons, and later introduce the cascaded servo motor to improve navigation. Both use the same concepts but one works in the outer ambient (GPS locations, courses, etc.) while the inner loop operates inside the boat (course to steer, rudder position, etc.)
Sure it's not a beginner's project, but is perfect to show how the same PID principles can be used in a wider control as well as in a local and more internal way.
Thanks for the detailed research.
It's instructive stuff.
In the meantime, I have got the PID control working, the boat goes relatively straight, in high winds/flutter there are minimal deflections, but they are perfectly acceptable.
However, I would like to refine the system, and to do that I would need to be very familiar with the use of PID. I have a few more questions:
1. I define the following variables for PID control:
double Kp=2, Ki=5, Kd=1;
I know it's a stupid question, but it's the same as writing:
double Kp=2.0, Ki=5.0, Kd=1.0;
right? because its a double
2. In the main loop i can change dynamically this values, and after i stored them in EEPROM.
So the next time I start the device it will read from memory the last saved Ki,Kp,Kd values. the variables are written with a 5 byte offset. example:
EEPROM.put(100, kp);
EEPROM.put(105, ki);
EEPROM.put(110, kd);
Double can fit into 5byte space, right?
3. By default, the autopilot function does not start automatically. When the program is running, it just watches a switch in the loop. If I press it, the autopilot function runs, which controls the servo and calculates the PID
int bearing = fix.location.BearingToDegrees(base);
int headingDegrees = fix.heading();
Input = ((bearing - headingDegrees + 540) % 360) - 180;
myPID.Compute();
Outputtemp=map(Output,-180,180,rudderleft,rudderright);
RudderServo.writeMicroseconds(Outputtemp);
If I press the button again, the autopilot function is switched off. The question is whether the PID resistor needs to be "reset" in the two on/off cycles? You can stop when it's perfectly in the right direction, turn it off, and then turn it on again, and then you might be 90 degrees off the target. Can this cause a problem? I can tell you honestly that I'm not resetting anything at the moment, and it's working after all, I just don't know if it's causing a problem that I haven't encountered yet
4. Not so much a PID, more a mathematical question. I have the possibility to increase and decrease the values Kp,Ki,Kd with separate buttons. If I press the Kp+ button the following happens:
Kp = Kp + 0.1;
EEPROM.put(100, Kp);
EEPROM.read(100, Kp);
Is that OK? I get back the same value on read as I entered. Decimal exactly?
What is a PID resistor?
The summed errors for your I action might contain many things. Offset of rudder, offset of measuring device (declination).
If so: keep it...
But it may also contain a correction for side winds at the moment you stopped the PID.
In that case it is better to throw it away. I guess your PID might have a resume function and a reset function...
On UNO a double is really a float and takes 4 bytes to store.
On other boards (such as ESP) a double is stored as 8 bytes...
Why a 5 byte offset if you store a 4 or 8 byte variable?
Suggest to rename Outputtemp to mappedOutput.
What's your code like now?
1: The compiler casts the integers to floats as it stores them.
The 2,5,1 settings are a common default that most likely do not fit your system. For saner defaults, try (2, 0.1, 0.1) You could play with PID_v1 on a completely dfferent simulated system on
2: Without the code, it's hard to say how the interaction with EEPROM will work. I'd set the 100, 105, and 110 magic numbers with constants, and calculate those constants based on sizeof(kp)
. in order to prevent wearing out the EEPROM, I wouldn't automatically store the updated values, maybe use another button press, or wrap it in a state-change+time condition if(pidChange == true && now - lastPIDchange > millissecondsInAMinute){...}
3: How are you switching PID on and off? If you use the myPID.setMode(MANUAL);
for off, mess around with Output for a while, and then myPID.setMode(AUTOMATIC)
for on, the code initializes for a bumpless transfer to the current settings ,notices the 90 degree error and starts moving towards that normally, as if you had just made a 90° setpoint change. If for off, you were ignoring Output, it could be bumpy. (Improving the Beginnerās PID: On/Off Ā« Project Blog)
I'd use a more meaningful variablename so the units have less chance to confuse:
RudderServoUs=map(Output,-180,180,rudderleft,rudderright);
RudderServo.writeMicroseconds(RudderServoUs);
4: You'll get back the same value 4-byte float value from EEPROM as it was storing in SRAM, at least until those EEPROM bits wear out.
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.