A boat going straight to the point

Hi, I keep fighting with my boat model to keep it flowing straight to my destination. The boat is steerable by two motors.
I just started with Arduino, please forgive me for my banal questions and dilemmas.
And now I have a few questions:

  1. GPS, would you add or correct something in the gpsdata (), gpsheading () section?
  2. Compass - I've read a bit about Auto Calibration but it's too hard for me to do. Do you have to calibrate the magnetometer every time? It's about pointing me in the direction. And if you would change anything in the headingcal () section
    Here is also the "Declination Angle" can it be calculated automatically ??
  3. The last steering of the motors, steering () is it ok, would you change something?

Thank you in advance for your help

static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do
  {
    while (GpsSerial.available()) 
    gps.encode(GpsSerial.read());
  } while (millis() - start < ms);
}

//GPS:-
void gpsdata()
{

  smartDelay(1000);
  unsigned long start;
  double lat_val, lng_val, alt_m_val, speedkmph;
  bool loc_valid, alt_valid;
  lat_val = gps.location.lat(); 
  loc_valid = gps.location.isValid(); 
  lng_val = gps.location.lng();
  alt_m_val = gps.altitude.meters(); 
  alt_valid = gps.altitude.isValid(); 
  speedkmph= gps.speed.kmph();
  if (!loc_valid)
  {
    Serial.print("Latitude : ");
    Serial.println("*****");
    Serial.print("Longitude : ");
    Serial.println("*****");
    delay(100);
  }
  else
  {
    Serial.println("GPS READING: ");
    //DegMinSec(lat_val);
    Serial.print("Latitude in Decimal Degrees : ");
    Serial.println(lat_val, 6);

    //DegMinSec(lng_val); 
    Serial.print("Longitude in Decimal Degrees : ");
    Serial.println(lng_val, 6);
    delay(100);
  }
  IBusSensor.setSensorMeasurement(1,speedBoat);
  speedBoat = speedkmph;
  
  latc=lat_val;
  logc=lng_val;  
  distance();
}



//GPS Heading:-
void gpsheading()
{
  float x,y,deltalog,deltalat;
  deltalog= logd-logc;
  deltalat=latd-latc;

  x=cos(latd)*sin(deltalog);
  y=(cos(latc)*sin(latd))-(sin(latc)*cos(latd)*cos(deltalog));
  
  bearing=(atan2(x,y))*(180/3.14);
  Serial.print("bearing");
  Serial.println(bearing);

  float a,d,c;
  a=(((sin(deltalat/2)))*(sin(deltalat/2))) + ((cos(latc))*(cos(latd))* (((sin(deltalog/2)))*(sin(deltalog/2)))  );
  c=2*(atan2(sqrt(a),sqrt(1-a)));
  d=6371*c; 
 Serial.println(d);
}



//Heading:-
void headingcal()
{
  int x,y,z;
  qmc.read(&x,&y,&z);
  delay(100);
// You must then add your 'Declination Angle' to the compass, which is the 'Error' of the magnetic field in your location.
// Find yours here: http://www.magnetic-declination.com/
  heading=atan2(x, y)/0.0937241808;
  if(heading < 0) 
  {
  heading+=360;
  }
  
  heading=360-heading; // N=0/360, E=90, S=180, W=270
  Serial.print("heading: ");
   Serial.println(heading);  
}

//Motor:-
void steering()
{

float finalv;
finalv=heading/bearing;
Serial.print("finalv: ");
Serial.println(finalv);
  
if(finalv>=0&&finalv<=1)
{
  forward();
}

else if(finalv >1 && finalv <=8)
{
  left();
}

else if(finalv <=13 && finalv >=8)
{
  right();
}

else if(logd==logc && latc==latd)
{
  stop1();
}
}

void forward()
{
    // Set Motor A forward
 
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
 
    // Set Motor B forward
 
    digitalWrite(in3, HIGH);
    digitalWrite(in4, LOW);
}

