Building a RC pilot kite

You can fence a code block like this:

```c++
int a=1;
```

Looks like this

int a=1; 

https://help.github.com/en/github/writing-on-github/creating-and-highlighting-code-blocks#syntax-highlighting

2 Likes

Thanks @tallakt
I think how my code looks is the least of my code worries…
How I structure code is often wanting. Must improve

The code is simple enough to read. It shows how much work is required just to make the simplest kite steering system. I think this is a good start. My guess is also that it might have a fair chance of working, especially when you add PID.

I would think finding the correct feedback control parameters could be the biggest hurdle between this and a working system.

What you have already is a P controller. For very many systems, the P controller would be the best choice. You could easily change the proportional gain by changing the (-40,40) limits.

Curious to hear how it flies

To fix the drift of the IMU perhaps just calculate the roll angle from the accelerometer manually. Then use the integrated (as in summing gyro value times sampling time) gyro reading in the x axis (roll) with a bandpass filter, and add to the lowpass filtered value from the accelerometer.

Sounds complicated perhaps, but it is not really… If you figure out the sampling time and time constants for your filters, the DSP package in Julia is free and will generate some parameters for the filters to be inserted into your code…

For steering a kite, suggested parameters for filters would be 10 seconds for the accel lowpass filter, and 10 to 2 seconds for the gyro filter… Then just wait 10-15 seconds before launching the kite.

https://juliadsp.github.io/DSP.jl/stable/filters/#DSP.Filters.Bandpass

3 Likes

@tallakt, you’re amazing. Thanks for your comments and suggestions.
but I’m lazy.

After another rummage through parts boxes … I’ve found one of these…
https://www.dfrobot.com/product-788.html
A 6DOF shield to sit on top of the UNO.
After installing the library, the code from
https://wiki.dfrobot.com/6_Dof_Shield__DFR0209_
ran first time.
There’s a handy x roll value which looks much more reliable.
Unfortunately this module is no longer for sale… There must be a better equivalent out there somewhere now.
Shame I don’t have an Xbee. But I can probably get theDSD Bluetooth running from it again.

This code has no filtering whatsoever. Could be a good thing, and if a P controller is sufficiently good then you have something working in no time.

Lazy is good

As the last code does not combine accel and gyro into a single roll, you could make a PD controller really easily by just making the servo output equal to P1 * accel roll + P2 * gyro reading. P1 is the proportional gain and P2 the derivative gain. Maybe the gyros are not sufficiently accurate in lower frequencies to be useful for roll control of a pilot kite. But I expect the gyro is not necessary, except if you want to dampen the yaw axis (which I would aim for).

1 Like

Perhaps using the L/D ratio 2.4 pilot kite in a dynamic way by implementing some components for RC, in such a way that some limited crosswind flight could be realized in order to compensate possible unbalancing from Daisy rotor(s) that are transmitted to the pilot kite by sensors settled in the rotor(s).

2 Likes

Wow, Like so many lock-down jobs…Can’t believe it’s taken me so long to get round to this.
We came back from some cycling, started the coding and
yay. The following tweaked code looks to be running on the DFRobot shield in no time.

@tallakt I may come back to you sometime for an explanation of why the P and D controls would map from accel and gyro like that due to the workings of an IMU… but for now, my brain is the size of a peanut and the pressure of extra knowledge would pop me. I think I read it somewhere before. I’m guessing it’s related to all that funky _atan2 stuff at the bottom.
Anyway… the new code… let’s see if I remember how to show it… got it third attempt, not bad

//Really basic Lift Kite steering 
//Getting rid of mpu6050 and using DFRobot 6dof shield instead.,

//Any value deviating fron 0 straight up maps straight to a servo steering

//roll reading is through a running smoothing matrix

//Reading an value from phone bluetooth and applying it to further offset the kite angle
// only acceptting 1 char at a time just now until we sort out parsing number data
// each offset value is case set as an offset steering angle


//STILL NEED TO ADD full PID 
//BTserial software from somewhere internety

