OOP Drone Flying Demo Program

Introduction

This article discusses a fun project I took on to create a program using a complicated class. The project had two objectives:

  1. Create a Drone flying simulation
  2. Include a Heads-Up_Display (HUD) for reporting speed, direction, altitude, etc.

Basic Requirements

I started out by cloning some of the code for this project from a car-driving demo. Unfortunately, that did not get me very far.

A drone is similar to a vehicle in the sense that we can "steer" it. But it also has some special characteristics as an aircraft (like a helicopter). For example:

  • It can't move forward or turn unless it is off the ground
  • It can't ascend or lift off until it is running
  • It can't descend if it is already on the ground
  • It can't be turned off while airborne

A drone can "yaw" (point in a different direction) without moving forward (unlike a vehicle), which is not a big deal. But I decided to keep my menu terminology to "turning" right and left.

There are also some actions that drones follow that I did not want to try to simulate:

  • Roll: spin the aircraft over with one side going up and the other one down
  • Pitch: tilt the front of the aircraft up or down
  • Move Backward: reverse direction, which I just didn't want to take the time to implement
  • Head vs. Flight Direction: Unlike almost all other vehicles, boats and planes, a drone (and helicopters) can fly in a direction other than where it is pointing. Too much for this demo.

The Codiing Process

This demo took about four hours total.

Programming the HUD (Heads-Up-Display) was a blast. It reminded me of 80's VB coding in DOS, which is where I learned how to program. I ended up with exactly what I had in mind:

Sample Drone HUD Display

Sample Drone HUD Display

I also decided to clear the display before each redisplay, so that it looked more like a GUI. I added code to let the user do the following:

  • Start the engine
  • Lift off
  • Move forward 5 MPH at a time
  • Slow down 5 MPH at a time
  • Turn right or left
  • Ascend or descend 10 feet at a time
  • Stop the engine

The code makes some obvious checks, such as not doing anything if the engine is off, not turning or moving forward if the drone is still on the ground, not being able to turn off the engine in mid-air, etc. For illustration purposes, clearing the screen before each HUD display was disabled in the example output below:

Sample Drone Program Output

Sample Drone Program Output

Code Design

All the code for both files is listed at the end of the article.

What were the challenges for this project?

The main program ended up being very simple. main() has 1 function call: drive(). The drive method simply loops waiting for the user to quit, and otherwise sends valid commands to the drone class.

The drone class ended up being much larger than I anticipated. Not because of the HUD, which ended up being fairly streamlined, but because of the many rules to check for each potential action, such as not turning if the engine is off. It would be possible to add more rules, if desired, such as preventing the drone from going too high or too far away.

Enhancements?

The number one enhancement I would love to add it to track things like maximum height and speed, estimated flight distance and flight time. It would also be cool to program a "return-to-home" feature like most drones have, where the drone would automatically descend and return to the lift-off location (which means the program would have to keep track of direction changes and distances).

It would also be very interesting to turn this into a GUI program. That would replace the coding for the HUD with simply updating the window. A very cool add-in feature would be a visual map of where the drone is compare to the lift-off spot. Superimposing that over an actual map image would also certainly be boss.

Lessons Learned

Some things are not as hard as they seem. Good planning and a step-by-step coding approach can make a big project much easier to attack and complete.

Zip file containing both files: Drive My Drone Zip

Code Dump: drone class

# drone object class: drm
# 2021-09-28
# object to manage drone data and state
# all code uses 4-space tab stops

# import os library to be able to clear the screen
import os

