Arduino nano quadcopter

Hi everybody,

I'm creating a home made quad with Arduino.
All was good since I tested radio pwm with PIN_CHANGE_INTERRUPT and PIDs.
The problem I thought was the function writeMicroseconds() of Servo.h library because I tought it was too slow for this application but I'm not sure.
If I send max throttle command the command is elaborated correctly and also the value written in ESC is correct (checked both with bluetooth telemetry and serial monitor prints) but instead of take off the quads starts trembling.
Can someone help me to write ESCs PWM values with port manipulation ?
I read something about the use of OCR...
Thanks for your help, I hope the problem is this one

If I send max throttle command the command is elaborated correctly and also the value written in ESC is correct (checked both with bluetooth telemetry and serial monitor prints) but instead of take off the quads starts trembling.

If the motors are at max throttle does it matter whether they got there using WriteMicroseconds() or some other method ?

In my opinion, yes.
I had a similar problem when I picked up the PWM using the pulseIn() function that was too slow and I had to do the same with PIN_CHANGE_INTERRUPT and if writeMicroseconds() was too slow I would have problems writing the value in the ESC because it's too slow to to the rest of the code (reading PWM and PIDs).

I wrote that the maximum value is set in the variable and it is printed correctly 2000 but I never said that the motors can reach this value (which they do not reach).
I'm sure of what is written above because carrying out the tests by connecting the ESC directly to the receiver's channel 3 you immediately see that the engine runs much faster than it does trembling in the quadcopter.

The problem I thought was the function writeMicroseconds() of Servo.h library because I tought it was too slow for this application but I'm not sure.

How long does it take for "writeMicroseconds" to execute?
What is a servo frame time?

AWOL:
How long does it take for "writeMicroseconds" to execute?
What is a servo frame time?

I don't know how writeMicroseconds takes to be executed but absolutely much more than using port manipulation.

I've not understood the second question

carrying out the tests by connecting the ESC directly to the receiver's channel 3 you immediately see that the engine runs much faster than it does trembling in the quadcopter.

Are you saying that when the Arduino sends pulses of 2000 microseconds in length that the motor runs at a different speed to when the RC receiver sends pulses of the same length ?

If so then what is different between the two cases ?
How is the quad powered in each case, for instance ?

Answers;
A) a few tens of microseconds (I haven't timed it)

B) 20 milliseconds.

UKHeliBob:
Are you saying that when the Arduino sends pulses of 2000 microseconds in length that the motor runs at a different speed to when the RC receiver sends pulses of the same length ?

If so then what is different between the two cases ?
How is the quad powered in each case, for instance ?

No, I think that there will be problems in timing because if I connect the motor to Channel 3 of the receiver It rotates faster because my quad has PID loop that modifies pure throttle value.

Main loop does three operations:

  1. read IMU (MPU 6050)
  2. PID calc (without libraries I did by myself)
  3. write values in Motors

Motors seems don't like something when writing values in ESCs; they tremle and the impression seeing this behaviour is that another value is arrived when the One before is not finished.

The quad is powered by both 2S and 3S LiPo and the VBAT is filtered by a BEC to use 5V.

Something wrong with the code we haven't seen yet?

AWOL:
Something wrong with the code we haven't seen yet?

Witch part of code do you want to see ? It's splitted in multiple .h and .cpp files (PID,IMU,OUTPUT,SETUP,...)
When I came back home I can send parts of It

Thank you.

Meanwhile can someone help me to write pwm to ESCs using porta manipulation ?

Meanwhile cancellare someone help me to write pwm to ESCs using porta manipulation ?

Why?
Do you expect something that operates on the order of tens of nanoseconds to make a significant difference to something that operates on the order of tens of milliseconds?

I'm just trying to understand the problem and the only point that regards the output of the engines is the writeMicroseconds (). I repeat with the radio signal the problem was given by the pulseIn () function too slow for the purpose.
I attach below the function that writes the pwm values