//  Sketch: basicSerialWithNL_001
// 
//  Uses hardware serial to talk to the host computer and software serial 
//  for communication with the Bluetooth module
//  Intended for Bluetooth devices that require line end characters "\r\n"
//
//  Pins
//  Arduino 5V out TO BT VCC
//  Arduino GND to BT GND
//  Arduino D11 to BT RX through a voltage divider
//  Arduino D10 BT TX (no need voltage divider)
//
//  When a command is entered in the serial monitor on the computer 
//  the Arduino will relay it to the bluetooth module and display the result.
//

// # Editor     : Roy from DFRobot
// # Date       : 10.12.2013
// # Product name: 6 Dof shield for Arduino
// # Product SKU : DFR0209
// # Version     : 0.1
// # Description:
// # The sketch for driving the 6 Dof shield for Arduino via I2C interface

#include <FreeSixIMU.h>
#include <FIMU_ADXL345.h>
#include <FIMU_ITG3200.h>

#include <Wire.h>

int16_t angle[2]; // pitch & roll

// Set the FreeSixIMU object
FreeSixIMU sixDOF = FreeSixIMU();
int rawSixDof[6];

#include <SoftwareSerial.h>
SoftwareSerial BTserial(10, 11); // RX | TX BT05 Uno connections


short c = 0;
boolean NL = true;//4


#include <Servo.h>
Servo myservo;  // create servo object to control a servo

long val;    // variable to set as roll value read from MPU6050
int offsetANGLEselected; //a variable to set as chosen offset angle
int steer;  // variable to send to the servo 
int slowcount;  // dont send every reading over bluetooth only when this count is...
int slowamount;// only every 5 (as set in setup) over BT



const int numReadings = 8;  //size of array for smoothing roll readings 

int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average



void setup() {
    Serial.begin(9600);
    Serial.print("Sketch:   ");   Serial.println(__FILE__);
    Serial.print("Uploaded: ");   Serial.println(__DATE__);
    Serial.println(" ");
 
    BTserial.begin(9600);  
    BTserial.print("BTserial started at "); BTserial.println(9600);
    BTserial.println(" ");
    
    delay (1);      //because rods messed with programme timing


  Wire.begin();

  delay(5);
  sixDOF.init();                        //begin the IMU
  delay(5);
     
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
                          readings[thisReading] = 0;} //clear array    
                          Serial.println("smoothing array ready");
      delay(10);



 
      // attaches the servo on pin 9 to the servo object
    myservo.attach(9);  
     
    delay(1);       //because rods messed with programme timing
    
    slowcount=(0);  //count reset
    slowamount=(10);  //count limit

    
    BTserial.print("Sketch:   ");   BTserial.println(__FILE__);
        delay (1);
    BTserial.print("Uploaded: ");   BTserial.println(__DATE__);
        delay (1);
    BTserial.println(" ");
  
}

