0% found this document useful (1 vote)
178 views10 pages

Project Drone

C++ arduino drone program
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (1 vote)
178 views10 pages

Project Drone

C++ arduino drone program
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 10

////////////////////////////////////////////////////////////////////////////////

///////
//Terms of use
////////////////////////////////////////////////////////////////////////////////
///////
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
///////
#include <Wire.h> //Include the Wire.h library so we can communicate with the gy
ro.
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
//PID gain and limit settings
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
float pid_p_gain_roll = 1.3;
//Gain setting for the roll P-control
ler (1.3)
float pid_i_gain_roll = 0.05;
//Gain setting for the roll I-control
ler (0.3)
float pid_d_gain_roll = 15;
//Gain setting for the roll D-control
ler (15)
int pid_max_roll = 400;
//Maximum output of the PID-controlle
r (+/-)
float pid_p_gain_pitch = pid_p_gain_roll;
ller.
float pid_i_gain_pitch = pid_i_gain_roll;
ller.
float pid_d_gain_pitch = pid_d_gain_roll;
ller.
int pid_max_pitch = pid_max_roll;
r (+/-)

//Gain setting for the pitch P-contro

float pid_p_gain_yaw = 4.0;


ller. //4.0
float pid_i_gain_yaw = 0.02;
ller. //0.02
float pid_d_gain_yaw = 0.0;
ller.
int pid_max_yaw = 400;
r (+/-)

//Gain setting for the pitch P-contro

//Gain setting for the pitch I-contro


//Gain setting for the pitch D-contro
//Maximum output of the PID-controlle

//Gain setting for the pitch I-contro


//Gain setting for the pitch D-contro
//Maximum output of the PID-controlle

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
//Declaring Variables
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
byte last_channel_1, last_channel_2, last_channel_3, last_channel_4;
int receiver_input_channel_1, receiver_input_channel_2, receiver_input_channel_3
, receiver_input_channel_4;
int counter_channel_1, counter_channel_2, counter_channel_3, counter_channel_4,
loop_counter;

int esc_1, esc_2, esc_3, esc_4;


int throttle, battery_voltage;
unsigned long timer_channel_1, timer_channel_2, timer_channel_3, timer_channel_4
, esc_timer, esc_loop_timer;
unsigned long timer_1, timer_2, timer_3, timer_4, current_time;
int cal_int, start;
unsigned long loop_timer;
double gyro_pitch, gyro_roll, gyro_yaw;
double gyro_roll_cal, gyro_pitch_cal, gyro_yaw_cal;
byte highByte, lowByte;
float pid_error_temp;
float pid_i_mem_roll, pid_roll_setpoint, gyro_roll_input, pid_output_roll, pid_l
ast_roll_d_error;
float pid_i_mem_pitch, pid_pitch_setpoint, gyro_pitch_input, pid_output_pitch, p
id_last_pitch_d_error;
float pid_i_mem_yaw, pid_yaw_setpoint, gyro_yaw_input, pid_output_yaw, pid_last_
yaw_d_error;
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
//Setup routine
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
void setup(){
Wire.begin();
s master.

//Start the I2C a

DDRD |= B11110000;
//Configure digit
al poort 4, 5, 6 and 7 as output.
DDRB |= B00110000;
//Configure digit
al poort 12 and 13 as output.
//Arduino (Atmega) pins default to inputs, so they don't need to be explicitly
declared as inputs.
//Use the led on the Arduino for startup indication
digitalWrite(12,HIGH);
ning led.
delay(3000);
efor continuing.
Wire.beginTransmission(105);
tion with the gyro (adress 1101001)
Wire.write(0x20);
e to register 1 (20 hex)
Wire.write(0x0F);
r bits as 00001111 (Turn on the gyro and enable all axis)
Wire.endTransmission();
ssion with the gyro

//Turn on the war


//Wait 2 second b
//Start communica
//We want to writ
//Set the registe
//End the transmi

Wire.beginTransmission(105);
//Start communica
tion with the gyro (adress 1101001)
Wire.write(0x23);
//We want to writ
e to register 4 (23 hex)
Wire.write(0x90);
//Set the registe
r bits as 10010000 (Block Data Update active & 500dps full scale)
Wire.endTransmission();
//End the transmi
ssion with the gyro

delay(250);
ime to start.

//Give the gyro t

//Let's take multiple gyro data samples so we can determine the average gyro o
ffset (calibration).
for (cal_int = 0; cal_int < 2000 ; cal_int ++){
//Take 2000 readi
ngs for calibration.
if(cal_int % 15 == 0)digitalWrite(12, !digitalRead(12)); //Change the led
status to indicate calibration.
gyro_signalen();
//Read the gyro o
utput.
gyro_roll_cal += gyro_roll;
//Ad roll value t
o gyro_roll_cal.
gyro_pitch_cal += gyro_pitch;
//Ad pitch value
to gyro_pitch_cal.
gyro_yaw_cal += gyro_yaw;
//Ad yaw value to
gyro_yaw_cal.
//We don't want the esc's to be beeping annoyingly. So let's give them a 100
0us puls while calibrating the gyro.
PORTD |= B11110000;
//Set digital poo
rt 4, 5, 6 and 7 high.
delayMicroseconds(1000);
//Wait 1000us.
PORTD &= B00001111;
//Set digital poo
rt 4, 5, 6 and 7 low.
delay(3);
//Wait 3 millisec
onds before the next loop.
}
//Now that we have 2000 measures, we need to devide by 2000 to get the average
gyro offset.
gyro_roll_cal /= 2000;
//Divide the roll
total by 2000.
gyro_pitch_cal /= 2000;
//Divide the pitc
h total by 2000.
gyro_yaw_cal /= 2000;
//Divide the yaw
total by 2000.
PCICR |= (1 << PCIE0);
able PCMSK0 scan.
PCMSK0 |= (1 << PCINT0);
ital input 8) to trigger an interrupt on state change.
PCMSK0 |= (1 << PCINT1);
ital input 9)to trigger an interrupt on state change.
PCMSK0 |= (1 << PCINT2);
ital input 10)to trigger an interrupt on state change.
PCMSK0 |= (1 << PCINT3);
ital input 11)to trigger an interrupt on state change.

