BlueCopter - Arduino Quadcopter

tnx . i will be great because this code is just for one channel and im sure i f go to edit this i will screw the code and everything go wrong :smiley: .

and some another question in this code we dont have any output pin ?

tnx . i will be great because this code is just for one channel and im sure i f go to edit this i will screw the code and everything go wrong smiley-grin .

Sure I can write the pin-change-interrupt explanation, but I'm going to use my quadcopter code as an example (Arduino Leonardo).. Meaning it won't work on the UNO... If you only got 4 channels you can go with a Arduino Leonardo (it has 5 external interrupts) or Arduino Mega (has 6 external interrupts) and reuse my example code that I posted above... if you don't want to buy another board you can use this library which makes PC-Interrupts look like a walk in the park..The library for the pin-change-interrupts will simplify a lot and make the code look like the example I posted..
Here is the library: Google Code Archive - Long-term storage for Google Code Project Hosting.

sk8amazing:
and some another question in this code we dont have any output pin ?

Well, no, because it's too simple really.. Here is a example of how to output PWM (combine this code with the previous example and give it a go)..

#define YOUR_OUTPUT_PIN 11
void setup(){
pinMode(YOUR_OUTPUT_PIN,OUTPUT);
}
void loop(){
int out = map(rxVal,1000,2000,0,255);
analogWrite(YOUR_OUTPUT_PIN,out);
}

But seriously, you clearly need to read and look at the different examples/tutorials in this forum and out there(internet of things).. I wouldn't mind writing the whole thing for you, but that won't teach you anything!

If you'd like, write your sketch and try it out and in-case it doesn't work and you've tried everything you can, post it here.. We would be more than happy to help you out..

//Basel

tnx . i mean it !!!! im working on it . and if goes wrong i will come and bothering you again . tnx

#define RX_INT_PIN_THROTTLE 3
#define YOUR_OUTPUT_PIN 11

volatile int rxPrev = 0; //variable that will contain the previous time
volatile int rxVal = 0; //variable that will contain the current throttle pulse length

void rxGoesHigh(){
pinMode (YOUR_OUTPUT_PIN, OUTPUT);
/* As soon as we get into this function we measure the current time (with micros)
and put that value in our variable rxPrev. After that we re-attach the same interrupt
but this time we want it to fire when the pin goes low "FALLING". When that happens it
will call the function rxGoesLow */
rxPrev=micros();

attachInterrupt(RX_INT_PIN_THROTTLE,rxGoesLow,FALLING);
}
void rxGoesLow(){
/* As soon as we get into this function we measure the current time (with micros) and
subtract the previous time (rxPrev). By doing that we will get the length of the pulse,
from start to the end. We end this process by re-attaching the throttle pin to a RISING
interrupt, which will repeat the process again... */
rxVal=micros()-rxPrev;
attachInterrupt(RX_INT_PIN_THROTTLE,rxGoesHigh,RISING);

}

void setup(){
attachInterrupt(RX_INT_PIN_THROTTLE,rxGoesHigh,RISING); /We begin by attaching a interrupt
to the throttle pin. the interrupt will fire when the pin goes high "RISING" (meaning the
start of the pulse). When that happens it will call the function rxGoesHigh.
/
}
void loop(){
//Nothing to do here!
int out = map(rxVal,1000,2000,0,255);
analogWrite(YOUR_OUTPUT_PIN,out);
}

i done this and i got some signals on pin 11
it is my ppm signal from receiver

and it is what i got after your code combination

is this pwm or i did edit the code wrong ?

:smiley: i was wrong after i change the throtle on radio and signal never changes and when i unpluged the receiver the signal i had signal from pin 11 still like befor showing on scope

sk8amazing:
:smiley: i was wrong after i change the throtle on radio and signal never changes and when i unpluged the receiver the signal i had signal from pin 11 still like befor showing on scope

ya because you changed the interrupt pin number.. And why did you put pinMode (YOUR_OUTPUT_PIN, OUTPUT); in the interrupt routine??? You only have 2 interrupts on the UNO.. They have the numbers 0 and 1 and are located on pins (2 and 3).. so in the code change the number 3 to a 1 and leave everything as is.. I've fixed the sketch for you... Here is the code, just copy paste and run it..

#define RX_INT_PIN_THROTTLE 1
#define YOUR_OUTPUT_PIN 11


volatile int rxPrev = 0; //variable that will contain the previous time
volatile int rxVal = 0; //variable that will contain the current throttle pulse length



void rxGoesHigh(){
/* As soon as we get into this function we measure the current time (with micros) 
and put that value in our variable rxPrev. After that we re-attach the same interrupt 
but this time we want it to fire when the pin goes low "FALLING". When that happens it 
will call the function rxGoesLow */
  rxPrev=micros();
  
  attachInterrupt(RX_INT_PIN_THROTTLE,rxGoesLow,FALLING);
}
void rxGoesLow(){
/* As soon as we get into this function we measure the current time (with micros) and 
subtract the previous time (rxPrev). By doing that we will get the length of the pulse,
from start to the end. We end this process by re-attaching the throttle pin to a RISING 
interrupt, which will repeat the process again... */
  rxVal=micros()-rxPrev;  
  attachInterrupt(RX_INT_PIN_THROTTLE,rxGoesHigh,RISING);
  
}