void pid(Servo mt[],float desired_angle[],float error[],float Total_angle[],float p[],float i[],float d[],float previous_error[],float elapsedTime,float PID[],float speedVec[],volatile int radioChannelCommands[]){
int yawValue;

//controllo che i valori pwm dei canali siano entro l'intervallo [1000;2000]
for(int i=0;i<4;i++){
if(radioChannelCommands < 1000)
_ radioChannelCommands = 1000;_
_ if(radioChannelCommands > 2000)
radioChannelCommands = 2000;
* }
//dopo aver normalizzato i valori mappo i comandi di roll e pitch ai rispettivi angoli della modalità stabilized*_

* desired_angle[0] = map(radioChannelCommands[0],1000,2000,45,-45);
desired_angle[1] = map(radioChannelCommands[1],1000,2000,45,-45);
//Serial.println("Angoli desiderati --> " + String(desired_angle[0]) + " " + String(desired_angle[1]));
error[0] = Total_angle[0] - desired_angle[0]; //calcolo l'errore tra l'angolo desiderato e quello misurato effettivamente
error[1] = Total_angle[1] - desired_angle[1]; //calcolo l'errore tra l'angolo desiderato e quello misurato effettivamente*

* Serial.println("Errori --> " + String(error[0]) + " " + String(error[1]));*
* //Applico il valore proporzionale dei PID*
_ p[0] = kperror[0];
p[1] = kperror[1];

* //L'integrale come spiegato nella documentazione agisce solo in un intorno relativamente stretto allo zero. Io ho scelto da [-2 ; +2]. Essendo l'inverso dell'azione derivativa esso ne smorza il comportamento*
* if(-3 < error[1] <3){
i[1] = i[1] + (kierror[1]);

* }
if(-3 < error[0] <3){
i[0] = i[0] + (kierror[0]);

* }
//La derivata, o componente derivativa, agisce sulla velocità di convergenza dell'errore*

d[0] = kd*((error[0] - previous_error[0])/elapsedTime);
d[1] = kd*((error[1] - previous_error[1])/elapsedTime);
* //Il PID finale è la somma delle tre componenti calcolate*
* PID[0] = p[0] + i[0] + d[0];
PID[1] = p[1] + i[1] + d[1];
//Serial.println("I PID per i due assi --> " + String(PID[0]) + " " + String(PID[1]));
/ Il PID agisce in un intervallo massimo che va da -1000 microsecondi a +1000 microsecondi. Matematicamente [-1000;+1000].

* Dato che comunque è praticamente impossibile che esso debba agire su tutta la scala, ovvero portare i valori da 2000 a 1000_

_ * e viceversa ci limitiamo a farlo lavorare in un intervallo [-600;+600] che è più che sufficiente a bilanciare i sistema e_
_ * alleggerisce il runtime*/
* if(PID[1] < -600){PID[1]=-600;}
if(PID[1] > 600) {PID[1]=600; }
if(PID[0] < -600){PID[0]=-600;}
if(PID[0] > 600) {PID[0]=600;}
if(radioChannelCommands[3] > 1510){
yawValue = map(radioChannelCommands[3],1500,2000,0,150); //ho scelto che la differenza massima tra i motori sulle diagonali durante la manovra sia al massimo 200*

speedVec[0] = /115 +/ radioChannelCommands[2] - PID[1] + PID[0] - yawValue;
speedVec[1] = /115 +/ radioChannelCommands[2] - PID[1] - PID[0] + yawValue;
speedVec[2] = /115 +/ radioChannelCommands[2] + PID[1] + PID[0] + yawValue;
speedVec[3] = /115 +/ radioChannelCommands[2] + PID[1] - PID[0] - yawValue;
* }
else if(radioChannelCommands[3] < 1490){
yawValue = map(radioChannelCommands[3],1500,2000,50,150); //ho scelto che la differenza massima tra i motori sulle diagonali durante la manovra sia al massimo 200*

speedVec[0] = /115 +/ radioChannelCommands[2] - PID[1] + PID[0] + yawValue;
speedVec[1] = /115 +/ radioChannelCommands[2] - PID[1] - PID[0] - yawValue;
speedVec[2] = /115 +/ radioChannelCommands[2] + PID[1] + PID[0] - yawValue;
speedVec[3] = /115 +/ radioChannelCommands[2] + PID[1] - PID[0] + yawValue;
* }
else{
speedVec[0] = /115 +/ radioChannelCommands[2] - PID[1] + PID[0];
speedVec[1] = /115 +/ radioChannelCommands[2] - PID[1] - PID[0];
speedVec[2] = /115 +/ radioChannelCommands[2] + PID[1] + PID[0];
speedVec[3] = /115 +/ radioChannelCommands[2] + PID[1] - PID[0];
}
//Questo controllo è fondamentale per evitare che qualcuno possa settare regimi minimi pericolosi in grado di arrecare gravi danni a persone o cose*

* if(MIN_RPM >= 1000 && MIN_RPM <= 1150 ){
if(speedVec[0] < MIN_RPM){ speedVec[0] = MIN_RPM; }*

* if(speedVec[0] > 2000){ speedVec[0] = 2000; }_
if(speedVec[1] < MIN_RPM){ speedVec[1] = MIN_RPM; }
_ if(speedVec[1] > 2000){ speedVec[1] = 2000; }_
if(speedVec[2] < MIN_RPM){ speedVec[2] = MIN_RPM; }
_ if(speedVec[2] > 2000){ speedVec[2] = 2000; }_
if(speedVec[3] < MIN_RPM){ speedVec[3] = MIN_RPM; }
_ if(speedVec[3] > 2000){ speedVec[3] = 2000; }
}else{
if(speedVec[0] < 1050){ speedVec[0] = 1050; }
if(speedVec[0] > 2000){ speedVec[0] = 2000; }
if(speedVec[1] < 1050){ speedVec[1] = 1050; }
if(speedVec[1] > 2000){ speedVec[1] = 2000; }
if(speedVec[2] < 1050){ speedVec[2] = 1050; }
if(speedVec[2] > 2000){ speedVec[2] = 2000; }
if(speedVec[3] < 1050){ speedVec[3] = 1050; }
if(speedVec[3] > 2000){ speedVec[3] = 2000; }
}
//Serial.println("i valori dei pid sui 4 motori --> " + String(speedVec[0]) + " " + String(speedVec[1]) + " " + String(speedVec[2]) + " " + String(speedVec[3]));
// delay(4000);
//dopo aver filtrato tutti i valori tramite PID e verificata la loro validità nella scala PWM [1000;2000] posso scrivere i 4 valori sui motori*

* mt[0].writeMicroseconds(speedVec[0]);
mt[1].writeMicroseconds(speedVec[1]);
mt[2].writeMicroseconds(speedVec[2]);
mt[3].writeMicroseconds(speedVec[3]);*_

* previous_error[0] = error[0]; //pitch error diventa pitch previous error.
previous_error[1] = error[1]; //roll error diventa roll previous error.
_ //delay(3000);
}*_