//Set PCIE0 to en
//Set PCINT0 (dig
//Set PCINT1 (dig
//Set PCINT2 (dig
//Set PCINT3 (dig

//Wait until the receiver is active and the throtle is set to the lower positi
on.
while(receiver_input_channel_3 < 990 || receiver_input_channel_3 > 1020 || rec
eiver_input_channel_4 < 1400){
start ++;
//While waiting i
ncrement start whith every loop.
//We don't want the esc's to be beeping annoyingly. So let's give them a 100
0us puls while waiting for the receiver inputs.
PORTD |= B11110000;
//Set digital poo
rt 4, 5, 6 and 7 high.
delayMicroseconds(1000);
//Wait 1000us.
PORTD &= B00001111;
//Set digital poo
rt 4, 5, 6 and 7 low.

delay(3);
onds before the next loop.
if(start == 125){
(500ms).
digitalWrite(12, !digitalRead(12));
status.
start = 0;
0.
}
}
start = 0;
to 0.

//Wait 3 millisec
//Every 125 loops
//Change the led
//Start again at

//Set start back

//Load the battery voltage to the battery_voltage variable.


//65 is the voltage compensation for the diode.
//12.6V equals ~5V @ Analog 0.
//12.6V equals 1023 analogRead(0).
//1260 / 1023 = 1.2317.
//The variable battery_voltage holds 1050 if the battery voltage is 10.5V.
battery_voltage = (analogRead(0) + 65) * 1.2317;
//When everything is done, turn off the led.
digitalWrite(12,LOW);
//Turn off the wa
rning led.
}
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
//Main program loop
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
void loop(){
//Let's get the current gyro data and scale it to degrees per second for the p
id calculations.
gyro_signalen();
gyro_roll_input = (gyro_roll_input * 0.8) + ((gyro_roll / 57.14286) * 0.2);
//Gyro pid input is deg/sec.
gyro_pitch_input = (gyro_pitch_input * 0.8) + ((gyro_pitch / 57.14286) * 0.2);
//Gyro pid input is deg/sec.
gyro_yaw_input = (gyro_yaw_input * 0.8) + ((gyro_yaw / 57.14286) * 0.2);
//Gyro pid input is deg/sec.
//For starting the motors: throttle low and yaw left (step 1).
if(receiver_input_channel_3 < 1050 && receiver_input_channel_4 < 1050)start =
1;
//When yaw stick is back in the center position start the motors (step 2).
if(start == 1 && receiver_input_channel_3 < 1050 && receiver_input_channel_4 >
1450){
start = 2;
//Reset the pid controllers for a bumpless start.
pid_i_mem_roll = 0;
pid_last_roll_d_error = 0;
pid_i_mem_pitch = 0;
pid_last_pitch_d_error = 0;
pid_i_mem_yaw = 0;
pid_last_yaw_d_error = 0;
}
//Stopping the motors: throttle low and yaw right.
if(start == 2 && receiver_input_channel_3 < 1050 && receiver_input_channel_4 >
1950)start = 0;

//The PID set point in degrees per second is determined by the roll receiver i
nput.
//In the case of deviding by 3 the max roll rate is aprox 164 degrees per seco
nd ( (500-8)/3 = 164d/s ).
pid_roll_setpoint = 0;
//We need a little dead band of 16us for better results.
if(receiver_input_channel_1 > 1508)pid_roll_setpoint = (receiver_input_channel
_1 - 1508)/3.0;
else if(receiver_input_channel_1 < 1492)pid_roll_setpoint = (receiver_input_ch
annel_1 - 1492)/3.0;
//The PID set point in degrees per second is determined by the pitch receiver
input.
//In the case of deviding by 3 the max pitch rate is aprox 164 degrees per sec
ond ( (500-8)/3 = 164d/s ).
pid_pitch_setpoint = 0;
//We need a little dead band of 16us for better results.
if(receiver_input_channel_2 > 1508)pid_pitch_setpoint = (receiver_input_channe
l_2 - 1508)/3.0;
else if(receiver_input_channel_2 < 1492)pid_pitch_setpoint = (receiver_input_c
hannel_2 - 1492)/3.0;
//The PID set point in degrees per second is determined by the yaw receiver in
put.
//In the case of deviding by 3 the max yaw rate is aprox 164 degrees per secon
d ( (500-8)/3 = 164d/s ).
pid_yaw_setpoint = 0;
//We need a little dead band of 16us for better results.
if(receiver_input_channel_3 > 1050){ //Do not yaw when turning off the motors.
if(receiver_input_channel_4 > 1508)pid_yaw_setpoint = (receiver_input_channe
l_4 - 1508)/3.0;
else if(receiver_input_channel_4 < 1492)pid_yaw_setpoint = (receiver_input_c
hannel_4 - 1492)/3.0;
}
//PID inputs are known. So we can calculate the pid output.
calculate_pid();
//The battery voltage is needed for compensation.
//A complementary filter is used to reduce noise.
//0.09853 = 0.08 * 1.2317.
battery_voltage = battery_voltage * 0.92 + (analogRead(0) + 65) * 0.09853;
//Turn on the led if battery voltage is to low.
if(battery_voltage < 1050 && battery_voltage > 600)digitalWrite(12, HIGH);
throttle = receiver_input_channel_3;
need the throttle signal as a base signal.
if (start == 2){
e motors are started.
if (throttle > 1800) throttle = 1800;
need some room to keep full control at full throttle.
esc_1 = throttle - pid_output_pitch + pid_output_roll
lculate the pulse for esc 1 (front-right - CCW)
esc_2 = throttle + pid_output_pitch + pid_output_roll
lculate the pulse for esc 2 (rear-right - CW)
esc_3 = throttle + pid_output_pitch - pid_output_roll
lculate the pulse for esc 3 (rear-left - CCW)
esc_4 = throttle - pid_output_pitch - pid_output_roll
lculate the pulse for esc 4 (front-left - CW)

//We
//Th
//We
- pid_output_yaw; //Ca
+ pid_output_yaw; //Ca
- pid_output_yaw; //Ca
+ pid_output_yaw; //Ca

if (battery_voltage < 1240 && battery_voltage > 800){


the battery connected?
esc_1 += esc_1 * ((1240 - battery_voltage)/(float)3500);
mpensate the esc-1 pulse for voltage drop.
esc_2 += esc_2 * ((1240 - battery_voltage)/(float)3500);
mpensate the esc-2 pulse for voltage drop.
esc_3 += esc_3 * ((1240 - battery_voltage)/(float)3500);
mpensate the esc-3 pulse for voltage drop.
esc_4 += esc_4 * ((1240 - battery_voltage)/(float)3500);
mpensate the esc-4 pulse for voltage drop.
}

//Is

if
ep the
if
ep the
if
ep the
if
ep the

= 1200;

//Ke

= 1200;

//Ke

= 1200;

//Ke

= 1200;

//Ke

(esc_1
motors
(esc_2
motors
(esc_3
motors
(esc_4
motors

< 1200) esc_1


running.
< 1200) esc_2
running.
< 1200) esc_3
running.
< 1200) esc_4
running.

if(esc_1 > 2000)esc_1 = 2000;


mit the esc-1 pulse to 2000us.
if(esc_2 > 2000)esc_2 = 2000;
mit the esc-2 pulse to 2000us.
if(esc_3 > 2000)esc_3 = 2000;
mit the esc-3 pulse to 2000us.
if(esc_4 > 2000)esc_4 = 2000;
mit the esc-4 pulse to 2000us.
}
else{
esc_1
start is
esc_2
start is
esc_3
start is
esc_4
start is
}

= 1000;
not 2 keep
= 1000;
not 2 keep
= 1000;
not 2 keep
= 1000;
not 2 keep

//Co
//Co
//Co
//Co

//Li
//Li
//Li
//Li

//If
a 1000us pulse for ess-1.
//If
a 1000us pulse for ess-2.
//If
a 1000us pulse for ess-3.
//If
a 1000us pulse for ess-4.

//All the information for controlling the motor's is available.


//The refresh rate is 250Hz. That means the esc's need there pulse every 4ms.
while(micros() - loop_timer < 4000);
//We
wait until 4000us are passed.
loop_timer = micros();
//Se
t the timer for the next loop.
PORTD |= B11110000;
t digital outputs 4,5,6 and 7 high.
timer_channel_1 = esc_1 + loop_timer;
lculate the time of the faling edge of the
timer_channel_2 = esc_2 + loop_timer;
lculate the time of the faling edge of the
timer_channel_3 = esc_3 + loop_timer;
lculate the time of the faling edge of the
timer_channel_4 = esc_4 + loop_timer;
lculate the time of the faling edge of the

//Se
//Ca
esc-1 pulse.
//Ca
esc-2 pulse.
//Ca
esc-3 pulse.
//Ca
esc-4 pulse.

while(PORTD >= 16){


ay in this loop until output 4,5,6 and 7 are low.
esc_loop_timer = micros();
ad the current time.
if(timer_channel_1 <= esc_loop_timer)PORTD &=
t digital output 4 to low if the time is expired.
if(timer_channel_2 <= esc_loop_timer)PORTD &=
t digital output 5 to low if the time is expired.
if(timer_channel_3 <= esc_loop_timer)PORTD &=
t digital output 6 to low if the time is expired.
if(timer_channel_4 <= esc_loop_timer)PORTD &=
t digital output 7 to low if the time is expired.
}
}

