RC helicopter UAS

I am building a UAS from a Gaui mini zoom RC remote control helicopter, very similar to the following link except with 2 blades rather than 3;
http://www.buzzflyer.co.uk/Sub-Micro-RC-Helicopters/GAUI-Mini-Zoom-3-Blade/p-97-677/

My intention is to initially mount the helicopter onto the end of a cantilever, therefore restricting it to 1 degree of freedom. The helicopter will then be able to stabilize itself against any disturbances to cantilever angle as well as navigate itself to user defined cantilever angles with high precision.

There are 2 control systems used; 1 to govern the speed of the motor, 1 to control the angle of attack of the blades through the swash-plate servo motors. I plan to maintain a constant speed by varying the voltage to the motor and control the lift through the blade angle of attack. I have already designed the control laws for both of these systems. I am now in the process of implementing what I have designed into the hardware.

My most recent progress was building a speed sensor to monitor the speed of the blades. This was achieved using a similar method to the demo code found here; http://www.arduino.cc/playground/Main/ReadingRPM. I am having some problems due to the interrupt technique that it uses. I plan to fix this by changing the code to use an alternative technique yet to be decided! The video below demonstrates the sensor in operation.

UAS ?

Ultimate Astro-Starship ?

Ultra Automatic Steering?

Universal Assigned Seating?

Unmanned Air System, although I do like the starship abbreviation.

Sorted out the servo control now. I have attempted to sync the rear, front and left servo so that they all raise the swashplate by the same amount throughout a sweep.

What is the problem with the interrupt?

If you watch the video, you can see and hear that occasionally the motor will decelerate. I believe this is because the signal to the ESC is not being sent due to the interrupt function. I'm welcome to any other suggestions to what might be happening though.

Can you show the entire code you use?

I am using a modified version of the fan rpm counter for my network cabinet, and it handles both the rpm, humidity, and temperature sensor just fine, and that is even up to 6000 rpm on the fan... And also handles the ethernet shield on top of it all.

Thank you for having a look;

//Speed & motor code

#include <Servo.h>

volatile byte revcount;
unsigned int w;
unsigned long timeold;
unsigned int nomags;
float pi;

Servo ESC;  //Creates servo object
int pos;  //ESC throttle position (0-180)

void setup(){
  Serial.begin(9600);
  
  //Some ESC initialization - temperemental but seems to work
  ESC.attach(9);  //Attach esc to pin 9 as a "servo"
  pos = 150;
  ESC.write(pos);
  delay(5000);
  pos = 50;
  ESC.write(pos);
  
  attachInterrupt(0, rev_fun, RISING);  //Run rev_fun every time the hall switch rises from low to high (interupt 0 = pin 2)
  
  revcount = 0;
  pi = 3.14;
  timeold = 0;  //Initial time value
  nomags = 1;  //Number of magnets around circumference
}

void loop()
{                               
  for(pos = 50; pos < 150; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    ESC.write(pos);              // tell servo to go to position in variable 'pos
    delay(1500);                       // waits 15ms for the servo to reach the position
  } 
  for(pos = 150; pos>=50; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    ESC.write(pos);              // tell servo to go to position in variable 'pos'
    delay(1500);                       // waits 15ms for the servo to reach the position 
  }
}
 
 void rev_fun()
 {
   revcount++;  //Increase revolution count by 1
   //Serial.println("blip");
   
    if (revcount>= 10) 
    {  //Update RPM every 20 counts, increase this for better RPM resolution, decrease for faster update
       w = 2000*pi*(revcount/nomags)/(millis() - timeold); //Calculating rad/sec by number of revolutions in a time sample 
       timeold = millis();  //Reset timeold for revcount = 0
       revcount = 0;  //Reset rev count
       Serial.print("Speed = ");
       Serial.print(w,DEC);  //Print most recent speed value to serial
       Serial.println(" RAD/s");
    }
 }

The drop of speed have nothing to do with a delay of commands as I see it.

You use a servo to set the speed of the motor, so a delayed command will just leave it at the same speed for a bit longer.

Another thing I did was to have as little as possible in the interrupt, I only got the counter for rpms in there, nothing else.

I then use mills to decide when to do the next readout, and calculate how many times it triggered, and over how long time, and then get the rpm's from that.