edocit:
Motors seems don't like something when writing values in ESCs; they tremle and the impression seeing this behaviour is that another value is arrived when the One before is not finished.

So your saying your PID routine is changing values before your ESC can get the motor up to the previous commander value and your thinking the fault is time it takes for your nano to send a pulse?

Sounds like you could easily confirm that by slowing down your PID update.

Slumpert:
So your saying your PID routine is changing values before your ESC can get the motor up to the previous commander value and your thinking the fault is time it takes for your nano to send a pulse?

Sounds like you could easily confirm that by slowing down your PID update.

exactly one of the options I was thinking to try.
Suggest to slow down the pid by running the function for example every 200 cycles of the main loop ()?

Otherwise how?

Hi,
Welcome to the forum.

Please read the first post in any forum entitled how to use this forum.
http://forum.arduino.cc/index.php/topic,148850.0.html then look down to item #7 about how to post your code.
It will be formatted in a scrolling window that makes it easier to read.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Have you some simple code that you can load that JUST runs the ESC at FULL SPEED, forget any coding for the rest of the machine.
Just a simple bit of code no PID.

Thanks.. Tom.. :slight_smile:

TomGeorge:
Hi,
Welcome to the forum.

Please read the first post in any forum entitled how to use this forum.
http://forum.arduino.cc/index.php/topic,148850.0.html then look down to item #7 about how to post your code.
It will be formatted in a scrolling window that makes it easier to read.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Have you some simple code that you can load that JUST runs the ESC at FULL SPEED, forget any coding for the rest of the machine.
Just a simple bit of code no PID.

Thanks.. Tom.. :slight_smile:

Hi, sorry for the formatting of the code ... I will make it according to the guidelines.
Here a simple code for all 4 motors at full speed.
I've done a picture of the drone but I don't know how to upload.
By the way it's simply motor 1 attacched to pin D8,motor 2 attacched to pin D9,motor 3 attacched to pin D10,motor 4 attacched to pin D11; then I have MPU 6050 attacched in the I2C pins.

Servo mt[4];

setup(){
for(int i=0;i<4;i++){
mt[ i ].attach(8+i);
mt[ i ].writeMicroseconds(1000);
}
}

loop(){
for(int i=0;i<4;i++){
mt[ i ].writeMicroseconds(2000);
}
}

Hi,
Thanks for the code, now what does it do in your project, do you get full speed?

Try one ESC on its own.

Do you have the ESC power supply gnd connected to the Nano gnd?

Tom... :slight_smile:

TomGeorge:
Hi,
Thanks for the code, now what does it do in your project, do you get full speed?

Try one ESC on its own.

Do you have the ESC power supply gnd connected to the Nano gnd?

Tom... :slight_smile:

yes all connections are correct.
If i run the code without PID filter I get full speed.
If I add pid I cannot get full speed though values in the variable are correct setted as 2000 (if the throttle stick is fully up), motors shake and the quad doesn't take off.
The problem is all here (and we all know that a quadcopter without PIDs is unflyable).

In my projects all the code you see in the post before writes the 4 different values for the motor by sum throttle radio input with the x-axis pid and y-axis pid according to values in range [1000;2000] (if greater than 2000 value will be 2000 if smaller than 1000 it will be 1000)

//controllo che i valori pwm dei canali siano entroYou do realise the forum has an Italian section?

AWOL:
//controllo che i valori pwm dei canali siano entroYou do realise the forum has an Italian section?

Ok but I want to ask here because I think there are much more programmers that could answers