void right()
{
  digitalWrite(in1,HIGH);
  digitalWrite(in2,LOW);
  digitalWrite(in3,LOW);
  digitalWrite(in4,HIGH);
}

void left()
{
  digitalWrite(in1,LOW);
  digitalWrite(in2,HIGH);
  digitalWrite(in3,HIGH);
  digitalWrite(in4,LOW);
}

void back()
{
  digitalWrite(in1,HIGH);
  digitalWrite(in2,LOW);
  digitalWrite(in3,HIGH);
  digitalWrite(in4,LOW);
}
void stop1()
{
  digitalWrite(in1,LOW);
  digitalWrite(in2,LOW);
  digitalWrite(in3,LOW);
  digitalWrite(in4,LOW);
}

void distance()
{
const double LAKE_LAT = 50.391551;
const double LAKE_LNG = 18.451354;
distanceM = gps.distanceBetween(gps.location.lat(), gps.location.lng(), LAKE_LAT, LAKE_LNG);
Serial.print("Distance (m) to Point: ");
Serial.println(distanceM);


  IBusSensor.setSensorMeasurement(2,distanceM);
  
}

that's a drawing showing navigation terminology


(source Introduction to navstar gps)

you start from the blue star at the bottom left and want to go to the red star at the top right

on your way there you drift away from the active leg trajectory and find yourself at "present location"

you need to orient your motors so that your true bearing brings you back towards the red star.

One way to do this is to constantly monitor XTE (= Cross-track distance āžœ the distance from the Active Leg) and adjust your motor positions to bring it back to 0

A PID could help and you could get a course made good that would be small oscillation around the active Leg line.

1 Like

Hello
To get some additional ideas.

I’d look at using a gyro as a possible too . ( in WW2 V1 rockets were controlled with a simple mechanical gyro operating the flight surfaces directly)

  • you can also buy devices for drones which might be worth looking at ?

It is absolutely essential to calibrate the magnetometer, after mounting it in its final place. Check again from time to time. Tutorial here: Magnetometer Calibration | Adafruit SensorLab - Magnetometer Calibration | Adafruit Learning System

the "Declination Angle" can it be calculated automatically

No, magnetic declination is determined by your location.

For course steering with dual motors, it is convenient to use PID and differential steering.

For example to turn right, lower the power to the right motor and increase the power to the left motor, in an amount proportional to the current heading error.

1 Like

We had a rather large ship run aground here useing similar approach.
Fine, it was pointing at the final destination but it ended up sideways around 3 miles and ended it's days on the rocks.

Currently i use finalv from my code to increase or decrease speed on 2 motors
(I am just giving a snippet code of the whole above)

