Open Source Boat Controller Project

Discussion in 'OnBoard Electronics & Controls' started by MurphyLaw, Aug 7, 2018.

  1. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    Ok here it is with the motion sensing and ultrasonic distance and the servos. I checked the trigger pulse and return echo on the scope. The red trace is the trigger the blue is the echo. Will add the laser tomorrow.


    Code:
    #include "NineAxesMotion.h"
    #include <Wire.h>
    #include <Servo.h>
    
    NineAxesMotion mySensor;  
    unsigned long lastStreamTime = 0;
    const int streamPeriod = 20;          //To stream at 50Hz without using additional timers (time period(ms) =1000/frequency(Hz))
    bool updateSensorData = true;         //Flag to update the sensor data. Default is true to perform the first read before the first stream
    
    Servo myservo;  // create servo object to control a servo
    // twelve servo objects can be created on most boards
    
    int pos = 0;    // variable to store the servo position
    int myPitchAngle = 0;
    long duration;
    int distance;
    
    const int trigPin = 10;
    const int echoPin = 11;
    
    void setup() //This code is executed once
    {
      //Peripheral Initialization
      Serial.begin(115200);           //Initialize the Serial Port to view information on the Serial Monitor
      I2C.begin();                    //Initialize I2C communication to the let the library communicate with the sensor.
    
      myservo.attach(9);  // attaches the servo on pin 9 to the servo object
    
      pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
      pinMode(echoPin, INPUT); // Sets the echoPin as an Input
      pinMode(echoPin,INPUT_PULLUP);
    
      //Sensor Initialization
      mySensor.initSensor();          //The I2C Address can be changed here inside this function in the library
      mySensor.setOperationMode(OPERATION_MODE_NDOF);   //Can be configured to other operation modes as desired
      mySensor.setUpdateMode(MANUAL);  //The default is AUTO. Changing to MANUAL requires calling the relevant update functions prior to calling the read functions
      //Setting to MANUAL requires fewer reads to the sensor
    }
    
    
    
    void loop()
    {
      if (updateSensorData)  //Keep the updating of data as a separate task
      {
        mySensor.updateEuler();
        mySensor.updateLinearAccel();
        mySensor.updateMag();
        mySensor.updateGyro();
        mySensor.updateCalibStatus();  //Update the Calibration Status
        updateSensorData = false;
      }
    
     
     
      if ((millis() - lastStreamTime) >= streamPeriod)
      {
        lastStreamTime = millis();
    
        //Euler
     
        //Serial.print(" H: ");
        Serial.print(mySensor.readEulerHeading());
        Serial.print(", ");
    
        //Serial.print(" R: ");
        Serial.print(mySensor.readEulerRoll());
        Serial.print(",");
    
        //Serial.print(" P: ");
        myPitchAngle=(mySensor.readEulerPitch());
        Serial.print(myPitchAngle);
        Serial.print(", ");
     
     
        //Acceleration
     
        //Serial.print(" AccelX: ");
        Serial.print(mySensor.readLinearAccelX());
        Serial.print(", ");
     
        //Serial.print(" AccelY: ");
        Serial.print(mySensor.readLinearAccelY());
        Serial.print(", ");
     
        //Serial.print(" AccelZ: ");
        Serial.print(mySensor.readLinearAccelZ());
        Serial.print(", ");
    
        //Magnetometer
     
        //Serial.print(" MagX: ");
        Serial.print(mySensor.readMagX());
        Serial.print(", ");
     
        //Serial.print(" MagY: ");
        Serial.print(mySensor.readMagY());
        Serial.print(", ");
     
        //Serial.print(" MagZ: ");
        Serial.print(mySensor.readMagZ());
        Serial.print(", ");
    
        //Gyroscope
     
        //Serial.print(" GyroX: ");
        Serial.print(mySensor.readGyroX());
        Serial.print(", ");
    
        //Serial.print(" GyroY: ");
        Serial.print(mySensor.readGyroY());
        Serial.print(", ");
     
        //Serial.print(" GyroZ: ");
        Serial.print(mySensor.readGyroZ());
        Serial.print(", ");
     
        Serial.println();
    
        updateSensorData = true;
    
    //Code added to standard motion sensor code
    
    
       //Code for Ultrasonic Distance
     
       // Clears the trigPin
        digitalWrite(trigPin, LOW);
        delayMicroseconds(2);
        // Sets the trigPin on HIGH state for 10 micro seconds
        digitalWrite(trigPin, HIGH);
        delayMicroseconds(10);
        digitalWrite(trigPin, LOW);
        // Reads the echoPin, returns the sound wave travel time in microseconds
        duration = pulseIn(echoPin, HIGH);
        // Calculating the distance
        distance= duration*0.034/2;
        // Prints the distance on the Serial Monitor
        Serial.print("Distance: ");
        Serial.println(distance);// Clears the trigPin
        delay(100);
    
    
       //Code for Servos
    
        if (myPitchAngle > 3.0){  //Boat pitching too high
          Serial.print("High");
          myservo.write(65);
          delay(100);
        }else if(myPitchAngle < -3.0){  //Boat pitching too low
          Serial.print("Low");
          myservo.write(115);
          delay(100);
        }else if (distance > 50){  //Boat too high
          Serial.print("High");
          myservo.write(65);
          delay(100);
        }else if (distance < 30){  //Boat too low
          Serial.print("Low");
          myservo.write(115);
          delay(100);
        }else{
          Serial.print("Middle"); //Boat OK
          myservo.write(90);
          delay(100);
        }
    
      }
     
    }

    Here is the code for the servos now, you will find it at the bottom of the main code, if you want to run this code you just use the code above, this is just a snippet from that code.

    Code:
     //Code for Servos
    
        if (myPitchAngle > 3.0){  //Boat pitching too high
          Serial.print("High");
          myservo.write(65);
          delay(100);
        }else if(myPitchAngle < -3.0){  //Boat pitching too low
          Serial.print("Low");
          myservo.write(115);
          delay(100);
        }else if (distance > 50){  //Boat too high
          Serial.print("High");
          myservo.write(65);
          delay(100);
        }else if (distance < 30){  //Boat too low
          Serial.print("Low");
          myservo.write(115);
          delay(100);
        }else{
          Serial.print("Middle"); //Boat OK
          myservo.write(90);
          delay(100);
        }

    What is happening is it goes through a list of sensor readings that will make you want to move the foil.

    First it checks whether the boat is pitching too high. If so it decreases the pitch of the foil
    Then it checks if the boat is pitching too low. If so it increase the pitch of the foil.
    Then it checks if the boat is too high in the water. If so it decreases the pitch of the foil
    Then it checks if the boat is too low in the water.If so it increase the pitch of the foil.
    If all is OK then it set the foil in the centre position.
     

    Attached Files:

    Last edited: Aug 16, 2018
  2. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    Here is the updated circuit diagram. This shows you how to connect with a single power source like on the boat, I don't recommend this at all for testing, if you have unshielded wires all over the place you will find it impossible not to get a glitch or two. When you wire up in a boat use shielded cable for the TRIG/ECHO lines.We will be using a 12V motor to control the foil which will need a few amps of power. There are many 12V to 5V converters on the market, get a small switch mode power supply that do not use a transformer and do not waste power.


    circuitdiagram2.jpg
     
  3. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    Yay!!!!! I finally got it all working together, what a freeking mission, without that blue scope and my new learnt language of I2C I would have never cracked it.
    There were so many bugs in the manufacturers code it wasn't funny, man oh man this has been a VERY frustrating two weeks.

    This laser range finder is the business, on a different level to the ultrasonic sensor. Very responsive.

    I left the ultrasonic sensor circuit running for 6 hours and it fired off twice in response to noise, so much for encoding the signal. I might modify it and make it more robust.

    OK I am going to get some rest and write the code up later, yay no more coding for a while now. Time to put it in a waterproof case and test on water and on the road, I am going to stick it out the car window and see what the max speed the laser will work at on land then try it on water.


    MK1.jpg
     
  4. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    Last edited: Aug 18, 2018
  5. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    OK I need to stick this on a foil and test it and I need others to stick it on a foil and test it, I am going to rig something up in my swimming pool because then I can put in the many hours needed to get it right but it also needs to be tested on existing foiling craft. What are the mechanics you are using with your floats Doug?
    You want to give one of these Arduino's a try on your foils, you just have to wire them up as I have shown, the Arduino has just one program you run, you just select which code, then upload and then for ever after you power it up it will run that code until you reprogram it with something else.
    I think I am just going to lay a beam across my pool and fit a motor in the centre and then a big arm with a foil at the end and spin the foil in big 2m diameter circles. Simple and I can keep it running continuously so I can experiment a lot while actually foiling, if I make a standard length of the pool test rig, I have to keep restarting over and over.
     
  6. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    I just figured out how to make the ultimate laser water level indicator that will not be effected by any level of spray.
    2 lasers and a camera, I thought of the idea from the meatball that pilots use to land on aircraft carriers.
    You shine the 2 lasers, where they meet at the ideal height above the water you will see a single dot, when you are too high or low you will see 2 dots, the distance apart will be proportional to the height above the water, by using a different wave length laser for each laser you will be able to know whether it is too high or too low. Only a 0.5w laser will shine through any amount of spray and you can get them for $10, and the camera needed to work with a Pi is only $50, the software to process the camera and do the math will be trivial. It will be far faster than any of the other sensors because you don't need to wait for a return of any kind, you just grab a frame from the camera and process it. In fact it will be very quick because you will just tell the software to increase the servo until there is only one dot or decrease it. So very simple, just count how many bright pixels in the picture and move the servos until you have the least amount of bright pixels, no need to take any measurement other than the number of bright pixels from the camera. Light from spray wont be anywhere near the intensity from the surface, so you tell it to only count pixels from the image that are at the maximum value. You will also only process pixels in the image that are in the correct zone, which will make it blisteringly quick and once again reflections and spray outside the target area wont even be looked at. I challenge anyone else to find a faster method of keeping level.

    laser2.jpg


    Here is what the camera sees, using a red laser and a green laser. You only need to check the small areas and calculate the amount of red pixels and green pixels in each area. Actually from doing these diagrams I can see you only need a single coloured laser, if you have light in-between the bright dots you know you are too high.
    laser8.jpg
     
    Last edited: Aug 18, 2018
  7. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    As easy as ABC.
    Just have 2 red lasers and divide the picture up into 3 parts like this.
    The code would be;
    Are there bright red pixels in A and B and C if so move down.
    Are there bright red pixels in A and C but none in B then move up.
    Are there bright red pixels in just B then you are level.

    abc.jpg
     
    Last edited: Aug 18, 2018
  8. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    Here are the type of laser diodes used in traffic speeding devices, 75W pulsed, which is perfect, no need to have them on continuously and range of hundreds of metres will cut through any spray like a tuna fish. Uses 905nm which means it is invisible to the human eye. The Raspberry Pi infra-red camera I already have, I am going to get a couple of these and try it out.
    Uses 40 freeking amps of current. Good job it's pulsed this baby will fry the fish.

    https://www.mouser.com/ds/2/311/SPL PL90_3-461392.pdf

    On second thoughts I think 0.5w is more than enough, probably go for these. I can plug these into the raspberry without any mods.

    https://www.mouser.co.za/datasheet/...powered-860nm-vcsels-product-spec-1374989.pdf
     
  9. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    OK it just got better, as the camera and lasers will be fixed, the lasers do not need to be on at the same time, in fact you don't even need 2 lasers you just need the laser and camera to be in fixed positions and with just the one laser you will know where the dot should be for different heights. As simple as it gets, with a 0.5w it will be immune to anything other than solids getting in the way.

    laser10.jpg


    This is what a 0.5W laser looks like more than enough power, probably get away with a 0.1W.

    laser409.jpg
     
  10. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    OK I have connected the Raspberry Pi up to the Arduino, so now I have WiFi and full remote control. Which is important because my test rig will rotate in circles and the dozens of wires would be impossible to rig up to a rotating test rig. I have also connected my infra-red video camera up to it and will take video of the foil when I am testing and I can test my range theory using the infra-red laser that will also be taking it's own measurements. I am going to go with 2 foils front and rear and see if I can get the computer to fly the thing and if successful I am going to see if I can get it to control and balance a single foil.
     
  11. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    Here's the code for controlling both the Ultrasonic and Laser range finders together with the servo, you just connect the laser in parallel to the Ultrasonic they have the same connections. This Code just prints the Pitch Angle of the boat, the Ultrasonic distance and the Laser Distance. It's very messy but I don't plan on using any of this code going forward, so I didn't want to waste any time cleaning it up.

    Code:
    #include "NineAxesMotion.h"       
    #include <Wire.h>
    #include <Servo.h>
    #include "Adafruit_VL53L0X.h"
    
    NineAxesMotion mySensor;         
    unsigned long lastStreamTime = 0;     
    const int streamPeriod = 20;          //To stream at 50Hz without using additional timers (time period(ms) =1000/frequency(Hz))
    bool updateSensorData = true;         //Flag to update the sensor data. Default is true to perform the first read before the first stream
    
    Servo myservo;  // create servo object to control a servo
    // twelve servo objects can be created on most boards
    
    int pos = 0;    // variable to store the servo position
    int myPitchAngle = 0;
    long duration;
    int distance;
    
    const int trigPin = 10;
    const int echoPin = 11;
    
    Adafruit_VL53L0X lox = Adafruit_VL53L0X();
    
    void setup() //This code is executed once
    {   
      //Peripheral Initialization
      Serial.begin(115200);           //Initialize the Serial Port to view information on the Serial Monitor
      I2C.begin();                    //Initialize I2C communication to the let the library communicate with the sensor.
    
      myservo.attach(9);  // attaches the servo on pin 9 to the servo object
    
      pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
      pinMode(echoPin, INPUT); // Sets the echoPin as an Input
      pinMode(echoPin,INPUT_PULLUP);
    
      //Sensor Initialization
      mySensor.initSensor();          //The I2C Address can be changed here inside this function in the library
      mySensor.setOperationMode(OPERATION_MODE_NDOF);   //Can be configured to other operation modes as desired
      mySensor.setUpdateMode(MANUAL);  //The default is AUTO. Changing to MANUAL requires calling the relevant update functions prior to calling the read functions
      //Setting to MANUAL requires fewer reads to the sensor
    
    
      Serial.println("Adafruit VL53L0X test");
      if (!lox.begin()) {
        Serial.println(F("Failed to boot VL53L0X"));
        while(1);
      }
      // power
      Serial.println(F("VL53L0X API Simple Ranging example\n\n"));
    }
    
    
    
    void loop()
    {
      if (updateSensorData)  //Keep the updating of data as a separate task
      {
        mySensor.updateEuler();
        mySensor.updateLinearAccel();
        mySensor.updateMag();
        mySensor.updateGyro();
        mySensor.updateCalibStatus();  //Update the Calibration Status
        updateSensorData = false;
      }
    
     
     
      if ((millis() - lastStreamTime) >= streamPeriod)
      {
        lastStreamTime = millis();   
    
        //Euler
        
        //Serial.print(" H: ");
        //Serial.print(mySensor.readEulerHeading());
        //Serial.print(", ");
    
        //Serial.print(" R: ");
        //Serial.print(mySensor.readEulerRoll());
        //Serial.print(",");
    
        //Serial.print(" P: ");
        myPitchAngle=(mySensor.readEulerPitch());
        Serial.println(" ");
        Serial.print(myPitchAngle);
        Serial.print(", ");
     
        
        //Acceleration
        
        //Serial.print(" AccelX: ");
        //Serial.print(mySensor.readLinearAccelX());
        //Serial.print(", ");
        
        //Serial.print(" AccelY: ");
        //Serial.print(mySensor.readLinearAccelY());
        //Serial.print(", ");
        
        //Serial.print(" AccelZ: ");
        //Serial.print(mySensor.readLinearAccelZ());
        //Serial.print(", ");
    
        //Magnetometer
        
        //Serial.print(" MagX: ");
        //Serial.print(mySensor.readMagX());
        //Serial.print(", ");
        
        //Serial.print(" MagY: ");
        //Serial.print(mySensor.readMagY());
        //Serial.print(", ");
        
        //Serial.print(" MagZ: ");
        //Serial.print(mySensor.readMagZ());
        //Serial.print(", ");
    
        //Gyroscope
        
        //Serial.print(" GyroX: ");
        //Serial.print(mySensor.readGyroX());
        //Serial.print(", ");
    
        //Serial.print(" GyroY: ");
        //Serial.print(mySensor.readGyroY());
        //Serial.print(", ");
        
        //Serial.print(" GyroZ: ");
        //Serial.print(mySensor.readGyroZ());
        //Serial.print(", ");
        
        //Serial.println();
    
        updateSensorData = true;
    
    //Code added to standard motion sensor code
    
    
       //Code for Ultrasonic Distance
      
       // Clears the trigPin
        digitalWrite(trigPin, LOW);
        delayMicroseconds(2);
        // Sets the trigPin on HIGH state for 10 micro seconds
        digitalWrite(trigPin, HIGH);
        delayMicroseconds(10);
        digitalWrite(trigPin, LOW);
        // Reads the echoPin, returns the sound wave travel time in microseconds
        duration = pulseIn(echoPin, HIGH);
        // Calculating the distance
        distance= duration*0.034/2*10;
        // Prints the distance on the Serial Monitor
        //Serial.print("Distance: ");
        Serial.print(distance);// Clears the trigPin
        Serial.print(", ");
        delay(100);
    
       //Code for Laser Range Finder
      
       VL53L0X_RangingMeasurementData_t measure;
    
       //Serial.print("Reading a measurement... ");
       lox.rangingTest(&measure, false); // pass in 'true' to get debug data printout!
      
       if (measure.RangeStatus != 4) {  // phase failures have incorrect data
        Serial.print(measure.RangeMilliMeter);
        
       } else {
        Serial.println(" out of range ");
       }
        
       delay(100);
    
       //Code for Servos
    
        if (myPitchAngle > 3.0){  //Boat pitching too high
          //Serial.print("High");
          myservo.write(65);
          delay(100);
        }else if(myPitchAngle < -3.0){  //Boat pitching too low
          //Serial.print("Low");
          myservo.write(115);
          delay(100);
        }else if (distance > 300){  //Boat too high
          //Serial.print("High");
          myservo.write(65);
          delay(100);
        }else if (distance < 100){  //Boat too low
          //Serial.print("Low");
          myservo.write(115);
          delay(100);
        }else{
         //Serial.print("Middle"); //Boat OK
          myservo.write(90);
          delay(100);
        }
    
      } 
        
    }
     
  12. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    Super excited just finished making my test rig. Made it using a counter balance so by using the laser I can measure force by measuring the distance the foil rises.
    foil6.jpg
    I bolted the wind shield motor to a ladder, I bolted everything together with thick 5mm Aluminium plate, the tubes are also Aluminium but I used a steel angle iron to bolt to the motor shaft, so that I could cut a slot in steel for the woodruff key, this is a piece of metal that sits in the shaft to stop the gear spinning, if I did not use this with the tubing they would spin on the shaft, been there done that got the t-shirt, no matter how much you tighten something onto a shaft, if there is not some sort of anti-spinning plan then it will slip.

    foil8.jpg
    foil5.jpg
    foil1.jpg

    foil16.jpg
    foil2.jpg
    View attachment 142642
    foil13.jpg

    View attachment 142644
    foil10.jpg
    foil12.jpg
    By accurately measuring the weight, I have good scales and with it being such a large circle 4m, then if I can measure the distance above the water accurately then I can calculate the force accurately.

    The larger you make your balance the more torque available to any imbalance therefore the more force to overcome any friction. So to make a very accurate balance you want to make the length as long as possible. This balance will be 4m in length and I can detect movement of only a mm, so it should be very accurate.


    I just finished it as the sun was going down, so I will rig it up tomorrow and start testing.
     

    Attached Files:

    Last edited: Aug 22, 2018
  13. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    OK time for some maths.

    The radius of the circle the foil will travel will be 2m therefore the circumference and the distance the foil will travel with one revolution will be;

    2 x Pi x 2 = 12.57m

    Therefore to get the foil to move at 10kts will be;

    10 x 0.514444 = 5.14m/s

    5.14/12.57 = 0.4 revolutions per second

    When the test rig is spinning a 1 revolution per second then the foil will be doing 24.43kts.

    Which is very cool because the wind-shield motor runs at about 2 revolutions per second at full speed which means I can test up to 50kts.
     
  14. M&M Ovenden
    Joined: Jan 2006
    Posts: 345
    Likes: 69, Points: 38, Legacy Rep: 527
    Location: Ottawa

    M&M Ovenden Senior Member


  15. MurphyLaw
    Joined: Aug 2012
    Posts: 123
    Likes: 5, Points: 18, Legacy Rep: 10
    Location: Mars

    MurphyLaw Senior Member

    Looked at that route already, controlling 4 motors to provide powered lift does not have much similarity to controlling a foil. They use barometric pressure for height/depth adjustment, I spent a lot of time looking at the code, you would have to hack so much, it's not worth the hassle, quicker and better to do a new one for foiling. Then a lot of their hardware all hinges around barometric and multiplies the cost of the individual components 10 fold. Also so much of their hardware is to do with communicating the long distances needed for aero drones, which is not needed at all for foiling. If anything you need to use low frequency sonar to communicate with stuff under the water.
    I want to create a tool for producing better foiling craft so others can try creating their own foiling craft, not interested at all in producing a plug in play device for rich kids so they don't have to overload their 2 second attention span. The part about pushing the boundaries of drone control is the part they are charging for, their code will fly the drone for you while you make your drone do stuff you want it to do, the flying itself is what I am interested in, I don't want someone else's idea of what is the best way to control a foil and I don't really want to push my way of what I think is the best way, I want to create something that people can use to control foils the way they want them controlled for the least amount of money. Like here is the cheapest way to control a foil and if you want it to do this then this is how you do it, off you go enjoy.
     
    Last edited: Aug 22, 2018
Loading...
Forum posts represent the experience, opinion, and view of individual users. Boat Design Net does not necessarily endorse nor share the view of each individual post.
When making potentially dangerous or financial decisions, always employ and consult appropriate professionals. Your circumstances or experience may be different.