void setup(){
pinMode (YOUR_OUTPUT_PIN, OUTPUT);
attachInterrupt(RX_INT_PIN_THROTTLE,rxGoesHigh,RISING); /*We begin by attaching a interrupt
to the throttle pin. the interrupt will fire when the pin goes high "RISING" (meaning the 
start of the pulse). When that happens it will call the function rxGoesHigh.*/
}
void loop(){
//Nothing to do here!
int out = map(rxVal,1000,2000,0,255);
analogWrite(YOUR_OUTPUT_PIN,out);
}

Edit: If this still doesn't work for you, I need you to show me how you hooked everything up.. Just draw a simple schematic.. And can you give me the name of your receiver/transmitter??? Just want to check and make sure that it's really PPM we are talking about..

//Basel

tnx . i didnt know if i change the pins it will go wrong . my radio is wfly wft06x-b and receiver is wfr04s , i will try it know and give you feed back. tnx

i tired your code this happend .... befor i connect receiver to pin1(tx) it has signal and after i connect signal it has change unormally and brushless motor dont work

i upload video of process ...
befor and after i connect receiver to arduino uno

sk8amazing:
i tired your code this happend .... befor i connect receiver to pin1(tx) it has signal and after i connect signal it has change unormally and brushless motor dont work

i upload video of process ...
befor and after i connect receiver to arduino uno

Looking at the video I see that the duty cycle is changing from zero to 100%.. That means that it was hooked right the first time..
Did you read what I wrote in the previous post? Why did you connect the receiver to pin 1????? The code is calling for interrupt 1.. And interrupt 1 is on PIN 3.. Forget about the brushless motor for a second.. Connect the output (pin 11) to the oscilloscope and connect the receiver signal to PIN 3 on the UNO.. Dont change anything in the code, and reupload and test it again.. If you get the same results as in the video, then connect the output (PIN 11) to the ESC input signal and try again.. (Dont forget to connect the GND of the ESC to the GND on arduino)..

When you first start up the ESC remember to put the throttle on 0% (stick all the way down) for at least 3-5 seconds before fiddling with it..

Edit: If you got propellers on then remove them.. We don't want you to lose a finger now do we..?

yummmyyyyyy it seem work fine in the scope .... its late here and i cant start esc's bbeeping in home :smiley: tomorrow morning i will test it ..... if it works fine i will start study arduino tommorow hardly .... .... <3 <3

sk8amazing:
yummmyyyyyy it seem work fine in the scope .... its late here and i cant start esc's bbeeping in home :smiley: tomorrow morning i will test it ..... if it works fine i will start study arduino tommorow hardly .... .... <3 <3

Glad to hear that.. Alright! Good luck with your project =)

//Basel

For those following this: I've updated the code.. Made the sketch run more efficiently and reduced code size.. With this change I managed to find better PID values and thus get better/faster stabilization.. For those interested I've added pictures of the main board and hardware setup (see first post)..

Nice work! :slight_smile:

I am currently doing my own quad on a Arduino MEGA, everything is built and the code is mostly done (quadcopter/quad at master · RomainGoussault/quadcopter · GitHub), I am just doig some PID tuning :cold_sweat:

So I looked at your code, and if I think right you have got 2 modes (depending on the value of rateAngleSwitch).
One mode is a position control loop.
The other one is a rate control loop.

So now begin the questions :slight_smile: :
Which mode is the easiest to tune for a first time?
How did you actually do your tuning? Did you put your quad on some sort of ball joint ? Or did you hold it in your hand ?

thanks!

GoussLegend:
So I looked at your code, and if I think right you have got 2 modes (depending on the value of rateAngleSwitch).
One mode is a position control loop.
The other one is a rate control loop.

That is correct my friend =)!

GoussLegend:
Which mode is the easiest to tune for a first time?

Now this question made me sad.. Because it means that you didn't read through my code correctly.. Let me explain how my closed-loop works..
I got 2 loops, yes, one is as you called it position loop (angle loop) and the other one is the rate loop.. But I like to also call them the slow/outer loop (angle) and the fast/inner loop (rate)..
I just drew a "map" in paint (MsPaint > PhotoShop =P) to make the process clear.. As you can see in the map, PID-rate is part of the inner loop which in turn controls the motors directly.. The reason for this is because of the update rate (400Hz) which is close to the motors PWM refresh rate (~480 to 500Hz)..
The PID-angle is part of the outer loop (because it's slow approx. 50Hz), which corresponds to the receiver refresh rate which in fact is 50Hz also! So PID-angle is like an auto-pilot for the position of the copter.. So to answer your question: You have to tune the rate-PID before the angle-PID!.. If the rate-PID isn't stable you won't get a stable copter with the angle-PID!

Note: I didn't read or see your code (something wrong with github, keep getting errors when I try to open your files).. My answer above assumes that you have one inner loop and one outer loop! Two separate inner loops will only lead to a slow response from the quadcopter which in turn will lead to bad PID values, which in turn will make the quadcopter uncontrollable in harsh environments!

GoussLegend:
How did you actually do your tuning? Did you put your quad on some sort of ball joint ? Or did you hold it in your hand ?

A ball joint would be the safest way.. But because I don't have a ball joint, I used a FAN-stand, like the one on this: http://www.tradeindeed.com/images/Stand-Fan_482-700.gif And tied the copter to the stand top.. When I got the copter pretty stable, I took it out of the stand and held it in my hand during fine tuning (DON'T RECOMMEND THIS, I still got a bunch of scars all over my arms!)