void steering()
{

float finalv;
finalv=heading/bearing;
Serial.print("finalv: ");
Serial.println(finalv);
....

 if(finalv >1 && finalv <=8)
{
  left();
}

....
void right()
{
  digitalWrite(in1,HIGH);
  digitalWrite(in2,LOW);
  digitalWrite(in3,LOW);
  digitalWrite(in4,HIGH);
  analogWrite(enA, map(finalv*100, 100, 800, 120, 0));
  analogWrite(enB, 120);
}

And now I have a question @jremington. You write about PID, I have watched a few movies on this subject so far to know what's going on more or less.
And now I want to use this library
From GitHub - br3ttb/Arduino-PID-Library
Will it be ok ??

And the second question as the input to the PID should I enter my FINALV value, or maybe the value from the HEADING compass ??

Sure, but for a boat, due to the viscosity of water, I found that a very simple form of PID using just Kp was fine.

Here is the decision making process of the steering loop:

// heading error and PID steering 
		error = heading_error(point_bearing, imu_heading);

//error is positive if current_heading > bearing (compass direction)
//positive bias acts to reduce left motor speed, so bear left
		bias = (kp*error)/10;  //Kp in tenths (Ki, Kd not necessary in water)

		// the motor routines internally limit the argument to {-255, 255}
		set_m1_speed(motorSpeed-bias); //left motor
		set_m2_speed(motorSpeed+bias); //right motor
// check for recent GPS fix
		if (millis() - gps_last_fix > gps_timeout) //5 seconds, currently

Here is a short video of the result: http://www.uoxray.uoregon.edu/orangutan/boat2.mp4

1 Like

@jremington Wow, that's perfect for me ... thank You. I would like to make an identical movie with my model of a boat going straight to the point :wink:

I have two last questions.

  1. I don't know if I understand correctly function
    error = heading_error (point_bearing, imu_heading)
    where point_bearing is the data from GPS data conversion to a bearing according to Haversin Formula.
    What Is the Definition of 'Bearing' in GPS Navigation?

I calculate the degrees using the magnetometer (correctly calibrated, of course) and the result is imu_heading ??

  heading=atan2(x, y)/0.0937241808;
  if(heading < 0) 
  {
  heading+=360;
  }
  
  heading=360-heading;
  1. Is tilt compensation required for the compass ??

Thanks in advance for valuable tips.

Angle from true North, clockwise in degrees. Get this from current and target GPS coordinates according to this page: Calculate distance and bearing between two Latitude/Longitude points using haversine formula in JavaScript

I calculate the degrees using the magnetometer (correctly calibrated, of course) and the result is imu_heading ??

After correcting for the magnetic declination at your location, in degrees, clockwise from North

Is tilt compensation required for the compass ??

Yes.

Simple function to calculate heading error:

// routine to calculate heading error in degrees, taking into account compass wrap

int heading_error(int bearing, int current_heading)
{
 int error = current_heading - bearing;
// if (error >  180) error -= 360;
// if (error < -180) error += 360;
 error = (error + 540)%360-180;
 return error;
}
1 Like

A known fixed point and navigating a course relative to the fixed point could provide away to travel in a straight line.

jpb10/SolarCalculator: SolarCalculator Library for Arduino (github.com)

It would be best used with a MCU that can, actually, do 64 bit floating point for greatest accuracy.

The solar calculator can calculate a very accurate solar position, without needing to look at the sun. The solar position would give a waypoint to base a straight line off of to a set course against.

calcHorizontalCoordinates( rtc.getYear(), (rtc.getMonth() + monthX) , rtc.getDay(),  rtc.getHour(true) , rtc.getMinute(), rtc.getSecond(), latitude, longitude, x_eData.azimuth, x_eData.elevation );
      x_eData.azimuth   = double(round(x_eData.azimuth * 100)) / 100; // Round to two decimal places
      x_eData.elevation = double(round(x_eData.elevation * 100)) / 100;

A relative course can be set against the suns position, a big triangle can be realized and all the math as per post 2 can be done

I don't want to open a new topic. therefore I will use this for discussion. Namely, my boat turns out to be sailing towards the destination, but in an arc.

The boat goes to the destination like this red line.
@jremington will controlling the P value help eliminate this arc ???

Thank you in advance for your answer.

Please post the full sketch that shows this effect

Nothing has changed in my code everything is above. It only uses the Kp of the PID. Therefore, my question to @jremington will the Kp control give any effect

The boat gets to its destination, so the code is working correctly.

The code does not attempt to constrain the boat to travel on a straight line between start and end. That is a very different and more difficult problem.

Could you please give me some examples to solve this problem ?? where can I read about it ??? would full PID implementation solve the problem ??

To constrain the motion to a straight line, the boat has to know its deviation from that straight line.

Secondly, it has to have some means of compensating for the changed heading, because the bow will no longer be pointed directly toward the destination.

Start by solving the math problem. Keep in mind that with wind gusts it is impossible to solve perfectly.

Oh, it will be hard for me;)
I would not like to give up your solution completely, because the boat performed very well on waves and crosswinds.

  1. I have a starting point A and a destination B
  2. I would have to find a straight line between these points (something like the equation of a straight line going through two points y = ax + b?)
  3. Would having a check point every 10 meters on the line improve the straight line flow of the boat?

If you go back to answer 2, you want to measure the Cross-track distance
Look up this keyword

1 Like

Why do you care about moving in a straight line?