Go Down

Topic: RC helicopter UAS (Read 7655 times) previous topic - next topic

davidbrowne

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.

http://www.youtube.com/watch?v=WStM2QJQAh0



retrolefty

UAS ?

Ultimate Astro-Starship ?

Ultra Automatic Steering?

Universal Assigned Seating?


davidbrowne

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.

http://www.youtube.com/watch?v=09UEIiHRGY8




bld

What is the problem with the interrupt?
captain-slow.dk | non contagious!

davidbrowne

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.

bld

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.
captain-slow.dk | non contagious!

davidbrowne

Thank you for having a look;

Code: [Select]
//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");
    }
}

bld

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 :()
captain-slow.dk | non contagious!

keeper63


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.

:)
I will not respond to Arduino help PM's from random forum users; if you have such a question, start a new topic thread.

retrolefty

#9
Mar 03, 2011, 06:04 am Last Edit: Mar 03, 2011, 06:06 am by retrolefty Reason: 1
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


bld

This is basically what I got...
Code: [Select]
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: http://captain-slow.dk/2010/12/12/from-drawing-to-reality/
captain-slow.dk | non contagious!

davidbrowne

#11
Mar 03, 2011, 06:52 pm Last Edit: Mar 03, 2011, 06:54 pm by davidbrowne Reason: 1
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.

Code: [Select]
//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)

http://en.wikipedia.org/wiki/Unmanned_Aircraft_System
http://en.wikipedia.org/wiki/Unmanned_Aerial_Vehicle

UAS seems to be entirely appropriate nomenclature



keeper63


http://en.wikipedia.org/wiki/Unmanned_Aircraft_System
http://en.wikipedia.org/wiki/Unmanned_Aerial_Vehicle

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.

:)
I will not respond to Arduino help PM's from random forum users; if you have such a question, start a new topic thread.

zitron

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.

davidbrowne

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:
Code: [Select]
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
}



Go Up