class Drone:
	def __init__ (self,direction):
		self.__speed = 0
		self.__altitude = 0
		self.__speedchange = 5 
		self.__altitudechange = 10 
		self.__engine = 0
		self.__direction = direction
		self.__canrun = 1 				# in case it crashes

		# these are character codes for box corners and lines
		# they emulate extended ASCII - which Python does not support 
		# these are all double-line characters
		self.c1 = u'\u2554' # upper left corner
		self.c2 = u'\u2550' # horizontal
		self.c3 = u'\u2557' # upper right corner
		self.c4 = u'\u2551' # vertical
		self.c5 = u'\u255a' # lower left corner
		self.c6 = u'\u255d' # lowere right corner
		self.c7 = u'\u2560' # vertical left split
		self.c8 = u'\u2563' # vertical right split
		
		self.eng = " engine "
		self.spd = " speed  "
		self.dflt = ""            
		self.msgsz = 26	# width of the HUD panel	
	
	def clearScreen(self): 
		os.system('cls') #on Windows System (Mac is different)

	# calculate width of message and number of spaces need to print the right box line
	def hudMessage(self,msg):
		lnmsg = len(msg)
		spcs = self.msgsz - lnmsg
		print (" " + self.c4, end="");
		print ("" + msg,end="")	
		for i in range(spcs):
			print (" ",end="")
		print (self.c4)
	
	# clear the screen then print the standard lines and a custom message	
	def drawHUD (self,cmsg):
		# comment this line out to see all HUD displays
		self.clearScreen()
		bxw = self.msgsz

		# header (with HUD title)
		print (" " + self.c1, end="");
		for i in range(bxw):
			print (self.c2,end="")
		print (self.c3)	
		self.hudMessage(" Drone HUD")

		# top line
		print (" " + self.c7, end="");
		for i in range(bxw):
			print (self.c2,end="")
		print (self.c8)	

		# assign a word to engine status
		engstate = "off"
		if self.__engine==1:
			engstate = "on"

		# setup stndard lines
		emsg = f"    engine: {engstate}"
		espd = f"     speed: {self.__speed} MPH"
		edir = f" direction: {self.calcDirection()}"
		ealt = f"  altitude: {self.__altitude}"

		# display static lines and fields
		# engine
		self.hudMessage(emsg)
		# speed
		self.hudMessage(espd)
		# direction
		self.hudMessage(edir)
		# altitude
		self.hudMessage(ealt)
		# custom message
		self.hudMessage(" " + cmsg)	

		# print bottom box parts
		print (" " + self.c5, end="")
		for i in range(bxw):
			print (self.c2,end="")
		print (self.c6)

	def accelerate (self):
		if self.__engine==0:
			self.drawHUD("not running...")
		elif self.__altitude==0:
			self.drawHUD("must lift off first")
		elif self.__speed <=65: 
			self.__speed += self.__speedchange
			self.drawHUD("speed up")
		else:
			self.stop_engine()
			self.__canrun = 0
			msg = " engine BLOWN"
			self.drawHUD(msg)
		
	def brake (self):
		if self.__speed >0: 
			self.__speed -= self.__speedchange
			self.drawHUD("speed down")
		else:
			msg = "not moving"
			self.drawHUD(msg)

	def get_makeinfo (self):
		return self.__year + ' ' + self.__make + ' ' + self.__model
		
	def get_speed (self):
		return self.__speed

	def lift (self):
		if self.__engine==0:
			self.drawHUD("not running...")
		else:	
			self.__altitude += self.__altitudechange
			self.drawHUD("ascended");

	def drop (self):
		if self.__engine==0:
			self.drawHUD("not running...")
		elif self.__altitude==0:
			self.drawHUD("already on ground...")
		else:	
			self.__altitude -= self.__altitudechange
			self.drawHUD("descended");

		
	def turnright (self):
		if self.__engine==0:
			self.drawHUD("not running...")
		elif self.__altitude==0:
			self.drawHUD("not airborne")
# not appropriate, drones can turn without moving forward
#		elif self.__speed==0:	
#			self.drawHUD("standing still...")
		else:	
			self.__direction += 1
			self.drawHUD("turned right");

	def turnleft (self):
		if self.__engine==0:
			self.drawHUD("not running...")
		elif self.__altitude==0:
			self.drawHUD("not airborne")
# not appropriate, drones can turn without moving forward
#		elif self.__speed==0:	
#			self.drawHUD("standing still...")
		else:	
			self.__direction -= 1
			if self.__direction<1:
				self.__direction = 4
			self.drawHUD("turned left");

	def calcDirection(self):
		dir = "North"
		if self.__direction==2: dir="East" 
		elif self.__direction==3: dir="South" 
		elif self.__direction==4: dir="West" 
		else: dir="North"
		return dir

	def start_engine (self):
		if self.__canrun==0:	
			msg = "Nope. Fix engine..."
			self.drawHUD(msg);
		elif self.__engine==1:
			msg = "already started..."
			self.drawHUD(msg);
		else:
			self.__engine = 1
			msg = "engine started..."
			self.drawHUD(msg);
		return self.__engine

	def stop_engine (self):	
		if self.__altitude > 0:
			self.drawHUD("Cannot stop in midair")
		else:	
			self.__engine = 0
			msg = "engine stopped..."
			self.drawHUD(msg);
			return self.__engine
		
	def check_engine (self):
		return self.__engine
		
	def check_altitude (self):
		return self.__altitude
		    

Code Dump: main drone program

# Demo Project: Fly a drone: drm
# 2021-09-28
# program demonstrates OOP (using a drone class)
# all code uses 4-space tab stops

# import libraries and objects  
import DRM_Drone  

# global variables
# multiple prompts for different permitted actions
prompt1 = " What would you like to do? (on=start engine, q=quit): "
prompt2 = " Next? (on, q=quit): "
prompt3 = " Next? (off, g=go, b=brake, r=right, f=left, u=up, d=down, q=quit): "
prompt4 = " Next? (u=up, off=stop engine, q=quit): "
prompt5 = " Next? (g=go, b=brake, r=right, f=left, u=up, d=down, q=quit): "

# main executable code
def main():
	# instantiate a car object
	drive()

def drive():	
	drone = setupDrone(1)		
	entry = "x"
	cnt = 0
	while entry!="q":
		if cnt==0:
			entry = input (prompt1) 
		elif drone.check_engine()==0:
			entry = input (prompt2) 
		else:
			if drone.check_altitude()>0:
				entry = input (prompt5) 
			else:
				entry = input (prompt4) 
		cnt +=1
		entry = entry.lower();
		if entry=="q":
			break
		elif entry=="on":	
			drone.start_engine()
		elif entry=="off":	
			drone.stop_engine()
		elif entry=="g":
			drone.accelerate()
		elif entry=="b":
			drone.brake()
		elif entry=="r":
			drone.turnright()
		elif entry=="l":
			drone.turnleft()
		elif entry=="u":
			drone.lift()
		elif entry=="d":
			drone.drop()
		
# instantiate a drone object with year, make and model
def setupDrone(direction):
	print(' \n Baby You Can Drive My Drone, by drm')
	drone = DRM_Drone.Drone(direction)
	return drone

main();