Autonomous Drone Docking Bay

by juanjimenez22 in Circuits > Arduino

410 Views, 4 Favorites, 0 Comments

Autonomous Drone Docking Bay

Picture1.png

This instructable was created in fulfillment of the project requirement of the Makecourse at the University of South Florida (www.makecourse.com)


Hello everyone,

This instructable is for a senior-level elective in the engineering department at the University of South Florida in Tampa, FL.

Inspiration:

The inspiration for this project is my interest in autonomous technologies. Although, drones are only part of my interest in this area, I thought it was an excellent start to showcase a project. This semester, I volunteered as a research assistant at the LACIS lab under Dr. Tansel Yucelen, where research is done with drones and other related areas. I saw this as an opportunity to use available technologies that align with my area of interest while learning new concepts.


Explanation:

So, what exactly will this look like when you are done with it?

Below I am attaching a video that explains the hardware, logic and shows the system being implemented.

Video

Supplies

What you will need for this instructable:

Optional:

  • Access to a soldering iron
  • Solder Flux
  • Heat Shrink
  • Wire Cutters

The optional supplies are listed as optional because the wiring harness was completed using these supplies, although not completely necessary to do.

Now, the crazyflie system is costly. An alternative would be to manually fly a drone into the drone bay, and I will touch upon this in step 4.

Gather STL Files

Download the STL files and print all files, I use Ultimaker Cura for all of my 3D slicings need to convert STL to G-Code. Select your appropriate printer.

All files are STL files, which will be ready to print once sliced.

Print Files Via 3D Printer

fun.png

Slice the files and print them! Be sure to monitor your prints in case there are any hiccups.

Assemble the Drone Docking Bay!

STATION_ASSEMBLY.JPG
STATION_ASSEMBLY_2.JPG
STATION_ASSEMBLY_3.JPG
IMG_8292.jpg
IMG_8291.jpg
IMG_8294.jpg
IMG_8295.jpg

I will provide screenshots of important orientations for parts of the assembly to ensure you get the right parts in the right places as well as a video of the overall assembly. In addition to this, I provide the SolidWorks assembly as a part file (SLDPRT).

Important Notes:

  • The holes for the ultrasonic sensor mounting plate and case are all M5 in size. Use a nut and bolt for this fastener.
  • The electronics housing uses 3x1mm round magnets, easily found on amazon. The link is provided.
  • For the mount for the tower, feel free to use an insert or use super glue to mount it to the electronics housing.
  • Place the L298 H Bridge onto the stands in the electronics housing.
  • Place battery pack and Arduino within the electronics housing.
  • Use the mounting brackets that come with the linear actuator on the base of the docking bay as well as the lid.
  • The pushbuttons should be either pressed in place or superglued in place depending on the tolerances that the 3D printer can hold.
  • The Arduino Uno goes inside the Arduino Uno enclosure, which goes inside the electronics bay.
  • The red circle denotes the placement of the 3x1mm magnets these inserts are also on the electronics bay, make sure to get the polarity correct once the the glue is applied.

Instructions:

  1. Place the linear actuator within the docking bay and fasten the bottom portion to the hole in the bottom.
  2. Place the drone docking bay door, align it with the drone docking bay hinges on both parts and run the rod through one hole to the other side of the bay.
  3. Run the linear actuator wires to the electronics housing and connect it to the H bridge, follow the circuit schematic in the next step for this
  4. Take the top bracket of the linear actuator and fasten it to the drone bay door, we should have a fully closed door.
  5. Take the mounting place of the ultrasonic sensor and mount it to the top tower with the rod pin.
  6. Fasten the ultrasonic sensor housing with the sensor inside, and use M5 bolts and nuts.
  7. Place push buttons in any of the two slots on the tower side of the electronics housing.
  8. Go to Step 4 in the Instructables and proceed to wire.

Lets Wire This Thing!

Drone Bay Wiring SC.png

Providing the wiring diagram and a simulated TinkerCAD diagram will help visualize where the wires will go in the assembly.

As I mentioned in the supplies, the soldering iron is not necessary. If you have a breadboard at hand, it is possible to do this by the breadboard and allow a broader audience to engage in the project.


Follow the "Drone Bay Wiring" pdf for all of the wirings. A slightly more straightforward approach to this is to follow the screenshot provided.


Note:

  • The 9V battery is strictly for the Arduino Uno.
  • A 12V is needed to power the linear actuator.

The Code, the Brains and Heart of Our System!

The code below is uploaded to the Arduino Uno. Once all of the wirings are done, double, triple, and then check again to make sure all the wires go to their designated terminals and hardware parts.

The code below is written in the Arduino IDE.

Make sure to have the corresponding libraries these libraries will be highlighted below.

C++ Code for Arduino Uno and Drone Bay Docking (.ino) Click here for explanation:

// C++ code
#include <Wire.h>
// Motion Sensor Pins & Conversions
const int trigPin = 6;
const int echoPin = 5;
int distanceCm, distanceInch;
long duration;
int distance;


// Linear Actuator Pins
int enA = 9;
int in1 = 7;
int in2 = 8;


// Push Button Pins
const int OpenDoor_But = 2;
const int CloseDoor_But = 3; 


// Lockout State
boolean lockout = false;




void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600); // Starts the serial communication
  pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
  pinMode(echoPin, INPUT); // Sets the echoPin as an Input
  // Motor Pin modes 
  pinMode(enA, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  // Push Button Pin modes
 pinMode(OpenDoor_But, INPUT_PULLUP);
 pinMode(CloseDoor_But, INPUT_PULLUP);
 
 // Setting Initial Position of Motor
 digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);


 // Setting Speed Via PWM 200 out of of possible 0~255
  analogWrite(enA, 200);
  delay(5000); // Delaying next for 2 seconds  
}


 /* // Creating Motor Functions 
void InitialPosition(){
  // Reversing Direction of Motor to close bay door
  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);


  // Setting Speed Via PWM 200 out of of possible 0~255
  analogWrite(enA, 200);
  delay(2000); // Delaying next for 2 seconds  
}
*/ 


  void DetectionOfObject(){  
  if(lockout == false && distanceCm <= 50){
  digitalWrite(in1,LOW); // Turn on the actuator
  digitalWrite(in2,HIGH);
  analogWrite(enA, 255); // Setting speed of motor
  delay(15000);
  // Closing of Bay Door
  digitalWrite(in1,HIGH); 
  digitalWrite(in2,LOW);
  analogWrite(enA, 200); // Setting speed of motor
  delay(5000); 
  lockout = true; 
  } 
  if(lockout == true)
    delay(1000);
    lockout = false;
   }
   
   
void PushButtonActuation(){
   while(digitalRead(OpenDoor_But) == LOW){ // Open Door
    digitalWrite(in1,LOW); // Turn on the actuator
    digitalWrite(in2,HIGH);
    analogWrite(enA, 255); 
    Serial.println(digitalRead(OpenDoor_But));
  }
  if(digitalRead(OpenDoor_But) == HIGH){ // Once open door is unpressed the motor will stop
    analogWrite(enA, 0); 
  }
  
  while(digitalRead(CloseDoor_But) == LOW){ // Close Door
    digitalWrite(in1,HIGH); // Turn on the actuator
    digitalWrite(in2,LOW);
    analogWrite(enA, 200);  
  }
  
  if(digitalRead(CloseDoor_But) == HIGH){ // Once close door is unpressed the motor will stop
    analogWrite(enA, 0); 
  }
  
}


void loop() {
//  put your main code here, to run repeatedly:
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  // Sets the trigPin on HIGH state for 10 micro seconds
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  // Reads the echoPin, returns the sound wave travel time in microseconds
  duration = pulseIn(echoPin, HIGH);
  // Calculating the distance
  distance = duration * 0.034 / 2;
  // Prints the distance on the Serial Monitor
  Serial.print("Distance: ");
  Serial.println(distance);
  distanceCm = duration * 0.034 / 2; // Conversion to cm
  // distanceInch = duration * 0.0133 / 2; // Conversion to in
  // If motion sensor detects the drone at  40cms or less open door  
  //  InitialPosition();
  
 DetectionOfObject();
 PushButtonActuation();
}


Python Code for Crazflie LPS(.py) Click here for explanation:

The following code will need to be run in tandem with the LPS system provided by Crazyflie. Since most people will not have this at hand, I suggest trying to actuate the drone bay door with a small drone that you may have at home since it will allow you to sharpen up those drone skills.


import time


import cflib.crtp
from cflib.crazyflie.log import LogConfig
from cflib.crazyflie.swarm import CachedCfFactory
from cflib.crazyflie.swarm import Swarm
from cflib.crazyflie.syncLogger import SyncLogger




#Establishing Connection to CF Radio
URI1 = 'radio://0/1/2M/E7E7E7E7E7'