void loop() {


  sixDOF.getRawValues(rawSixDof);
  angle[0] = _atan2(rawSixDof[0],rawSixDof[2]);
  angle[1] = _atan2(rawSixDof[1],rawSixDof[2]);

  Serial.print("roll X:");              //pitch & roll
  Serial.println(angle[0]/10.0);
  val = (angle[0]/10.0);
  
  Serial.print("pitch Y:");
  Serial.println(angle[1]/10.0);
  Serial.println("");
  delay(100);

  

 if (Serial.available())
    BTserial.write(Serial.read());
     delay(10);
          // Read from the Bluetooth module and send to the Arduino Serial Monitor
          if (BTserial.available())
          {
              c = BTserial.read();
                    switch(c) { 
                      //case 48: /* Number 0 */
                      case '0':
                    offsetANGLEselected = (0);     
                            break;      
                      //case 49: /* Number 1 */
                      case '1':
                    offsetANGLEselected = (5);     
                            break;
                     case '2':
                    offsetANGLEselected = (10);     
                            break;      
                      case '3':
                    offsetANGLEselected = (15);     
                            break;      
                       case '4':
                    offsetANGLEselected = (20);     
                            break;      
                      case '5':
                    offsetANGLEselected = (25);     
                            break; 
                     case '6':
                    offsetANGLEselected = (30);     
                            break;      
                      case '7':
                    offsetANGLEselected = (35);     
                            break;
                      case '8':
                    offsetANGLEselected = (40);     
                            break;      
                      case '9':
                    offsetANGLEselected = (65);     
                            break;         
                            default:
                              //Serial.print("Offset Angle: ");
                             //Serial.println(offsetANGLEselected);
                            BTserial.print("Offset Angle: ");
                            BTserial.print(offsetANGLEselected);

                            break;
                            }
                   delay(1);
           }
              
   
   delay(1);
  
    //Serial.print(val);
      
        total = total - readings[readIndex];// subtract the last reading:
        readings[readIndex] = val;// read roll value from this program:
        total = total + readings[readIndex];// add the reading to the total:
        readIndex = readIndex + 1;// advance to the next position in the array:
        
        if (readIndex >= numReadings) { // if we're at the end of the array...wrap around to the beginning:
           readIndex = 0;             }
           delay(1);        // delay in between reads for stability
           
        average = total / numReadings; // calculate the average:  
            delay(1);   

      
            steer = map(average, -40, 40, 30, 150);      // set human readable kite roll limits to servo range
            delay(1); 

            steer = (steer - offsetANGLEselected);
            if (steer <30) { steer = 30; }
            if (steer >150) { steer = 150; }
            myservo.write(steer);                        // sets the servo position according to roll 
            delay(5);                              // wait for the servo to get there

           // Serial.print("    Steer =  ");
           // Serial.println(steer);  

      slowcount++;
      
      if (slowcount >= slowamount){
                slowcount = (0);
               // BTserial.write(val);
               
                BTserial.println(average);
                BTserial.print("    Steer =  ");
                BTserial.println(steer);


                      //Serial.print(val);
                      //Serial.print("Smoothed Average Angle: ");
                      //Serial.println(average);
                      //Serial.print("    Steer =  ");
                      //Serial.println(steer);  
                }
  
delay (20);
}


int16_t _atan2(int32_t y, int32_t x)   //get the _atan2
{
  float z = (float)y / x;
  int16_t a;
  if ( abs(y) < abs(x) )
  {
    a = 573 * z / (1.0f + 0.28f * z * z);
    if (x<0)
    {
    if (y<0) a -= 1800;
    else a += 1800;
    }
  }
  else
  {
    a = 900 - 573 * z / (z * z + 0.28f);
    if (y<0) a -= 1800;
  }
  return a;
}

Yeah the BT serial screen isn’t nice to read but … It’ll do.
Time for a bit of soldering, then stuff it in a housing and sew it all into a kite.

Really enjoy seeing productive conversation happening. :blush:


Well, I neatened it all up.
Damit maybe I should have tested how much tension a toothed belt needs to be under so as not to slip when it drives. Slips even when it has all the servo weight on the belt.
I’ll probably have to go back to the dual servo horn CW & CCW wound yacht controller style.

OK this was fun but I think I’ve played enough with the lego now. Either the belt slips over the cog or it gets caught, folded, mangled…
There would need to be a guaranteed tension… Elastic extension is possible.
Hmmmm

3D print? that was my immediate thought. I dont understand why you need the rubber thingie…

Maybe I can get some wee rollers arrayed around to push the belt onto the cog.
The winding route is really easy though.
A lot of other work competing for priority for now.

I 3d printed a lego compatible clamp to hold the belt onto the servo head.
That works well. The belt runs without snagging.
Thing is… I should break this out into 2 components…


The battery and servo are too heavy to easily be turned over by the 3 bridle lines.

The controller and sensor head are probably light enough to be turned by the lines… The battery and servo should be fitted to hold lower down on a single tether line.

