MonoCopter - 13CL
As part of a quarter-long university lab project under the physics department at UC Santa Barbara, our group decided to attempt to build a fully functioning quadcopter. This consisted of sourcing parts, such as motors and propellers, and building the finished product, including writing the control algorithms and assembling the final product.
Due to a substantial number of issues, problem-solving, and straying from our original design, the project was reduced to building a monocopter which could use inputs from an accelerometer to keep itself level. The inputs are fed into a PID loop (which stands for Proportional, Integral, Derivative; these are used in many common machines, such as thermostats) and the computer (in this case, assisted by a LabJack U3) sends pulse width modulated voltages to the motor and propeller.
Below, we will explain how to program and build the quadcopter. We will conclude with a bit of information about some of the issues we ran into, and our planned next steps.
Supplies
Software and Controls
- LabJack U3-HV
- Laptop/computer
- Python (Spyder/Anaconda)
- Jumper wires
- Electrical solid-core wire
Hardware
- Brushed motor (w/ propeller)
- Accelerometer (ADXL335)
- DC Power Supply
- Solid State Relay (SSR)
- 1.5V AA battery
- Light, sturdy stick, ~50cm length (e.g. 2mm thick balsa wood plates, meterstick, etc.)
- Upright stand with adjustable screw attachments
- Small rubber bands
- CA glue / hot glue
General Tools
- Small flat head screwdriver
- Soldering iron, solder
- Drill & drill bits
Wiring All of the Components
To set everything up, you need to wire all of the component parts correctly.
For the accelerometer:
1. Solder the pins into the accelerometer.
2. Solder long wires to the V_in, GND (ground), X_out, Y_out, and Z_out pins.
3. Connect the wires to the labjack. The V_in wire should go to DAC0, the GND wire should go to GND, the X_out should be connected to AIN0, Y_out goes to AIN1, and Z_out goes to AIN2.
For the brushed DC motor:
1. Solder the positive (red) wire from the motor to a long wire, preferably red. Solder the ground (black) wire from the motor to another long wire, preferably black.
2. Connect one wire to the negative terminal on channel one of the power supply and the other wire to the output labeled "1" on the solid-state relay.
3. Connect a wire from the output labeled "2" on the solid-state relay to the positive terminal on channel one of the power supply.
4. Connect the input labeled "-4" on the solid-state relay to ground on the labjack.
5. Connect the input labeled "3+" on the solid-state relay to a 1.5V battery, which should be connected to FIO4 on the labjack.
Calibrating the Accelerometer
1. Set DAC0 to 5V in order to supply the accelerometer with the correct amount of power.
import numpy as np import u3 def setDAC0(voltage): if voltage<= 5 and voltage>=0: DAC0_VALUE = d.voltageToDACBits(voltage, dacNumber = 0, is16Bits = False) d.getFeedback(u3.DAC0_8(DAC0_VALUE)) else: print("Voltage needs to be between 0 and 5V.")<br>
2. Read the voltage output out of each of the directional accelerations by calling the accelerometer () function in the code, while keeping the accelerometer on a flat, level surface. This will print out the voltage readings from the accelerometer.
def accelerometer(): global gx, gy, gz d = u3.U3() x = d.getAIN(0) y = d.getAIN(1) z = d.getAIN(2) return x, y, z accelerometer()
3. Find the average of the output voltages and set that as your reference point.
expected = np.mean([accelerometer()]) def gvalues(): dx = d.getAIN(0)-expected dy = d.getAIN(1)-expected dz = d.getAIN(2)-expected gval = np.sqrt(dx**2 + dy**2 + dz**2) gx = dx*9.8/gval gy = dy*9.8/gval gz = dz*9.8/gval
The accelerometer is now calibrated.
Building the Monocopter
1. Cut the balsa wood plank into a strip, approx. 3cm wide.
2. At one end, glue the correctly wired motor along its body and along the end of the plank so that the motor is fixed, with the axle pointing in the plane of the widest face of the plank, and sticking upwards enough to give room for the propeller to spin freely.
3. Connect the propeller to the motor axle.
4. At the other end of the plank, drill a hole just larger than the screw used on the mounting system of the vertical stand. This will be the hinge of the pivot.
5. Put the screw through the hole in the plank and motor system and attach it to the vertical stand. Make sure the arm can pivot freely.
6. Just to the side of the pivot, on the side towards the motor, place the correctly wired accelerometer on the face of the plank. Be sure that the x-axis of the accelerometer is pointed toward the pivot of the arm, and the y-axis is pointed parallel to the motor axis. Secure the accelerometer with a small rubber band, in case it needs adjusting.
7. Feed the motor and accelerometer's wires along the back of the plank, toward the pivot, and down the vertical stand. Secure with rubber bands, but make sure the arm can still pivot freely.
Testing
1. Turn the power supply on. Set the current to ~0.5A (more than needed), and its voltage to 5V.
2. Make sure DAC0 is set to 5V because the accelerometer needs 5V to work properly.
3. Run the function keepConstantPivotHeight(), which begins the PID loop as shown:
def keepConstantPivotHeight(): setDAC0(3) d.configIO(NumberOfTimersEnabled = 2,FIOAnalog=3) d.configTimerClock(TimerClockBase = 6, TimerClockDivisor = 100) x_vals = np.array([]) t = 0 pwm_num = 65535 while True: x, y, z = accelerometer() x_vals = np.append(x_vals, x) pwm_num = PID(x_vals, t, pwm_num) t += 1 time.sleep(0.1)
The propeller should start up at 0% pulse width modulation (PWM), meaning that it starts being off. This way, it begins slowly at first and increases its power until it starts lifting the pivot. Once it swings past its horizontal level, the PID loop should recognize the positive angle using the accelerometer and decrease its PWM to decrease the power provided by the motor as shown:
def PID(x_vals, i, pwm_num): expected = 0 errors = x_vals - expected integral = 0.1*np.sum(errors) deriv = (errors[len(errors)-1]-errors[0])/i a = 10 b = 0 c = 0 controlvar = a*errors[-1] + b*integral + c*deriv num = pwm_num if i % 10 == 0: if controlvar < 0: if pwm_num + (5/100 * 65535) <= 65535: num = int(pwm_num + (5/100 * 65535)) d.getFeedback(u3.Timer0Config(TimerMode=0, Value=num)) d.getFeedback(u3.Timer1Config(TimerMode=0, Value=num)) elif controlvar > 0: if pwm_num - (5/100 * 65535) >= 0: num = int(pwm_num - (5/100 * 65535)) d.getFeedback(u3.Timer0Config(TimerMode=0, Value=num)) d.getFeedback(u3.Timer1Config(TimerMode=0, Value=num)) return num
By taking one measurement per second, the PID loop uses the input from the accelerometer to either lower or raise the PWM output by 5% depending on the acceleration it reads. In the way we oriented the accelerometer, a positive x reading meant the monocopter was too low, and a negative x reading meant the monocopter was too high. When testing the PID loop, first begin with just the proportional in order to see how the monocopter oscillates around where it is level. Then, add and change the coefficient for the integral component until it oscillates well around where it is level. Then, add and change the coefficient for the derivative component until it oscillates better around where it is level.
Congratulations, you have built a working monocopter that keeps itself level!
Problems Solved & Conclusion
1. We quickly found out that the original motors we bought were brushless DC motors which require three-phase power. We tried looking up how to make a three-phase power circuit but found out that it was very complicated and involved microcontrollers. Thus, we looked for an alternative and found brushed DC motors which work on a single DC input.
2. We bought a distance sensor in order to stabilize the drone at a certain height. We quickly found out that this wouldn't work with our pivot design due to the sensor reading different values while looking at the ground at an angle. This means that we couldn't get an accurate reading of how high the drone was from the ground. Instead of using the distance sensor, we decided to use an accelerometer to find out whether or not the drone on the pivot was level.
3. We already had an accelerometer but found out that it outputs digital bits instead of an analog output. This requires I2C for the labjack which might not work very well. Thus, we bought analog sensors that simply modulate their output voltages.
4. We found out that the labjack cannot output high enough current to power the propellor fast enough. Thus, we decided to use a relay connected to a power supply to have enough power.
5. With the relay, we then found out that the labjack also does not output enough voltage through its PWM output (FIO4). It outputs a static voltage of about 2.3V while the threshold to activate the relay was 3V. Thus, we simply put a AA battery (which outputs 1.5V) in between the labjack and the relay to get over the threshold.
Overall, there is still more to be completed with this project. We still need to upgrade the drone to a dicopter and then a quadcopter. For the dicopter, this would primarily involve making a setup that can constrain the dicopter to move up and down the z-axis and also tilt along one axis as well as making a program that can keep the drone level rotationally and keep a constant height. For the quadcopter, we would use most of the dicopter's code but then add one more rotational degree of freedom to finally get it to be able to keep itself level completely.