#    x   y   z  time
sequence1 = [
    (0.75, 3, 0.5, 3.0),
    (0.85, 2.9, 0.5, 0.5),
    (0.95, 2.8, 0.5, 0.5),
    (1.05, 2.75, 0.5, 0.5),
    (1.15, 2.4, 0.5, 1.5),
    (1.25, 2.2, 0.5, 1.5),
    (1.53, 2.14, 0.5, 2.5),
    (1.53, 1.95, 0.4, 3.0),
    (1.5, 0.8, 0.5, 3.0),
    (1.55, .73, 0.5, 3.0),
    (1.41, 1.87, 0.8, 4.0),
    (1.5, 1.87, 0.8, 2.0),
    (1, 4, 0.8, 10.0),
    






seq_args = {
    URI1: [sequence1],


}


# List of URIs, comment the one you do not want to fly
uris = {
    URI1,
}




def wait_for_position_estimator(scf):
    print('Waiting for estimator to find position...')


    log_config = LogConfig(name='Kalman Variance', period_in_ms=500)
    log_config.add_variable('kalman.varPX', 'float')
    log_config.add_variable('kalman.varPY', 'float')
    log_config.add_variable('kalman.varPZ', 'float')


    var_y_history = [1000] * 10
    var_x_history = [1000] * 10
    var_z_history = [1000] * 10


    threshold = 0.001


    with SyncLogger(scf, log_config) as logger:
        for log_entry in logger:
            data = log_entry[1]


            var_x_history.append(data['kalman.varPX'])
            var_x_history.pop(0)
            var_y_history.append(data['kalman.varPY'])
            var_y_history.pop(0)
            var_z_history.append(data['kalman.varPZ'])
            var_z_history.pop(0)


            min_x = min(var_x_history)
            max_x = max(var_x_history)
            min_y = min(var_y_history)
            max_y = max(var_y_history)
            min_z = min(var_z_history)
            max_z = max(var_z_history)


            # print("{} {} {}".
            #       format(max_x - min_x, max_y - min_y, max_z - min_z))


            if (max_x - min_x) < threshold and (
                    max_y - min_y) < threshold and (
                    max_z - min_z) < threshold:
                break




def wait_for_param_download(scf):
    while not scf.cf.param.is_updated:
        time.sleep(1.0)
    print('Parameters downloaded for', scf.cf.link_uri)




def reset_estimator(scf):
    cf = scf.cf
    cf.param.set_value('kalman.resetEstimation', '1')
    time.sleep(0.1)
    cf.param.set_value('kalman.resetEstimation', '0')


    wait_for_position_estimator(cf)
    


def position_callback(timestamp, data, logconf):
    x = data['kalman.stateX']
    y = data['kalman.stateY']
    z = data['kalman.stateZ']
    print('pos: ({}, {}, {})'.format(x, y, z))




def start_position_printing(scf):
    log_conf = LogConfig(name='Position', period_in_ms=500)
    log_conf.add_variable('kalman.stateX', 'float')
    log_conf.add_variable('kalman.stateY', 'float')
    log_conf.add_variable('kalman.stateZ', 'float')


    scf.cf.log.add_config(log_conf)
    log_conf.data_received_cb.add_callback(position_callback)
    log_conf.start()




def take_off(cf, position):
    take_off_time = 1
    sleep_time = 0.1
    steps = int(take_off_time / sleep_time)
    vz = position[2] / take_off_time


    print(vz)


    for i in range(steps):
        cf.commander.send_velocity_world_setpoint(0, 0, vz, 0)
        time.sleep(sleep_time)




def land(cf, position):
    landing_time = 1.0
    sleep_time = 0.1
    steps = int(landing_time / sleep_time)
    vz = -position[2] / landing_time


    print(vz)


    for i in range(steps):
        cf.commander.send_velocity_world_setpoint(0, 0, vz, 0)
        time.sleep(sleep_time)


    cf.commander.send_setpoint(0, 0, 0, 0)
    # Make sure that the last packet leaves before the link is closed
    # since the message queue is not flushed before closing
    time.sleep(0.1)




def run_sequence(scf, sequence):
    try:
        cf = scf.cf
        cf.param.set_value('flightmode.posSet', '1')


        take_off(cf, sequence[0])
        for position in sequence:
            print('Setting position {}'.format(position))
            end_time = time.time() + position[3]
            while time.time() < end_time:
                
                cf.commander.send_setpoint(position[1], position[0], 0,
                                           int(position[2] * 1000))


        land(cf, sequence[-1])
    except Exception as e:
        print(e)




if __name__ == '__main__':
    # logging.basicConfig(level=logging.DEBUG)
    cflib.crtp.init_drivers(enable_debug_driver=False)


    factory = CachedCfFactory(rw_cache='./cache')
    with Swarm(uris, factory=factory) as swarm:
        # If the copters are started in their correct positions this is
        # probably not needed. The Kalman filter will have time to converge
        # any way since it takes a while to start them all up and connect. We
        # keep the code here to illustrate how to do it.
        swarm.parallel(reset_estimator)
        swarm.parallel(start_position_printing)
        # The current values of all parameters are downloaded as a part of the
        # connections sequence. Since we have 10 copters this is clogging up
        # communication and we have to wait for it to finish before we start
        # flying.
        print('Waiting for parameters to be downloaded...')
        swarm.parallel(wait_for_param_download)


        swarm.parallel(run_sequence, args_dict=seq_args)


Now we have all the hardware and software to have a fully functioning AUTONOMOUS drone docking bay system!

The world is our oyster and the only limitation to the capabilities of the system is our imagination and our passion to pursue said possibilities.


If you want more explanation regarding the code do not forget to click on the hyperlinks in each of the subheadings!