//Basel

Thanks for the nice explanation!

Still one question though:

You have to tune the rate-PID before the angle-PID

I agree with this but how do you practically tune it? I mean with the angle-PID, it's easy to tune, you can look at the angles values or even look at your quad to see if it's stable but with the rate-PID, I have no idea how to tune it.

GoussLegend:
Thanks for the nice explanation!

Still one question though:

You have to tune the rate-PID before the angle-PID

I agree with this but how do you practically tune it? I mean with the angle-PID, it's easy to tune, you can look at the angles values or even look at your quad to see if it's stable but with the rate-PID, I have no idea how to tune it.

ya, I get what you're saying.. The method I'm using today is pretty advanced and requires some expensive software.. I basically made a Matlab script that sends data back and forth to the quadcopter and optimize the PID values.. Before that I did it by hand, this is my old method:

1 ) Put the quadcopter in rate mode
2 ) Increase the throttle until the quadcopter starts to hover on its own..
3 ) Increase the P-term until you observe oscillations.. Now subtract 10% of the value and set it as your P-term ..
4 ) Increase the D-term until you observe oscillations.. Now subtract 10-20% of the value (this is your D-term now)..
5 ) Now go back and increase the P-term with small steps until you observe oscillations.. Then subtract a couple of steps..
6 ) Increase the I-term until you observe oscillations.. These oscillations will have a lower frequency, so step up the I-term slowly.. And be careful when it starts to oscillate.. Because the P- and D-term will kick in, and increase the frequency of the oscillations.. So be careful! After that, substract approx. 10-15% of the value...
7 ) If you are satisfied with the results, then go out and take it for a spin, and observe how it flies and reacts to the wind etc.. Then go back and fine tune your copter if you observed any types of oscillations/control problems..
8 ) Buy me a beer!!!!! :slight_smile:

//Basel

About your old method of rate-PID tuning:
What is your speed setpoint/reference ? I guess 0 deg/s and before your have manually put your quad at 0 deg for roll and pitch angle?

The method I'm using today is pretty advanced and requires some expensive software.. I basically made a Matlab script that sends data back and forth to the quadcopter and optimize the PID values

I know well Matlab and I have got it on my laptop. (My dream would have been to use Simulink :)). Anyway what do you do with your Matlab script? Transfer function?

8 ) Buy me a beer!!!!! smiley

I will send you one to Sweden once my quad is flying :slight_smile:

GoussLegend:
About your old method of rate-PID tuning:
What is your speed setpoint/reference ? I guess 0 deg/s and before your have manually put your quad at 0 deg for roll and pitch angle?

I have trouble understanding what you mean.. But I'll give it a shot.. When the quadcopter is in rate-mode, the set-point is determined by the RC-reciever.. Meaning you change the setpoint directly with the transmitter/Remote control.. For example, if you roll left then the setpoint will be something <0deg/s (negative angular rate), and the opposite if you roll right, >0deg/s (positive angular rate)..

If you're in angle mode, the reciever controls the setpoint of the angle-PID, and the angle-PID will in turn decide the setpoint for the rate-PID...

GoussLegend:
I know well Matlab and I have got it on my laptop. (My dream would have been to use Simulink :)). Anyway what do you do with your Matlab script? Transfer function?

Simulink is slow and not optimal for something you run and optimize in real time.. Sure, you got the Real-Time toolbox, but if you ask me it sucks!

No it's not transfer functions, that would be optimal in-case i modeled the system.. But instead I did the following:

Arduino side: Wrote a whole new sketch that only handles inputs, outputs and gyro, acc calculations.. Meaning all the PID calculations are done in matlab..

Matlab side: I implemented some known PID tuning methods, like Ziegler-Nichols, SIMC, IMC etc.. They are easy to calculate.. It was just a matter of iterating and testing the results I got from the different methods.. The method that gave the best result for my quad. was the SIMC.. Still the results were not perfect, but it got me pretty close to something acceptable.. I know your going to ask why it didn't give me the optimal magical numbers =P! The reason is lag/delay in the system.. Matlab runs fast, a little too fast for the Serial-communication on the arduino.. So by the time arduino sends the data, the quadcopter would have changed its state drastically (not for the human eye maybe, but you can see it if you look at the data).. This kind of lag/delay can lead to early oscillations in the PID-tuning..

The optimal way to run this type of tuning is on the arduino itself.. So maybe write a PID optimizer for the arduino??

//Basel

hi again.
i have some question i fixed my receiver and now i have some question .
how we can change pid values? is it compatible with multiwii gui ?