Can give you the entire source a bit later, or tomorrow (down with a flu atm :()

davidbrowne:
Unmanned Air System, although I do like the starship abbreviation.

Standard nomeclature for your device is "UAV" - Unmanned Aerial Vehicle; please use this in the future, both so that others will generally know what you are referring to, and so that your future research on the subject will return relevant results.

:slight_smile:

Your ISR routine void rev_fun() has way too much stuff inside it (and those serial commands are real time killers) and is probably the source of your problem symptoms.

Recall that all system interrupts (including timers) are disable while you are inside the ISR function and are not re-enabled until your ISR returns. You should just increment the rev count and then set a global flag and have the loop function test the flag and do the stuff you were doing prior with the ISR.

ISR rules are keep it simple, short, then get the hell out of Dodge.

Lefty

This is basically what I got...

int rpmcount;
unsigned long lastSend;

void setup()
{
  attachInterrupt(0, count, RISING); //0 = Digital pin 2
  Serial.begin(9600);
}

void loop()
{
  if ((millis() - lastSend) >= 5000)
  {
    int rpm = 30*1000/(millis() - lastSend)*rpmcount;
    rpmcount = 0;
    Serial.println(rpm);

    lastSend = millis();
  }
}

void count()
{
  rpmcount++;
}

Stripped all of my networking and humidity sensor code out, and only left the rpm stuff.

This should give you the RPM every 5th second.

This is what I use it for: Mikeys Notes

Thanks for the suggestions. The speed code appears to work with no hiccups now that the calculations code have been placed in the loop rather than the interrupt.

//AOA sweep
//David Browne

//Arduino nano pwm ports
// 3,5,6,9,10,11


#include <Servo.h>
#include "math.h"    
 
Servo rear;  // create servo object to control a servo 
Servo right;
Servo left;
Servo esc;
 
int aoa = 0;    // variable to store the aoa
int angleright;
int angleleft;
int anglerear;

int escspeed;

float fp1 = -0.000011087;
float fp2 = 0.00047226;
float fp3 = -0.0054815;
float fp4 = 0.020924;
float fp5 = -1.631;
float fp6 = 101.18;

float rp1 = -0.0076468;
float rp2 = -1.8592;
float rp3 = 100.73;

float pi = 3.14;
volatile byte revcount;
unsigned int w;
unsigned long timeold = 0;
unsigned int nomags = 1;
 
void setup() 
{
  Serial.begin(9600);
  
  attachInterrupt(0, rev_fun, RISING);  //Run rev_fun every time the hall switch rises from low to high (interupt 0 = pin 2)
  
  rear.attach(3);
  left.attach(6);
  right.attach(5);
  esc.attach(9);
  
  delay(5000);
  esc.write(50);
  delay(2000);
  esc.write(150);
  delay(2000);
  
  right.write(50);
  left.write(140);
  rear.write(50);
} 
 
void loop() 
{ for(escspeed = 50; escspeed < 100; escspeed += 1)
  {
    aoa = 5;
    angleright = fp1*pow(aoa,5) + fp2*pow(aoa,4) + fp3*pow(aoa,3) + fp4*pow(aoa,2) + fp5*aoa + fp6;
    angleleft = 140-angleright+50;
    anglerear = rp1*pow(aoa,2) + rp2*aoa + rp3;
    
    right.write(angleright);
    left.write(angleleft);
    rear.write(anglerear);
    esc.write(escspeed);

    if (revcount>= 10) 
    {  //Update RPM every 20 counts, increase this for better RPM resolution, decrease for faster update
       w = 2000*pi*(revcount/nomags)/(millis() - timeold); //Calculating rad/sec by number of revolutions in a time sample 
       timeold = millis();  //Reset timeold for revcount = 0
       revcount = 0;  //Reset rev count
       Serial.print("Speed = ");
       Serial.print(w,DEC);  //Print most recent speed value to serial
       Serial.println(" RAD/s");
    }
    
    delay (1500);
  }
  
 for(escspeed = 8; escspeed > 5; escspeed -= 1)
  {
    aoa=5;
    angleright = fp1*pow(aoa,5) + fp2*pow(aoa,4) + fp3*pow(aoa,3) + fp4*pow(aoa,2) + fp5*aoa + fp6;
    angleleft = 140-angleright+50;
    anglerear = rp1*pow(aoa,2) + rp2*aoa + rp3;
    
    right.write(angleright);
    left.write(angleleft);
    rear.write(anglerear);
    esc.write(escspeed);
    
    if (revcount>= 10) 
    {  //Update RPM every 20 counts, increase this for better RPM resolution, decrease for faster update
       w = 2000*pi*(revcount/nomags)/(millis() - timeold); //Calculating rad/sec by number of revolutions in a time sample 
       timeold = millis();  //Reset timeold for revcount = 0
       revcount = 0;  //Reset rev count
       Serial.print("Speed = ");
       Serial.print(w,DEC);  //Print most recent speed value to serial
       Serial.println(" RAD/s");
    }
    
    delay (1500);
  }
} 

 void rev_fun()
 {
   revcount++;  //Increase revolution count by 1
   //Serial.println("blip");
 }

The helicopter now has an Arduino nano mounted on it which, along with the servos and the motor, can be powered from the lipo battery:

(edit. photos removed for being too big - will resize another time)

UAS seems to be entirely appropriate nomenclature

davidbrowne:
Unmanned aerial vehicle - Wikipedia
Unmanned aerial vehicle - Wikipedia

UAS seems to be entirely appropriate nomenclature

Thank you for that update; I wasn't aware of this until now! I'll have to keep it in mind for the future. I stand corrected.

:slight_smile:

Haha I'm the original author of that crappy RPM reading code, I never thought people will actually want to use it!! Makes me happy that it actually works for you!

BTW, I am currently using the same code in my CCPM helicopter flight computer as part of the governor with PID control.

Well, thanks for your work. It's been very helpful to me. I adapted the code to use a rolling average over 10 cycles to increase the speed reading frequency, but like the initial code, this has the problem of given a speed reading which is on average 5 cycles old.

Now i've switched to reading the microseconds taken for just 1 cycle for a good resolution, high frequency and low delay.

Rolling average:

void speedcalc()
{
  if (revcount > 0)	
  {
  time = (lap0-lap10);  //(ms)
  w = 20000*pi/time; //Calculating rad/sec by number of revolutions in a time sample
  Serial.print(millis(),DEC);
  Serial.print(",");
  Serial.println(w,DEC);
  revcount = 0;  //Reset rev count
  }
}

 void rev_fun()
{
  lap10=lap9;
  lap9=lap8;
  lap8=lap7;
  lap7=lap6;
  lap6=lap5;
  lap5=lap4;
  lap4=lap3;
  lap3=lap2;
  lap2=lap1;
  lap1=lap0;
  lap0=millis();
  revcount=1;  //Increase revolution count by 1
}

Any reason you are doing it like that?

The code I gave you only uses a "timer" with millis(), you decide how often you want to get the rpm, and it then calculates how many revolutions it had, over how long time, and then returns rpm when you want to know it.

So no matter if it spins with 500 rpm or 3000 rpm, you will still get the reading at a constant rate.

I wonder if there's a better algorithm of measuring RPM. The two ways that are often used both have advantages and disadvantages:

  1. Measuring how long it takes for N revolutions
  • More accurate at lower RPM, but slower update as well
  • Inconsistent update rate, could be a problem for your control system
  1. Measuring the number of revolutions in N seconds
  • Consistent update rate
  • Less accurate at lower RPM
  • Less RPM resolution if you want high update rate

in measuring a motorcycle engine speed i track the time for 10 rpm. It does give variable update times - up to a second at low rpm but it's much more accurate than counting the pulses in (say)100ms. I tried some variations on the theme like timing 5 interrupts at low speeds and 10 at higher, and counting the time for the 10 preceding pulses. neither of these added any real value.

In my case, I am creating a controller to make a rotor turn at ~ 300rad/s which is approximately 50rev/s or 1 rev per 20ms.

For my control system it was desirable to have a high resolution reading, and a fast reading rate. Using an interval of one revolution I got a fast reading rate, but a low resolution. With a rolling average over 10 laps I got the same reading rate, but with a higher resolution.

The same reasoning applies to why I didn't use the fixed time interval; If I used a small time interval I would get a fast reading rate, low resolution. If I used a long time interval I would get a slow reading rate but high resolution.

Fully implemented control system preview: