def telloControllerThread(): tello = Tello('', 8889) north = 0 west = 0 south = 0 east = 0 landing = False while not receiving: pass try: tello.takeoff() except Exception: print("error occured taking off") while True: print("height") print(tello.get_height()) if tello.get_height() < 1 and not landing: resp = tello.move_up(0.2) print("response for moving up") print(resp) if north < 1: print("moving forward") resp = tello.move_forward(0.2) print("response for moving forward") print(resp) north = north + 0.2 elif west < 1: print("moving left") resp = tello.move_left(0.2) print("response for moving left") print(resp) west = west + 0.2 elif south < 1: print("moving backward") resp = tello.move_backward(0.2) print("response for moving backward") print(resp) south = south + 0.2 elif east < 1: print("moving right") resp = tello.move_right(0.2) print("response for moving east") print(resp) east = east + 0.2 else: landing = True tello.land()
class Drone: def __init__(self, local_ip, local_port): self.drone = Tello(local_ip, local_port) def close(self): self.drone.close() def dame_imagen(self): return self.drone.get_image() def dame_objeto(self): return self.drone.get_object(showImageFiltered=True) # ------------ EXTRA ------------ def despegar(self): return self.drone.takeoff() def aterrizar(self): return self.drone.land() # ------------ CONTROL EN VELOCIDAD ------------ def to_rc(self, v): return math.floor((100 * v) / V_MAX) def to_rc_rot(self, w): return math.floor((100 * w) / W_MAX) # Las 4 funciones: velocidad, avanzar, retroceder y lateral_izquierda # son bloqueantes, avanzan hasta una determinada posición y paran # y después ejecutan la siguiente instrucción (no hay movimiento contínuo) #def velocidad(self, vel): # return self.set_speed(vel) #def avanzar(self, vel): # self.drone.forward_speed(vel) #def retroceder(self, vel): # self.drone.backward_speed(vel) #def lateral_izquierda(self, vel): # self.drone.left_speed(vel) # Modo RadioControl, movimiento constante con rangos de velocidades # -100 a 100 (simulando un stick analógico). He hecho pruebas y la velocidad # máxima de avance (con vx = 100) es de aprox. 1.5 m/s (igual que si pilotas en la app # del móvil) aunque en los foros y la documentación pone que es máximo 1 m/s. # La velocidad máxima teórica del Tello es de 10 m/s pero está capado (hay que tocar el # firmware). # Con el API solo se puede ir en modo lento (velocidad max de 1 m/s), y con la app # del movil se puede poner el modo rápido que tiene una velocidad maxima de 3 m/s. def _rc(self, vy, vx, vz, rot): self.drone.set_velocities(vy, vx, vz, rot) def avanzar(self, vx): vx = self.to_rc(vx) self._rc(0, vx, 0, 0) def retroceder(self, vx): vx = self.to_rc(vx) * (-1) self._rc(0, vx, 0, 0) def lateral_izquierda(self, vy): vy = self.to_rc(vy) self._rc(vy, 0, 0, 0) def lateral_derecha(self, vy): vy = self.to_rc(vy) * (-1) self._rc(vy, 0, 0, 0) def subir(self, vz): vz = self.to_rc(vz) self._rc(0, 0, vz, 0) def bajar(self, vz): vz = self.to_rc(vz) * (-1) self._rc(0, 0, vz, 0) def girar_izquierda(self, rot): rot = self.to_rc_rot(rot) self._rc(0, 0, 0, rot) def girar_derecha(self, rot): rot = self.to_rc_rot(rot) * (-1) self._rc(0, 0, 0, rot) def movimiento_libre(self, vx, vy, vz, rot): self._rc(self.to_rc(vy), self.to_rc(vx), self.to_rc(vz), self.to_rc_rot(rot)) def parar(self): self.drone.stop() # ------------ CONTROL EN POSICION ------------ def girar_derecha_hasta(self, grados): return self.drone.rotate_cw(grados) #def girar_derecha(self, grados): # return self.rotate_cw(grados) def girar_izquierda_hasta(self, grados): return self.drone.rotate_ccw(grados) #def girar_izquierda(self, grados): # return self.rotate_ccw(grados) def retrocecer_hasta(self, distancia): return self.drone.move_backward(distancia) def bajar_hasta(self, distancia): return self.drone.move_down(distancia) def avanzar_hasta(self, distancia): return self.drone.move_forward(distancia) def izquierda_hasta(self, distancia): return self.drone.move_left(distancia) def derecha_hasta(self, distancia): return self.drone.move_right(distancia) def subir_hasta(self, distancia): return self.drone.move_up(distancia) # ------ METODOS INFORMATIVOS ------ def altura_actual(self): return self.drone.get_height() def bateria_restante(self): return self.drone.get_battery() def tiempo_de_vuelo(self): return self.drone.get_flight_time() def velocidad_actual(self): return self.drone.get_speed()
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()