//St
//Re
B11101111;

//Se

B11011111;

//Se

B10111111;

//Se

B01111111;

//Se

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
//This routine is called every time input 8, 9, 10 or 11 changed state
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
ISR(PCINT0_vect){
current_time = micros();
//Channel 1=========================================
if(PINB & B00000001){
//Is input 8 high
?
if(last_channel_1 == 0){
//Input 8 changed
from 0 to 1
last_channel_1 = 1;
//Remember curren
t input state
timer_1 = current_time;
//Set timer_1 to
current_time
}
}
else if(last_channel_1 == 1){
//Input 8 is not
high and changed from 1 to 0
last_channel_1 = 0;
//Remember curren
t input state
receiver_input_channel_1 = current_time - timer_1;
//Channel 1 is cu
rrent_time - timer_1
}
//Channel 2=========================================
if(PINB & B00000010 ){
//Is input 9 high
?
if(last_channel_2 == 0){
//Input 9 changed
from 0 to 1
last_channel_2 = 1;
//Remember curren
t input state
timer_2 = current_time;
//Set timer_2 to
current_time
}
}
else if(last_channel_2 == 1){
//Input 9 is not
high and changed from 1 to 0
last_channel_2 = 0;
//Remember curren
t input state
receiver_input_channel_2 = current_time - timer_2;
//Channel 2 is cu
rrent_time - timer_2
}
//Channel 3=========================================

if(PINB & B00000100 ){

//Is input 10 hig

h?
if(last_channel_3 == 0){
d from 0 to 1
last_channel_3 = 1;
t input state
timer_3 = current_time;
current_time
}
}
else if(last_channel_3 == 1){
high and changed from 1 to 0
last_channel_3 = 0;
t input state
receiver_input_channel_3 = current_time - timer_3;
rrent_time - timer_3
}
//Channel 4=========================================
if(PINB & B00001000 ){

//Input 10 change
//Remember curren
//Set timer_3 to

//Input 10 is not
//Remember curren
//Channel 3 is cu

//Is input 11 hig

h?
if(last_channel_4 == 0){
d from 0 to 1
last_channel_4 = 1;
t input state
timer_4 = current_time;
current_time
}
}
else if(last_channel_4 == 1){
high and changed from 1 to 0
last_channel_4 = 0;
t input state
receiver_input_channel_4 = current_time - timer_4;
rrent_time - timer_4
}
}