Damit
I had the whole thing remade and took it out for a good strong wind test.
Tangled in launch and ripped the battery power line apart, knocked the cover off the BT module.
Damit
remake for smoothness coming.
I might post a vid of how it looked

1 Like

I’m closing in on you @Rodread. I just figured out how to send accelerometer data down to my RC transmitter, and from there I can control it quite simply with a Lua script… :wink:

No soldering, no C++

There seems to be some problems we have in common: we want the electronics to align with the kite. For that we need to maintain spacing in the bridles front/aft or left/right. I also think its a good idea to support «pitch» of the controller «pod» with a longish rod… still working on the details

My other thread for reference: Radio Controlled Peter Lynn SSSL

1 Like

You’ll leapfrog me in no time @tallakt

Keeping the lines clean and snag free was my issue today.
It was properly windy and the lifter was being flung around even before launch.
A complete collapse then refill and surge had the lines all run over the mounts, power connector and antenna.
In those conditions, anything which can snag a line will.
So I really like the idea of separating the accelerometer from the controller. That way it could be sewn out of the way onto the kite skin… and then relaying it’s data through a software control through your controller. More batteries on the kite but… a smoother implementation.

Yep, there’s a few things I have to fix before going back out.
Having the IMU and controller boards mounted on the bridling may be a real dumbass move.
Maybe also if I had them on the bottom lines instead of the top would help.
Or made a frame for all the bridle lines to pass through… nah

Here’s roughly what I had…

1 Like

I can probably sort my rig with a smooth interface between the lower bridles and the sensor/controller unit but…

Maybe a good way to neaten this whole design would be to place a one piece solution where the tail attaches to a KAP foil lifter.
Why the KAP kite? : They’re more popular.
Why 1 piece? : Easier for punters.
Why the tail? : It’s away from the bridling for fewer tangles and an easy place for anyone to attach a one piece design. Tail might be a better place to add a wee generator as it won’t spoil the wind over the kite.
Adjusting the bridle to kite centre distance instead of the tension might be the better control line design from the tail of the kite.

Thoughts…

Yay, a partially successful test down…
The controller worked in the air.
The kite went left the controller pulled it back right and vice versa for a good few cycles.
A ridiculously lumpy wind kept trying to collapse the whole rig.
The kite performance probably wasn’t helped by the mass of the controller and IMU being set on the lower lines.
Google Photos
Problems remain.
Why am I even bothering with bluetooth? Doesn’t work when the kite is only even >~15m up
Time for LoRa or Wifi or anything other than Bloody Bluetooth.
I did manage to record data in the phone and briefly control it from the phone …
Briefly - before yet again - borne of pure laziness - I relied on a USB connector for power - which broke again. Knob.
The controller box is mounted to the lower lines using a springy double hoop of 1mm epoxied something rod. It was a lot less prone to snagging this time. Still not foolproof.

The control response from the steering line tension wasn’t massive. But it happened.
The sensor was being flung all over the place with the lumpiness though… This is one of the smoother sections of the reading
21
Steer = 39
-35
Steer = 123
-11
Steer = 87
31
Steer = 30
-26
Steer = 109
-20
Steer = 100
-54
Steer = 150
-6
Steer = 79
-18
Steer = 97
-18
Steer = 97
Meant to be sitting at 0 Steer 90

1 Like

Bluetooth long range? Just kidding.

You might also find wifi a bit «range anxious», depending on the hardware at either side.

For more serious work something like this http://store.rfdesign.com.au/rfd-868x-modem/ works fine but is slightly pricier. Also a radio does raise the bar for your onboard power supply.

Or maybe just do logging on an SD card on your card and do post processing after. This would be my preferred way for something low cost and not too serious. There are some options eg on Sparkfun for sd card logging that you should be able to implement on the arduino in very short time.

Needs some sort of radio.
I was controlling it via Bluetooth so as to be able to reposition the kite in the sky…
Handy for smooth landings, launching tweaking, testing and KAP applications etc.
There’s so much rain today that sonar might be my best bet