up_down_v = S else: up_down_v = 0 if move_vector[2] < 0: for_back_v = -S elif move_vector[2] > 0: for_back_v = S else: for_back_v = 0 t.send_rc_control(left_right_v, for_back_v, up_down_v, yaw_v, verbose=True) else: # if no face, rotate until finding one print("No face detected.") t.send_rc_control(0, 0, 0, int(last_yaw_direction * 20), verbose=False) # cleanup after loop terminates cv2.destroyAllWindows t.end()
class FaceTracker: """Constants""" FB_S = 15 # FWD/BWD Speed of the drone LR_S = 25 # LFT/RGHT Speed of the drone UD_S = 25 CW_S = 25 # CW/CCW Speed of the drone FPS = 20 # Frames per second of the pygame window display def __init__(self, height, width): """Start loading""" namedWindow("drone") self.paper = imread("./resources/Tello.png") self.height = height self.width = width self.fv = FaceVector(height, width) self.tello = Tello() # Drone velocities between -100~100 self.for_back_velocity = 20 self.left_right_velocity = 20 self.up_down_velocity = 20 self.yaw_velocity = 20 self.speed = 20 self.send_rc_control = False self.should_stop = False self.mode = "keyboard" def run(self, show=False, debug=False): """Connecting block""" if not self.tello.connect(): return print("Connected") if not self.tello.set_speed(self.speed): return print("Speeds set") """Stream start block""" if not self.tello.streamoff(): return if not self.tello.streamon(): return cap = self.tello.server.get_video_capture() print("Stream started") """Main loop""" while not self.should_stop: vec = None """Frame reading block""" if self.mode == "tracking" or show or debug: try: r, frame = cap.read() except ValueError: continue if r: """Creating target directions vector""" if self.mode == "tracking" or debug: vec, frame = self.fv.direction_vector_3d_with_returning_image(frame) """Frame plotting(requires from argument: bool:SHOW)""" if show or debug: frame = self.fv.text_addition(frame, vec) imshow("drone", frame) if (not show) and (not debug): imshow("drone", self.paper) key = waitKey(5) & 0xff """Keyboard commands getting""" self.check_key(key) if self.mode == "tracking": """Driving block""" if vec is None: vec = [0, 0, 0] print(vec) """Setting velocities depending from directions vector VEC""" if vec[0] != 0 or vec[1] != 0: """Moving in 2D space at first""" # set left/right velocity self.left_right_velocity = -self.LR_S * vec[0] # set up/down velocity self.up_down_velocity = self.UD_S * vec[1] # set forward/backward velocity else: """Then moving forward/backward""" self.for_back_velocity = self.FB_S * vec[2] # set yaw clockwise velocity self.yaw_velocity = self.CW_S * 0 """Send move commands""" self.update() sleep(1 / self.FPS) self.tello.end() def update(self): """ Update routine. Send velocities to Tello.""" if self.send_rc_control: self.tello.move_with_velocities(self.left_right_velocity, self.for_back_velocity, self.up_down_velocity, self.yaw_velocity) def check_key(self, key): """ Moves Tello through the keyboard keys. - T / G : Takeoff / Land(Ground). - W / S : Up / Down. - A / D : CW / CCW. - I / K : Forward / Backward. - J / L : Left / Right. - SPACE : Start / Stop face tracking. - Q : Quit. """ if key == ord('q'): # stop self.should_stop = True elif key == ord('t'): # takeoff print("Flying") self.tello.takeoff() self.send_rc_control = True elif key == ord('g'): # land print("Landing") self.tello.land() self.send_rc_control = False elif key == ord('i'): # forward self.tello.forward(50) elif key == ord('k'): # backward self.tello.back(50) elif key == ord('j'): # left self.tello.left(50) elif key == ord('l'): # right self.tello.right(50) elif key == ord('w'): # up self.tello.up(50) elif key == ord('s'): # down self.tello.down(50) elif key == ord('a'): # cw self.tello.rotate_cw(30) elif key == ord('d'): # ccw self.tello.rotate_ccw(30) elif key == ord(' '): # change mode self.change_mode() """Realise mode changing event""" def change_mode(self): if self.mode == "tracking": self.mode = "keyboard" self.tello.stop() elif self.mode == "keyboard": self.mode = "tracking"
#!env/bin/python from flask import Flask, jsonify from tello import Tello app = Flask(__name__) tello = Tello() tello.initialize() @app.route('/drone/command/takeoff', methods=['POST']) def drone_takeoff(): print('Drone now taking off...') tello.takeoff() return jsonify({'status': 'OK'}) @app.route('/drone/command/land', methods=['POST']) def drone_land(): print('Drone now landing...') tello.land() return jsonify({'status': 'OK'}) if __name__ == '__main__': app.run(debug=False, host='10.0.0.3') tello.end()
class Follower: def __init__(self): pygame.init() pygame.display.set_caption("Tello video stream") self.screen = pygame.display.set_mode([960, 720]) # Drone velocities between -100~100 self.for_back_velocity = 0 self.left_right_velocity = 0 self.up_down_velocity = 0 self.yaw_velocity = 0 self.speed = 10 self.tello = Tello() self.send_rc_control = False pygame.time.set_timer(pygame.USEREVENT + 1, 1000 // FPS) self.personTracker = PersonTracker() self.Px, self.Ix, self.Dx = 0.10, 0, -0 #D gain should be negative. self.Py, self.Iy, self.Dy = 0.1, 0, -0 self.Pz, self.Iz, self.Dz = 0.25, 0, -0.001 self.prev_err_x, self.prev_err_y, self.prev_err_z = None, None, None self.accum_err_x, self.accum_err_y, self.accum_err_z = 0, 0, 0 self.found_person = False self.manual = True self.iter = 0 def run(self): self.tello.connect() self.tello.set_speed(self.speed) # In case streaming is on. This happens when we quit this program without the escape key. self.tello.streamoff() self.tello.streamon() frame_read = self.tello.get_frame_read() should_stop = False imprinted = False emergency_counter = 0 while not should_stop: for event in pygame.event.get(): if event.type == pygame.USEREVENT + 1: self.update() elif event.type == pygame.QUIT: should_stop = True elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: should_stop = True else: self.keydown(event.key) elif event.type == pygame.KEYUP: self.keyup(event.key) if frame_read.stopped: break self.screen.fill([0, 0, 0]) frame = frame_read.frame if self.iter > 240: humanBox, imprinted = self.personTracker.findMyHooman(frame) if humanBox is None and self.send_rc_control and not self.manual: emergency_counter += 1 if emergency_counter >= 120: #missed human for 120 frames; 1 second print("ENGAGING EMERGENCY HOVER.") cv2.putText(frame, "EMERGENCY HOVER", (700, 130), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2) self.emergencyHover() elif humanBox is not None: if self.found_person == False: self.found_person = True emergency_counter = 0 #PID LOOP: desired_pos = (frame.shape[1] // 2, frame.shape[0] // 2 ) #format x,y if humanBox is not None: xmin = int(humanBox[1] * frame.shape[1]) ymin = int(humanBox[0] * frame.shape[0]) xmax = int(humanBox[3] * frame.shape[1]) ymax = int(humanBox[2] * frame.shape[0]) centerHumanPosition = (np.mean( (xmax, xmin)), np.mean((ymax, ymin))) #format x,y #draw bounding box cv2.rectangle(frame, (xmin, ymin), (xmax, ymax), (255, 0, 0), 2) #blue #draw target coord cv2.circle(frame, (int( centerHumanPosition[0]), int(centerHumanPosition[1])), 10, (255, 0, 0), 1) #blue # print("z width: {}".format(np.abs(xmax-xmin))) #draw desired coord cv2.circle(frame, desired_pos, 10, (0, 0, 255), 1) #red if self.send_rc_control and not self.manual: self.update_control(centerHumanPosition, desired_pos, xmax, xmin) text = "Battery: {}%".format(self.tello.get_battery()) cv2.putText(frame, text, (5, 720 - 5), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) heightText = "Height:{}".format(self.tello.get_height()) cv2.putText(frame, heightText, (720 - 5, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) manualText = "Manual: {}".format(self.manual) if self.manual: cv2.putText(frame, manualText, (720 - 5, 70), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) else: cv2.putText(frame, manualText, (720 - 5, 70), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) imprintedTxt = "Imprinted: {}".format(imprinted) if imprinted: cv2.putText(frame, imprintedTxt, (720 - 5, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2) else: cv2.putText(frame, imprintedTxt, (720 - 5, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) frame = np.rot90(frame) frame = np.flipud(frame) frame = pygame.surfarray.make_surface(frame) self.screen.blit(frame, (0, 0)) pygame.display.update() if self.iter <= 240: self.iter += 1 time.sleep(1 / FPS) # Call it always before finishing. To deallocate resources. self.tello.end() def keydown(self, key): """ Update velocities based on key pressed Arguments: key: pygame key """ if key == pygame.K_UP: # set forward velocity self.for_back_velocity = S elif key == pygame.K_DOWN: # set backward velocity self.for_back_velocity = -S elif key == pygame.K_LEFT: # set left velocity self.left_right_velocity = -S elif key == pygame.K_RIGHT: # set right velocity self.left_right_velocity = S elif key == pygame.K_w: # set up velocity self.up_down_velocity = S elif key == pygame.K_s: # set down velocity self.up_down_velocity = -S elif key == pygame.K_a: # set yaw counter clockwise velocity self.yaw_velocity = -S elif key == pygame.K_d: # set yaw clockwise velocity self.yaw_velocity = S elif key == pygame.K_m: # set yaw clockwise velocity self.manual = not self.manual self.yaw_velocity = 0 self.up_down_velocity = 0 self.left_right_velocity = 0 print("MANUAL MODE IS NOW: {}".format(self.manual)) def keyup(self, key): """ Update velocities based on key released Arguments: key: pygame key """ if key == pygame.K_UP or key == pygame.K_DOWN: # set zero forward/backward velocity self.for_back_velocity = 0 elif key == pygame.K_LEFT or key == pygame.K_RIGHT: # set zero left/right velocity self.left_right_velocity = 0 elif key == pygame.K_w or key == pygame.K_s: # set zero up/down velocity self.up_down_velocity = 0 elif key == pygame.K_a or key == pygame.K_d: # set zero yaw velocity self.yaw_velocity = 0 elif key == pygame.K_t: # takeoff self.tello.takeoff() self.send_rc_control = True elif key == pygame.K_l: # land not self.tello.land() self.iter = 0 self.send_rc_control = False def update(self): """ Update routine. Send velocities to Tello.""" if self.send_rc_control: self.tello.send_rc_control(self.left_right_velocity, self.for_back_velocity, self.up_down_velocity, self.yaw_velocity) def update_control(self, curr_lateral_pos, desired_later_pos, xmax, xmin): #Three error directions. Two lateral, one forward/backward. How to calc forward/back error? err_x = curr_lateral_pos[0] - desired_later_pos[ 0] #if positive, we're to the left of where we want to be, so want positive control. (CHECK THIS) err_y = desired_later_pos[1] - curr_lateral_pos[ 1] #if positive, we're below where we want to be. (CHECK THIS) #hardcode desired box width. Must test!! desired_width = 350 curr_width = np.abs(xmax - xmin) #check. is this actually the width dim? err_z = desired_width - curr_width #if negative, too close; want backwards--> positive gain # print("Err z: {}".format(err_z)) if self.prev_err_x == None: derivative_x_input = 0 derivative_y_input = 0 derivative_z_input = 0 else: derivative_x_input = (err_x - self.prev_err_x) / (1 / FPS) derivative_y_input = (err_y - self.prev_err_y) / (1 / FPS) derivative_z_input = (err_z - self.prev_err_z) / (1 / FPS) #clip derivative errors to avoid noise derivative_x_input = np.clip(derivative_x_input, -11000, 11000) derivative_y_input = np.clip(derivative_y_input, -11000, 11000) derivative_z_input = np.clip(derivative_z_input, -11000, 11000) self.accum_err_x += err_x self.accum_err_y += err_y self.accum_err_z += err_z self.prev_err_x = err_x self.prev_err_y = err_y self.prev_err_z = err_z # print("derr_z: {}".format(derivative_z_input)) # self.left_right_velocity = self.Px*err_x+self.Dx*derivative_x_input+self.Ix*self.accum_err_x self.yaw_velocity = self.Px * err_x + self.Dx * derivative_x_input + self.Ix * self.accum_err_x self.up_down_velocity = self.Py * err_y + self.Dy * derivative_y_input + self.Iy * self.accum_err_y self.for_back_velocity = self.Pz * err_z + self.Dy * derivative_z_input + self.Iz * self.accum_err_z #limit velocity to 2*S. # self.left_right_velocity = np.clip(self.left_right_velocity, -S*2, S*2) self.yaw_velocity = int(np.clip(self.yaw_velocity, -S, S)) self.up_down_velocity = int(np.clip(self.up_down_velocity, -S, S)) self.for_back_velocity = int(np.clip(self.for_back_velocity, -S, S)) #Send new velocities to robot. self.update() def emergencyHover(self): print("Cannot find hooman. I am lonely doggo. Hovering and rotating.") self.found_person = False self.for_back_velocity = 0 self.left_right_velocity = 0 self.up_down_velocity = 0 self.yaw_velocity = int(S // 2) self.update()
class FrontEnd(object): def __init__(self): # Init pygame pygame.init() # Init Tello object that interacts with the Tello drone self.tello = Tello() # Drone velocities between -100~100 self.for_back_velocity = 0 self.left_right_velocity = 0 self.up_down_velocity = 0 self.yaw_velocity = 0 self.speed = 10 self.faceFound = False self.send_rc_control = True def run(self): if not self.tello.connect(): print("Tello not connected") return if not self.tello.set_speed(self.speed): print("Not set speed to lowest possible") return # In case streaming is on. This happens when we quit this program without the escape key. if not self.tello.streamoff(): print("Could not stop video stream") return if not self.tello.streamon(): print("Could not start video stream") return frame_read = self.tello.get_frame_read() if self.tello.takeoff(): print('Takeoff successfull') self.tello.connect() star = time.time() while True: # if time.time() - star > 10: # self.tello.connect() # star = time.time() pygame.event.pump() frame = frame_read.frame gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.1, 10, minSize=(65, 65)) for (x, y, w, h) in faces: self.faceFound = True cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2) cx = (2*x+w)/2 cy = (2*y+h)/2 faceSize = (w+h)/2 if self.faceFound: yaw_dif = abs(cx - 480) updo_dif = abs(cy - 360) face_dif = abs(faceSize - 140) sign_yaw = (cx - 480)/yaw_dif sign_updo = (360 - cy)/updo_dif sign_face = (140 - faceSize)/face_dif if yaw_dif > 65: self.yaw_velocity = sign_yaw*S#*(yaw_dif/65) else: self.yaw_velocity = 0 if updo_dif > 45: self.up_down_velocity = sign_updo*S#*(updo_dif/45) else: self.up_down_velocity = 0 if face_dif > 35: self.for_back_velocity = sign_face*S#*(face_dif/35) else: self.for_back_velocity = 0 else: self.for_back_velocity = 0 self.yaw_velocity = 0 self.up_down_velocity = 0 self.update() cv2.imshow('img', frame) frame_read.out.write(frame) self.faceFound = False k = cv2.waitKey(30) & 0xff if k==27: break self.tello.end() frame_read.out.release() frame_read.cap.release() cv2.destroyAllWindows() return 0 def update(self): """ Update routine. Send velocities to Tello.""" if self.send_rc_control: self.tello.send_rc_control(self.left_right_velocity, self.for_back_velocity, self.up_down_velocity, self.yaw_velocity)