//Input 11 change
//Remember curren
//Set timer_4 to

//Input 11 is not
//Remember curren
//Channel 4 is cu

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
//Subroutine for reading the gyro
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
void gyro_signalen(){
Wire.beginTransmission(105);
//Start communica
tion with the gyro (adress 1101001)
Wire.write(168);
//Start reading @
register 28h and auto increment with every read
Wire.endTransmission();
//End the transmi
ssion
Wire.requestFrom(105, 6);
//Request 6 bytes
from the gyro
while(Wire.available() < 6);
//Wait until the
6 bytes are received
lowByte = Wire.read();
//First received
byte is the low part of the angular data
highByte = Wire.read();
//Second received
byte is the high part of the angular data
gyro_roll = ((highByte<<8)|lowByte);
//Multiply highBy
te by 256 (shift left by 8) and ad lowByte

if(cal_int == 2000)gyro_roll -= gyro_roll_cal;


after the calibration
lowByte = Wire.read();
byte is the low part of the angular data
highByte = Wire.read();
byte is the high part of the angular data
gyro_pitch = ((highByte<<8)|lowByte);
te by 256 (shift left by 8) and ad lowByte
gyro_pitch *= -1;
if(cal_int == 2000)gyro_pitch -= gyro_pitch_cal;
after the calibration
lowByte = Wire.read();
byte is the low part of the angular data
highByte = Wire.read();
byte is the high part of the angular data
gyro_yaw = ((highByte<<8)|lowByte);
te by 256 (shift left by 8) and ad lowByte
gyro_yaw *= -1;
if(cal_int == 2000)gyro_yaw -= gyro_yaw_cal;
after the calibration
}

