Пример #1
0
def main():
    print("Setting up Tello")
    drone = Tello()
    time.sleep(1)
    print("Tello set up!")
    print("Starting stream service")
    drone.send_command("streamon")
    time.sleep(2)
    cap = cv2.VideoCapture("udp://" + drone.tello_ip + ":11111")
    print("Started")
    while 1:
        _, frame = cap.read()
        cv2.imshow("DJI Tello", frame)
        k = cv2.waitKey(1) & 0xFF
        if k == 27:
            break
    cap.release()
    cv2.destroyAllWindows()
    drone.send_command("streamoff")
Пример #2
0
class Tello_controller:

    def __init__(self, speed = 50):
        '''
        Well, initialize
        '''
        #Class variables
        self.emergency_land_activated = False
        self.take_off_activated = False
        self.battery_log_delay = 2
        self.speed = speed
        #Initialize the drone and stream
        print("INIT: Connecting to Ryze Tello @192.168.10.1")
        print("Intructions: \t'Esc': Emergency Land")
        self.drone = Tello()
        time.sleep(1)
        print("LOG: Tello set up!")
        self.drone.send_command("streamon")
        time.sleep(1)
        self.cap = cv2.VideoCapture("udp://192.168.10.1:11111")
        # self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 3)
        print("LOG: Stream service on")
        #Set up battery and emergency land threads
        self.battery_thread = Thread(target=self._battery_thread)
        self.battery_thread.daemon = True
        self.battery_thread.start()
        print("LOG: Battery thread started")
        #Set up ArUco detection
        self.aruco_dict = aruco.Dictionary_get(aruco.DICT_6X6_250)
        self.params = aruco.DetectorParameters_create()
        print("LOG: ArUco set up")
        #Set up camera matrix and distortion coeffs
        self.cv_file = cv2.FileStorage("tello_params.yaml", cv2.FILE_STORAGE_READ)
        self.cam_mtx = self.cv_file.getNode("camera_matrix").mat()
        self.dist_coeff = self.cv_file.getNode("dist_coeff").mat()[0]
        self.cv_file.release()
        #Publish own location


    def run(self):
        '''
        Get stream, read aruco, get data, start controller
        '''
        print("INIT: Starting controller!")
        #One last warning before starting
        user_inp = input("Enter q to exit. Just press enter to start\n")
        if user_inp == "q":
            print("EXIT: Controller exited!")
            return 0
        #First frame takes time to load up
        #Take off
        self.drone.takeoff()
        time.sleep(1)
        self.take_off_activated = True
        print("LOG: Successful takeoff")
        #NOTE: First few frames take time to flush out
        #Run while emergency land is off
        img_id = 0
        while not self.emergency_land_activated:
            #Key press
            k = cv2.waitKey(1)
            #Detect marker
            _, frame = self.cap.read()
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            corners, ids, rejected = aruco.detectMarkers(gray,
                                        self.aruco_dict,
                                        parameters = self.params)
            #Draw the marker
            self.detected = aruco.drawDetectedMarkers(frame, corners)
            #Get the rotation and translation vectors
            if np.all(ids != None):
                rvec_list = []
                tvec_list = []
                #Get the estimated pose of the marker wrt camera, marker size = 9cm
                rvecs, tvecs, _ = aruco.estimatePoseSingleMarkers(corners, 9, self.cam_mtx, self.dist_coeff)
                #Also draw axes
                for i in range(len(ids)):
                    aruco.drawAxis(self.detected, self.cam_mtx, self.dist_coeff, rvecs[i], tvecs[i], 4.5)
                #Get the rotation matrix using Rodrigues formula
                r_mat, _ = cv2.Rodrigues(rvecs[0][0])
                #The only translation vector to use
                t_vec = tvecs[0][0]
                #Get transformation matrix
                self.M_mat = np.zeros((4,4))
                self.M_mat[:3, :3] = r_mat
                self.M_mat[:3, 3] = t_vec
                self.M_mat[3,3] = 1
                #Get inverse transformation, of camera wrt marker
                self.M_inv = np.linalg.inv(self.M_mat)
                #Get camera location wrt marker
                self.cam_coords = self.M_inv[:3, 3]
                # print("Camera coordinates:", self.cam_coords)
                self.cv_file = cv2.FileStorage("drone_loc.yaml", cv2.FILE_STORAGE_WRITE)
                self.cv_file.write("curr_loc", self.cam_coords)
                self.cv_file.release()
            #Show detection
            cv2.imshow("Tello Detected", self.detected)
            #Take pictures with s
            if k == ord("s"):
                cv2.imwrite("tello_img{0}.jpg".format(img_id), frame)
                print("LOG: Saved image as tello_img{0}.jpg".format(img_id))
                img_id += 1
            #Emergency land
            if k == 27:
                self.emergency_land()
                break
        #End statement
        self.cv_file = cv2.FileStorage("drone_loc.yaml", cv2.FILE_STORAGE_WRITE)
        self.cv_file.write("curr_loc", np.array([0,0,0]))
        self.cv_file.release()
        print("EXIT: Exited successfully")

    def _battery_thread(self):
        '''
        Periodically print out battery status
        Emergency land if battery < 20%
        '''
        #Run while emergency land is off
        while not self.emergency_land_activated:
            self.battery = self.drone.get_battery()
            print("BATT: {}%".format(self.battery))
            #Warning
            #Check for failsafe
            if self.battery < 20:
                print("EMERGENCY: Battery < 20%")
                self.emergency_land()
            elif self.battery < 40:
                print("WARNING: Battery < 40%")
            time.sleep(self.battery_log_delay)

    def emergency_land(self):
        '''
        Activate landing for drone at the current position
        '''
        self.emergency_land_activated = True
        #First slow down movement
        self.drone.set_speed(10)
        #Land
        print("EMERGENCY: Landing Drone!")
        self.drone.land()
        #Shutdown stream
        time.sleep(1)
        print("EMERGENCY: Shutting stream")
        self.cap.release()
        self.drone.send_command("streamoff")
        cv2.destroyAllWindows()
Пример #3
0
class Simulator():
    def __init__(self):
        self.takeoff_alt = 81
        self._init_state()
        self.driver_instance = None

        # Put drone into command mode
        self.command()

    def _init_state(self):
        self.altitude = 0
        self.cur_loc = (0,0)
        self.bearing = 0
        self.altitude_data = []
        self.path_coors = [(0,0)]
        self.flip_coors = []
        self.fig_count = 1
        self.command_log = []

    @staticmethod
    def serialize_command(command: dict):
        serialized = command['command']
        command_args = command.get('arguments', ())
        if len(command_args) > 0:
            serialized = '{} {}'.format(serialized, ' '.join([str(arg) for arg in command_args]))
        return serialized

    @staticmethod
    def check_flip_param(param: str):
        if param not in ["f", "b", "r", "l"]:
            raise Exception("I can't tell which way to flip. Please use f, b, r, or l")
        else:
            pass

    @staticmethod
    def check_int_param(param: int):
        if type(param) != int:
            raise Exception("This command only accepts whole numbers without quotation marks.")
        else:
            pass

    def send_command(self, command: str, *args):
        # Command log allows for replaying commands to the actual drone
        command_json = {
            'command': command,
            'arguments': args
        }
        self.command_log.append(command_json)
        print('I am running your "{}" command.'.format(self.serialize_command(command_json)))

        time.sleep(2)

    # Control Commands
    def command(self):
        print("Hi! My name is TelloSim and I am your training drone.")
        print("I help you try out your flight plan before sending it to a real Tello.")
        print("I am now ready to take off. 🚁","\n")
        self.send_command('command')

    def check_altitude(self):
        if self.altitude == 0:
            raise Exception("I can't do that unless I take off first!")
        else:
            # print("I am flying at {} centimeters above my takeoff altitude.".format(self.altitude))
            pass

    # Plotting functions
    def plot_altitude_steps(self):
        fig, ax = plt.subplots()
        ax.xaxis.set_major_locator(MaxNLocator(integer=True))
        ax.plot(self.altitude_data,'ro', linestyle='dashed', linewidth=2, markersize=12)
        ax.plot(self.altitude_data, linewidth=25, alpha=.15)
        ax.grid()
        ax.set(xlabel='Step', ylabel='Altitude in Centimeters',title='Tello Altitude')
        plt.show()

    def plot_horz_steps(self):
        title = "Path of Tello from Takeoff Location. \nLast Heading= {} Degrees from Start".format(self.bearing)
        fig, ax = plt.subplots()
        horz_df = pd.DataFrame(self.path_coors)
        xlow = min(horz_df[0])
        xhi = max(horz_df[0])
        ylow = min(horz_df[1])
        yhi = max(horz_df[1])
        xlowlim = -200 if xlow > -200 else xlow - 40
        xhilim = 200 if xhi < 200 else xhi + 40
        ylowlim = -200 if ylow > -200 else ylow - 40
        yhilim = 200 if yhi < 200 else yhi + 40
        ax.set_xlim([xlowlim,xhilim])
        ax.set_ylim([ylowlim,yhilim])
        ax.plot(horz_df[0], horz_df[1], 'bo', linestyle='dashed', linewidth=2, markersize=12, label="Drone Moves")
        ax.plot(horz_df[0], horz_df[1], linewidth=25, alpha=.15)
        if len(self.flip_coors) > 0:
            flip_df = pd.DataFrame(self.flip_coors)
            ax.plot(flip_df[0], flip_df[1], 'ro', markersize=12, label="Drone Flips")
        ax.xaxis.set_major_locator(MaxNLocator(integer=True))
        ax.grid()
        ax.legend()
        ax.set(xlabel='X Distance from Takeoff', ylabel='Y Distance from Takeoff',title=title)
        plt.show()

    # Determine bearing relative to start which is inline with positive y-axis
    @staticmethod
    def dist_bearing(orig, bearing, dist):
        rads = np.deg2rad(bearing)
        sines = np.sin(rads)
        coses = np.cos(rads)
        dx = sines * dist
        dy = coses * dist
        x_n = np.cumsum(dx) + orig[0]
        y_n = np.cumsum(dy) + orig[1]
        return x_n[0], y_n[0]

   # Movement Commands
    def takeoff(self):
        """
        Command drone to takeoff.

        Examples
        ----------
        drone.takeoff() # command drone to takeoff

        """
        if self.altitude == 0:
            print("Get ready for takeoff!")
            self.altitude = self.takeoff_alt
            self.altitude_data.append(self.takeoff_alt)
            self.send_command('takeoff')
            print("My estimated takeoff altitude is {} centimeters".format(self.altitude))
        else:
            print("My current altitude is {} centimeters, so I can't takeoff again!".format(self.altitude))

    def land(self):
        """
        Command drone to land.

        Examples
        ----------
        drone.land() # command drone to land

        """
        print("Get ready for landing!")
        self.check_altitude()
        self.altitude = 0
        self.send_command('land')
        print("Here are the graphs of your flight! I can't wait to try this for real.")
        self.plot_horz_steps()
        self.plot_altitude_steps()

    def up(self, dist: int):
        """
        Command drone to fly up a given number of centimeters.

        Parameters
        ----------
        dist : int

        Examples
        ----------
        drone.up(100) # move drone up 100 centimeters

        """
        self.check_altitude()
        self.check_int_param(dist)
        print("My current bearing is {} degrees.".format(self.bearing))
        self.altitude = self.altitude + dist
        self.altitude_data.append(self.altitude)
        self.send_command('up', dist)
        self.plot_altitude_steps()

    def down(self, dist: int):
        """
        Command drone to fly down a given number of centimeters.

        Parameters
        ----------
        dist : int

        Examples
        ----------
        drone.down(100) # move drone down 100 centimeters

        """
        self.check_altitude()
        self.check_int_param(dist)
        print("My current bearing is {} degrees.".format(self.bearing))
        self.altitude = self.altitude - dist
        self.altitude_data.append(self.altitude)
        self.send_command('down', dist)
        self.plot_altitude_steps()

    def left(self, dist: int):
        """
        Command drone to fly left a given number of centimeters.

        Parameters
        ----------
        dist : int

        Examples
        ----------
        drone.left(100) # move drone left 100 centimeters

        """
        self.check_altitude()
        self.check_int_param(dist)
        # print("My current bearing is {} degrees.".format(self.bearing))
        new_loc = self.dist_bearing(orig=self.cur_loc, bearing=self.bearing-90, dist=dist)
        self.cur_loc = new_loc
        self.path_coors.append(new_loc)
        # print(self.path_coors)
        self.send_command('left', dist)
        self.plot_horz_steps()

    def right(self, dist: int):
        """
        Command drone to fly right a given number of centimeters.

        Parameters
        ----------
        dist : int

        Examples
        ----------
        drone.right(100) # move drone right 100 centimeters

        """
        self.check_altitude()
        self.check_int_param(dist)
        # print("My current bearing is {} degrees.".format(self.bearing))
        new_loc = self.dist_bearing(orig=self.cur_loc, bearing=self.bearing+90, dist=dist)
        self.cur_loc = new_loc
        self.path_coors.append(new_loc)
        self.send_command('right', dist)
        self.plot_horz_steps()

    def forward(self, dist: int):
        """
        Command drone to fly forward a given number of centimeters.

        Parameters
        ----------
        dist : int

        Examples
        ----------
        drone.forward(100) # move drone forward 100 centimeters

        """
        self.check_altitude()
        self.check_int_param(dist)
        # print("My current bearing is {} degrees.".format(self.bearing))
        new_loc = self.dist_bearing(orig=self.cur_loc, bearing=self.bearing, dist=dist)
        self.cur_loc = new_loc
        self.path_coors.append(new_loc)
        self.send_command('forward', dist)
        self.plot_horz_steps()

    def back(self, dist: int):
        """
        Command drone to fly backward a given number of centimeters.

        Parameters
        ----------
        dist : int

        Examples
        ----------
        drone.back(100) # move drone backward 100 centimeters

        """
        self.check_altitude()
        self.check_int_param(dist)
        new_loc = self.dist_bearing(orig=self.cur_loc, bearing=self.bearing+180, dist=dist)
        self.cur_loc = new_loc
        self.path_coors.append(new_loc)
        self.send_command('back', dist)
        self.plot_horz_steps()

    def cw(self, degr: int):
        """
        Rotate drone clockwise.

        Parameters
        ----------
        degr : int

        Examples
        ----------
        drone.cw(90) # rotates drone 90 degrees clockwise

        """
        self.check_altitude()
        self.check_int_param(degr)
        # print("My current bearing is {} degrees.".format(self.bearing))
        self.bearing = self.bearing + (degr % 360)
        self.send_command('cw', degr)
        # print("My new bearing is {} degrees.".format(self.bearing))

    def ccw(self, degr: int):
        """
        Rotate drone counter clockwise.

        Parameters
        ----------
        degr : int

        Examples
        ----------
        drone.ccw(90) # rotates drone 90 degrees counter clockwise

        """
        self.check_altitude()
        self.check_int_param(degr)
        # print("My current bearing is {} degrees.".format(self.bearing))
        self.bearing = self.bearing - (degr % 360)
        self.send_command('ccw', degr)
        # print("My current bearing is {} degrees.".format(self.bearing))

    def flip(self, direc: str):
        """
        Flips drones in one of four directions:
        l - left
        r - right
        f - forward
        b - back

        Parameters
        ----------
        direc : str

        Examples
        ----------
        drone.flip("f") # flips drone forward

        """
        self.check_altitude()
        self.check_flip_param(direc)
        self.send_command('flip', direc)
        self.flip_coors.append(self.cur_loc)
        self.plot_horz_steps()

    # Deploys the command log from the simulation state to the actual drone
    def deploy(self):
        """
        Deploys commands built up for drone object to real drone via easyTello.
        Note: computer must be connected to the drone's WiFi network.

        Examples
        ----------
        drone.deploy() # deploy commands to drone

        """
        print('Deploying your commands to a real Tello drone!')

        if (self.driver_instance is None):
            # Since the driver binds to a socket on instantiation, we can only
            # keep a single driver instance open per session
            self.driver_instance = Tello()

        for command in self.command_log:
            self.driver_instance.send_command(self.serialize_command(command))

    # Resets the simulation state back to the beginning: no commands + landed
    def reset(self):
        """
        Reset the drone object to initialization state.

        Examples
        ----------
        drone.reset() # reset sim state

        """
        print('Resetting simulator state...')
        self._init_state()
        self.command()

    def save(self, file_path='commands.json'):
        """
        Save commands from current sim state to a local file.

        Parameters
        ----------
        file_path : str

        Examples
        ----------
        drone.save("commands.json") # save current state to JSON file

        """
        print('Saving commands to {}'.format(file_path))
        with open(file_path, 'w') as json_file:
            json.dump(self.command_log, json_file, indent=4)

    def load_commands(self, file_path:str):
        """
        Load commands from a local file to the current sim object.
        See documentation for the required file format.

        Parameters
        ----------
        file_path : str

        Examples
        ----------
        drone.load_commands("commands.json") # load commands from file to current sim object.

        """
        self._init_state()
        print('Loading commands from {}'.format(file_path))
        with open(file_path) as json_file:
            commands = json.load(json_file)

        for command in commands:
            # TODO guard checks
            getattr(self, command['command'])(*command['arguments'])