Autonomous Quadcopter

Introduction
For this contest I looked forward into designing and building my own quadcopter. The design features the MAX32630FTHR board, mmWave Sensor from TI for distance measurments, BMP280 barometric sensor, Raspberry PI 3 with NoIR camera and RFM12B for wireless control.

BOM
- 1xMAX32630FTHR
- 1xIWR1443 mmWave Sensor - yet to arrive
- 1xBMP280 - Pressure Sensor Evaluation Board from Adafruit - yet to arrive
- 1xRaspberry PI 3
- 1xNoIR camera for RPi
- 2xRFM12B transceiver module
- 1xArduinoNano
- 4xBSS138
- 8xLED's
- 2xCapacitors
- 10xResistors
- 2.54mm Header Pins
- 2xPrototyping board
- 4xVibration Absorption bolts
- 1xF450 DJI frame kit
- 4xBrushless Motors
- 4xSN20A Mini ESC's from RTFQUADS w/ BLHeli
- 1x4S 3000mAh Turnigy Battery
- 1xLM2594 eval. board
Schematics

Instructions
So this project should be something like this: the MAX32630 the brain of the Quadcopter, controlling the motors through the ESC's based on the PID algorithm that sets the individual speeds for each motor. The remote control powered by an arduino nano connected to an RFM12B transceiver module sends to the MAX32630 the Setpoint for the PID algorithm. RFM12B boards connect via SPI the Nano and the MAX32630. Connected to the MAX board should be an mmWave sensor from TI, IWR1443 for distance measurments to obstacles in front of the Quadcopter. A barometer from Bosch should allow me to keep the Quadcopter at the same altitude.
On top of this setup an Raspberry Pi 3 will have connected a NoIR camera, with IR LED's (winter nights are quite long). The RPi will stream the images from the camera to the laptop or the the iOS or the Android app, BerryCam. The camera will also be attached to a custom camera gimball driven by 2 servo's controlled by the RPi in such a way to create a beautiful panorama or just to move around.
LED's on the bottom of the Quad will make it look nicer in the night.
To power this setup, I'll use the 4S battery from Turnigy. Of course, I cannot supply directly the 2 boards so I'm using an evaluation board with an LM2594, buck-converter, even though in the future I'll move to a smaller solution.
To check the battery level, all cells are connected to a diff op amp that outputs a logic high when one voltage cell is lower than a certain threshold.

1574644232551.png


For the software part, I am writing from scratch the control algorithms and sensor communications (except for the BMI160 which has a great library and RFM12B which I had to make a few modifications as the arduino library for RFM12 is a bit different).

As you can see in this image things did not go as planned. This is the second pair of propellers that I ruin. No one was hurt during the destruction of the propellers...except a small part of my forearm that felt the power of a propeller when spinning at about 85% of power...
Yes, the PID algorithm was badly configured...twice...
The RFM12B module is operating on 3.3V, but the arduino is 5V. Took me a pair of modules to figure that out...
Now I have two other modules, with proper level shifting, done with BSS138 MOSFET's and 10k resistors.

The SW also writes to an SD card various information for later visualization or for debugging purposes.

The Quadcopter's status at this moment is that it flies rather nicely with a nice PID algorithm. The entire computation of the roll and pitch angles takes about 750 microseconds and with this timing I think I can send OneShot125 signals to the ESC and stay in a loop of 1100 us. Currently I am sending 1-2ms signals at a rate of 250 HZ, that is every 4000 us.

Video
Unfortunately, due to the limited time I could not further advance with the development of this project. At this moment I am a final year student and I had to work extensively on my diploma project and prepare for the diploma exam in the last 2 months.

Source Code
Code:
while(1)
  {
 
  tmr.start(); //timer start.
  timerS = tmr.read_us();
 
//***************************************************Angle Computation*************************************************************************
 
  imu.getGyroAccXYZandSensorTime(accData, gyroData, sensorTime, accConfig.range, gyroConfig.range);
  imu.getTemperature(&imuTemperature);
 
  double dt = (double)(tmr.read_us() - timer) / 1000000; // Calculate delta time
 
  //No restriction
  double roll  = atan(accData.yAxis.scaled / sqrt(accData.xAxis.scaled * accData.xAxis.scaled + accData.zAxis.scaled * accData.zAxis.scaled)) * RAD_TO_DEG;
  double pitch = atan2(-accData.xAxis.scaled, accData.zAxis.scaled) * RAD_TO_DEG;
 
  // This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees
  if ((pitch < -90 && kalAngleY > 90) || (pitch > 90 && kalAngleY < -90)) {
  kalmanY.setAngle(pitch);
  kalAngleY = pitch;
  } 
  else
  kalAngleY = kalmanY.getAngle(pitch, gyroData.yAxis.scaled, dt); // Calculate the angle using a Kalman filter*/
  if (abs(kalAngleY) > 90)
  gyroData.xAxis.scaled = -gyroData.xAxis.scaled; // Invert rate, so it fits the restriced accelerometer reading*/

  kalAngleX = kalmanX.getAngle(roll, gyroData.xAxis.scaled, dt); // Calculate the angle using a Kalman filter

//************************************************PID computation and Speeds of individual motors update*****************************************
//************************************************X configuration of the Quadcopter******************************************************************

RollCorrection = compute(0.0, roll);
  PitchCorrection = compute(0.0, pitch);
  thrF1 = DefaultSpeed + RollCorrection + PitchCorrection;
  thrF2 = DefaultSpeed + RollCorrection - PitchCorrection;
  thrB3 = DefaultSpeed - RollCorrection - PitchCorrection;
  thrB4 = DefaultSpeed - RollCorrection + PitchCorrection;

//***********************************************************Limitation of the maximum and minimum speeds******************************************
  if(thrF1 > 1800)
  thrF1 = 1800;
  if(thrF2 > 1800)
  thrF2 = 1800;
  if(thrB3 > 1800)
  thrB3 = 1800;
  if(thrB4 > 1800)
  thrB4 = 1800;
  if(thrF1 < 1200)
  thrF1 = 1200;
  if(thrF2 < 1200)
  thrF2 = 1200;
  if(thrB3 < 1200)
  thrB3 = 1200;
  if(thrB4 < 1200)
  thrB4 = 1200;

//************************************************update esc's with new pulse widths************************************************
  MotorF1.pulsewidth_us(thrF1);
  MotorF2.pulsewidth_us(thrF2);
  MotorB3.pulsewidth_us(thrB3);
  MotorB4.pulsewidth_us(thrB4);
//**********************************************make sure we stay in the 4000 us loop************************************************
timerE = tmr.read_us();  
  while(tmr.read_us() < 4000){
  wait_us(1);
  }


/***********************************************PID functions***************************************************************************
void PIDGains(double Kp, double Ki, double Kd)
{
  kp = Kp;
  ki = Ki;
  kd = Kd;
}  

//*****************************************************************************
int compute(double Setpoint, double Input)  //input is the gyro; setpoint is 0 when self leveling or stick position from RFM
{
  PIDerror = Setpoint - Input;
  Pout = PIDerror * kp;
  Iout = Iout_prev + (PIDerror * ki);
  Dout = (PIDerror - prev_error) * kd;
  
  prev_error = PIDerror;
  
  PIDOutput = Pout + Iout + Dout;
  
  return PIDOutput;
}

.

Blog entry information

Author
vali andrei
Views
1,128
Last update

More entries in General

Share this entry

Top