//Only compensate
//First received
//Second received
//Multiply highBy
//Invert axis
//Only compensate
//First received
//Second received
//Multiply highBy
//Invert axis
//Only compensate

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
//Subroutine for calculating pid outputs
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////
//The PID controllers are explained in part 5 of the YMFC-3D video session:
//www.youtube.com/watch?v=JBvnB0279-Q
void calculate_pid(){
//Roll calculations
pid_error_temp = gyro_roll_input - pid_roll_setpoint;
pid_i_mem_roll += pid_i_gain_roll * pid_error_temp;
if(pid_i_mem_roll > pid_max_roll)pid_i_mem_roll = pid_max_roll;
else if(pid_i_mem_roll < pid_max_roll * -1)pid_i_mem_roll = pid_max_roll * -1;
pid_output_roll = pid_p_gain_roll * pid_error_temp + pid_i_mem_roll + pid_d_ga
in_roll * (pid_error_temp - pid_last_roll_d_error);
if(pid_output_roll > pid_max_roll)pid_output_roll = pid_max_roll;
else if(pid_output_roll < pid_max_roll * -1)pid_output_roll = pid_max_roll * 1;
pid_last_roll_d_error = pid_error_temp;
//Pitch calculations
pid_error_temp = gyro_pitch_input - pid_pitch_setpoint;
pid_i_mem_pitch += pid_i_gain_pitch * pid_error_temp;
if(pid_i_mem_pitch > pid_max_pitch)pid_i_mem_pitch = pid_max_pitch;
else if(pid_i_mem_pitch < pid_max_pitch * -1)pid_i_mem_pitch = pid_max_pitch *
-1;
pid_output_pitch = pid_p_gain_pitch * pid_error_temp + pid_i_mem_pitch + pid_d
_gain_pitch * (pid_error_temp - pid_last_pitch_d_error);
if(pid_output_pitch > pid_max_pitch)pid_output_pitch = pid_max_pitch;
else if(pid_output_pitch < pid_max_pitch * -1)pid_output_pitch = pid_max_pitch
* -1;
pid_last_pitch_d_error = pid_error_temp;

//Yaw calculations
pid_error_temp = gyro_yaw_input - pid_yaw_setpoint;
pid_i_mem_yaw += pid_i_gain_yaw * pid_error_temp;
if(pid_i_mem_yaw > pid_max_yaw)pid_i_mem_yaw = pid_max_yaw;
else if(pid_i_mem_yaw < pid_max_yaw * -1)pid_i_mem_yaw = pid_max_yaw * -1;
pid_output_yaw = pid_p_gain_yaw * pid_error_temp + pid_i_mem_yaw + pid_d_gain_
yaw * (pid_error_temp - pid_last_yaw_d_error);
if(pid_output_yaw > pid_max_yaw)pid_output_yaw = pid_max_yaw;
else if(pid_output_yaw < pid_max_yaw * -1)pid_output_yaw = pid_max_yaw * -1;
pid_last_yaw_d_error = pid_error_temp;
}

You might also like