Ejemplo n.º 1
0
 def start_log(self, directory, name):
     self.log = Logger(directory, name)
Ejemplo n.º 2
0
class Drone(object):
    """
    Drone class
    """

    def __init__(self, connection, tlog_directory="Logs", tlog_name="TLog.txt"):

        self.connection = connection

        self._message_time = 0.0
        self._message_frequency = 0.0
        self._time_bias = 0.0
        # Global position in degrees (int)
        # Altitude is in meters
        self._longitude = 0
        self._latitude = 0
        self._altitude = 0
        self._global_position_time = 0.0
        self._global_position_frequency = 0.0

        # Reference home position in degrees (int)
        # Altitude is in meters
        self._home_longitude = 0
        self._home_latitude = 0
        self._home_altitude = 0
        self._home_position_time = 0.0
        self._home_position_frequency = 0.0

        # Local positions in meters from the global home (float)
        # In NED frame
        self._north = 0.0
        self._east = 0.0
        self._down = 0.0
        self._local_position_time = 0.0
        self._local_position_frequency = 0.0

        # Locally oriented velocity in meters/second
        # In NED frame
        self._velocity_north = 0.0
        self._velocity_east = 0.0
        self._velocity_down = 0.0
        self._local_velocity_time = 0.0
        self._local_velocity_frequency = 0.0

        # If the drone is armed the motors are powered and the rotors are spinning.
        self._armed = False

        # If the drone is guided it is being autonomously controlled,
        # the other opposite would be manual control.
        self._guided = False
        
        # An integer to pass along random status changes specific for different vehicles
        self._status = 0
        self._state_time = 0.0
        self._state_frequency = 0.0

        # Euler angles in radians
        self._roll = 0.0
        self._pitch = 0.0
        self._yaw = 0.0
        self._attitude_time = 0.0
        self._attitude_frequency = 0.0

        # Drone body accelerations
        self._acceleration_x = 0.0
        self._acceleration_y = 0.0
        self._acceleration_z = 0.0
        self._acceleration_time = 0.0
        self._acceleration_frequency = 0.0

        # Drone gyro rates or angular velocities in radians/second
        # TODO: Explain what x, y, and z are.
        self._gyro_x = 0.0
        self._gyro_y = 0.0
        self._gyro_z = 0.0
        self._gyro_time = 0.0
        self._gyro_frequency = 0.0

        # Barometer
        # TODO: better explanation
        self._baro_altitude = 0.0
        self._baro_time = 0.0
        self._baro_frequency = 0.0

        # Initializing telemetry logger
        self.tlog = Logger(tlog_directory, tlog_name)

        self._update_property = {
            MsgID.STATE: self._update_state,
            MsgID.GLOBAL_POSITION: self._update_global_position,
            MsgID.LOCAL_POSITION: self._update_local_position,
            MsgID.GLOBAL_HOME: self._update_global_home,
            MsgID.LOCAL_VELOCITY: self._update_local_velocity,
            MsgID.RAW_GYROSCOPE: self._update_gyro_raw,
            MsgID.RAW_ACCELEROMETER: self._update_acceleration_raw,
            MsgID.BAROMETER: self._update_barometer,
            MsgID.ATTITUDE: self._update_attitude
        }

        # set the internal callbacks list to an empty dict
        self._callbacks = {}

        # configure this drone class to listen to all of the messages from the connection

        def on_message_receive(msg_name, msg):
            """Sorts incoming messages, updates the drone state variables and runs callbacks"""
            # print('Message received', msg_name, msg)
            if (((msg.time - self._message_time) > 0.0)):
                self._message_frequency = 1.0 / (msg.time - self._message_time)
                self._message_time = msg.time
                self._time_bias = msg.time - time.time()

            if msg_name == MsgID.CONNECTION_CLOSED:
                self.stop()
            if msg_name in self._update_property.keys():
                self._update_property[msg_name](msg)
            self.notify_callbacks(msg_name, msg)  # pass it along to these listeners
            self.log_telemetry(msg_name, msg)

        # add the above callback function as a listener for all connection messages
        self.connection.add_message_listener(MsgID.ANY, on_message_receive)

    @property
    def global_position(self):
        return np.array([self._longitude, self._latitude, self._altitude])

    @property
    def global_position_time(self):
        return self._global_position_time

    def _update_global_position(self, msg):
        self._longitude = msg.longitude
        self._latitude = msg.latitude
        self._altitude = msg.altitude
        if (msg.time - self._global_position_time) > 0.0:
            self._global_position_frequency = 1.0 / (msg.time - self._global_position_time)

        self._global_position_time = msg.time

    @property
    def global_home(self):
        return np.array([self._home_longitude, self._home_latitude, self._home_altitude])

    @property
    def home_position_time(self):
        return self._home_position_time

    def _update_global_home(self, msg):
        self._home_longitude = msg.longitude
        self._home_latitude = msg.latitude
        self._home_altitude = msg.altitude
        if (msg.time - self._home_position_time) < 0.0:
            self._home_position_frequency = 1.0 / (msg.time - self._home_position_time)
        self._home_position_time = msg.time

    @property
    def local_position(self):
        return np.array([self._north, self._east, self._down])

    @property
    def local_position_time(self):
        return self._local_position_time

    def _update_local_position(self, msg):
        self._north = msg.north
        self._east = msg.east
        self._down = msg.down
        if (msg.time - self._local_position_time) > 0.0:
            self._local_position_frequency = 1.0 / (msg.time - self._local_position_time)
        self._local_position_time = msg.time

    @property
    def local_velocity(self):
        return np.array([self._velocity_north, self._velocity_east, self._velocity_down])

    @property
    def local_velocity_time(self):
        return self._local_velocity_time

    def _update_local_velocity(self, msg):
        self._velocity_north = msg.north
        self._velocity_east = msg.east
        self._velocity_down = msg.down
        if (msg.time - self._local_velocity_time) > 0.0:
            self._local_velocity_frequency = 1.0 / (msg.time - self._local_velocity_time)
        self._local_velocity_time = msg.time

    @property
    def armed(self):
        return self._armed

    @property
    def guided(self):
        return self._guided

    @property
    def connected(self):
        return self.connection.open

    @property
    def state_time(self):
        return self._state_time
    
    @property
    def status(self):
        return self._status

    def _update_state(self, msg):
        self._armed = msg.armed
        self._guided = msg.guided
        if (msg.time - self._state_time) > 0.0:
            self._state_frequency = 1.0 / (msg.time - self._state_time)
        self._state_time = msg.time
        self._status = msg.status

    @property
    def attitude(self):
        """Roll, pitch, yaw euler angles in radians"""
        return np.array([self._roll, self._pitch, self._yaw])

    @property
    def attitude_time(self):
        return self._attitude_time

    def _update_attitude(self, msg):
        self._roll = msg.roll
        self._pitch = msg.pitch
        self._yaw = msg.yaw
        if (msg.time - self._attitude_time) > 0.0:
            self._attitude_frequency = 1.0 / (msg.time - self._attitude_time)
            self._attitude_time = msg.time

    @property
    def acceleration_raw(self):
        return np.array([self._acceleration_x, self._acceleration_y, self._acceleration_z])

    @property
    def acceleration_time(self):
        return self._acceleration_time

    def _update_acceleration_raw(self, msg):
        self._acceleration_x = msg.x
        self._acceleration_y = msg.y
        self._acceleration_z = msg.z
        if (msg.time - self._acceleration_time) > 0.0:
            self._acceleration_frequency = 1.0 / (msg.time - self._acceleration_time)
        self._acceleration_time = msg.time

    @property
    def gyro_raw(self):
        """Angular velocites in radians/second"""
        return np.array([self._gyro_x, self._gyro_y, self._gyro_z])

    @property
    def gyro_time(self):
        return self._gyro_time

    def _update_gyro_raw(self, msg):
        self._gyro_x = msg.x
        self._gyro_y = msg.y
        self._gyro_z = msg.z
        if (msg.time - self._gyro_time) > 0.0:
            self._gyro_frequency = 1.0 / (msg.time - self._gyro_time)
        self._gyro_time = msg.time

    @property
    def barometer(self):
        return np.array(self._baro_altitude)

    @property
    def barometer_time(self):
        return self._baro_time

    def _update_barometer(self, msg):
        self._baro_altitude = msg.z
        self._baro_frequency = 1.0 / (msg.time - self._baro_time)
        self._baro_time = msg.time

    def log_telemetry(self, msg_name, msg):
        """Save the msg information to the telemetry log"""
        if self.tlog.open:
            data = [msg_name]
            data.append(msg.time)
            for k in msg.__dict__.keys():
                if k != '_time':
                    data.append(msg.__dict__[k])
            self.tlog.log_telemetry_data(data)

    @staticmethod
    def read_telemetry_data(filename):
        log_dict = {}
        tlog = open(filename, 'r')
        lines = tlog.readlines()

        for line in lines:
            line_split = line.rstrip('\n').split(',')
            if line_split[0] in log_dict.keys():
                entry = log_dict[line_split[0]]
                for i in range(1, len(line_split)):
                    if line_split[i] == 'True':
                        entry[i - 1] = np.append(entry[i - 1], True)
                    elif line_split[i] == 'False':
                        entry[i - 1] = np.append(entry[i - 1], False)
                    else:
                        entry[i - 1] = np.append(entry[i - 1], float(line_split[i]))
            else:
                entry = []
                for i in range(1, len(line_split)):
                    if line_split[i] == 'True':
                        entry.append(np.array(True))
                    elif line_split[i] == 'False':
                        entry.append(np.array(False))
                    else:
                        entry.append(np.array(float(line_split[i])))

            log_dict[line_split[0]] = entry
        return log_dict

    #
    # Handling of internal messages for callbacks
    #

    def register_callback(self, name, fn):
        """Add the function, `fn`, as a callback for the message type, `name`.

        Args:
            name: MsgID describing the message id
            fn: Callback function

        Example:

            self.add_message_listener(MsgID.GLOBAL_POSITION, global_msg_listener)

            OR

            self.add_message_listener(MsgID.ANY, all_msg_listener)

        These can be added anywhere in the code and are identical to initializing a callback with the decorator
        """
        if name not in self._callbacks:
            self._callbacks[name] = []
        if fn not in self._callbacks[name]:
            self._callbacks[name].append(fn)

    def remove_callback(self, name, fn):
        """Remove the function, `fn`, as a callback for the message type, `name`

        Args:
            name: MsgID describing the message id
            fn: Callback function

        Example:

            self.remove_message_listener(MsgID.GLOBAL_POSITION, global_msg_listener)

        """
        if name in self._callbacks:
            if fn in self._callbacks[name]:
                self._callbacks[name].remove(fn)
                if len(self._callbacks[name]) == 0:
                    del self._callbacks[name]

    def notify_callbacks(self, name, msg):
        """Passes the message to the appropriate listeners"""
        for fn in self._callbacks.get(name, []):
            try:
                # print('Drone executing {0} callback'.format(name))
                fn()
            except Exception as e:
                traceback.print_exc()

        for fn in self._callbacks.get(MsgID.ANY, []):
            try:
                # print('Drone executing {0} callback'.format(MsgID.ANY))
                fn(name)
            except Exception as e:
                traceback.print_exc()

    #
    # Command method wrappers
    #

    def arm(self):
        """Send an arm command to the drone"""
        try:
            self.connection.arm()
        except Exception as e:
            traceback.print_exc()

    def disarm(self):
        """Send a disarm command to the drone"""
        try:
            self.connection.disarm()
        except Exception as e:
            traceback.print_exc()

    def take_control(self):
        """Send a command to the drone to switch to guided (autonomous) mode.

        Essentially control the drone with code.
        """
        try:
            self.connection.take_control()
        except Exception as e:
            traceback.print_exc()

    def release_control(self):
        """Send a command to the drone to switch to manual mode.

        Essentially you control the drone manually via some interface."""
        try:
            self.connection.release_control()
        except Exception as e:
            traceback.print_exc()

    def cmd_position(self, north, east, altitude, heading):
        """Command the local position and drone heading.

        Args:
            north: local north in meters
            east: local east in meters
            altitude: altitude above ground in meters
            heading: drone yaw in radians
        """
        try:
            # connection cmd_position is defined as NED, so need to flip the sign
            # on altitude
            self.connection.cmd_position(north, east, -altitude, heading)
        except Exception as e:
            traceback.print_exc()

    def takeoff(self, target_altitude):
        """Command the drone to takeoff to the target_alt (in meters)"""
        try:
            self.connection.takeoff(self.local_position[0], self.local_position[1], target_altitude)
        except Exception as e:
            traceback.print_exc()

    def land(self):
        """Command the drone to land at its current position"""
        try:
            self.connection.land(self.local_position[0], self.local_position[1])
        except Exception as e:
            traceback.print_exc()

    def cmd_attitude(self, roll, pitch, yaw_rate, thrust):
        """Command the drone through attitude command

        Args:
            roll: in radians
            pitch: in randians
            yaw_rate: in radians/second
            thrust: upward acceleration in meters/second^2
        """
        try:
            self.connection.cmd_attitude(roll, pitch, yaw_rate, thrust)
        except Exception as e:
            traceback.print_exc()

    def cmd_attitude_rate(self, roll_rate, pitch_rate, yaw_rate, thrust):
        """Command the drone orientation rates.

        Args:
            roll_rate: in radians/second
            pitch_rate: in radians/second
            yaw_rate: in radians/second
            thrust: upward acceleration in meters/second^2
        """
        try:
            self.connection.cmd_attitude_rate(roll_rate, pitch_rate, yaw_rate, thrust)
        except Exception as e:
            traceback.print_exc()

    def cmd_moment(self, roll_moment, pitch_moment, yaw_moment, thrust):
        """Command the drone moments.

        Args:
            roll_moment: in Newton*meter
            pitch_moment: in Newton*meter
            yaw_moment: in Newton*meter
            thrust: upward force in Newtons
        """
        try:
            self.connection.cmd_moment(roll_moment, pitch_moment, yaw_moment, thrust)
        except Exception as e:
            traceback.print_exc()

    def cmd_velocity(self, velocity_north, velocity_east, velocity_down, heading):
        """Command the drone velocity.

        Args:
            north_velocity: in meters/second
            east_velocity: in meters/second
            down_velocity: in meters/second
            heading: in radians
        """
        try:
            self.connection.cmd_velocity(velocity_north, velocity_east, velocity_down, heading)
        except Exception as e:
            traceback.print_exc()

    def set_home_position(self, longitude, latitude, altitude):
        """Set the drone's home position to these coordinates"""
        try:
            self.connection.set_home_position(latitude, longitude, altitude)

        except Exception as e:
            traceback.print_exc()

    def start_log(self, directory, name):
        self.log = Logger(directory, name)

    def stop_log(self):
        """Stop collection of logs"""
        self.log.close()

    def start(self):
        """Starts the connection to the drone"""

        # start the connection
        self.connection.start()

    def stop(self):
        """Stops the connection to the drone and closes the log"""

        # stop the connection
        self.connection.stop()

        # close the telemetry log
        self.tlog.close()
Ejemplo n.º 3
0
    def __init__(self, connection, tlog_directory="Logs", tlog_name="TLog.txt"):

        self.connection = connection

        self._message_time = 0.0
        self._message_frequency = 0.0
        self._time_bias = 0.0
        # Global position in degrees (int)
        # Altitude is in meters
        self._longitude = 0
        self._latitude = 0
        self._altitude = 0
        self._global_position_time = 0.0
        self._global_position_frequency = 0.0

        # Reference home position in degrees (int)
        # Altitude is in meters
        self._home_longitude = 0
        self._home_latitude = 0
        self._home_altitude = 0
        self._home_position_time = 0.0
        self._home_position_frequency = 0.0

        # Local positions in meters from the global home (float)
        # In NED frame
        self._north = 0.0
        self._east = 0.0
        self._down = 0.0
        self._local_position_time = 0.0
        self._local_position_frequency = 0.0

        # Locally oriented velocity in meters/second
        # In NED frame
        self._velocity_north = 0.0
        self._velocity_east = 0.0
        self._velocity_down = 0.0
        self._local_velocity_time = 0.0
        self._local_velocity_frequency = 0.0

        # If the drone is armed the motors are powered and the rotors are spinning.
        self._armed = False

        # If the drone is guided it is being autonomously controlled,
        # the other opposite would be manual control.
        self._guided = False
        
        # An integer to pass along random status changes specific for different vehicles
        self._status = 0
        self._state_time = 0.0
        self._state_frequency = 0.0

        # Euler angles in radians
        self._roll = 0.0
        self._pitch = 0.0
        self._yaw = 0.0
        self._attitude_time = 0.0
        self._attitude_frequency = 0.0

        # Drone body accelerations
        self._acceleration_x = 0.0
        self._acceleration_y = 0.0
        self._acceleration_z = 0.0
        self._acceleration_time = 0.0
        self._acceleration_frequency = 0.0

        # Drone gyro rates or angular velocities in radians/second
        # TODO: Explain what x, y, and z are.
        self._gyro_x = 0.0
        self._gyro_y = 0.0
        self._gyro_z = 0.0
        self._gyro_time = 0.0
        self._gyro_frequency = 0.0

        # Barometer
        # TODO: better explanation
        self._baro_altitude = 0.0
        self._baro_time = 0.0
        self._baro_frequency = 0.0

        # Initializing telemetry logger
        self.tlog = Logger(tlog_directory, tlog_name)

        self._update_property = {
            MsgID.STATE: self._update_state,
            MsgID.GLOBAL_POSITION: self._update_global_position,
            MsgID.LOCAL_POSITION: self._update_local_position,
            MsgID.GLOBAL_HOME: self._update_global_home,
            MsgID.LOCAL_VELOCITY: self._update_local_velocity,
            MsgID.RAW_GYROSCOPE: self._update_gyro_raw,
            MsgID.RAW_ACCELEROMETER: self._update_acceleration_raw,
            MsgID.BAROMETER: self._update_barometer,
            MsgID.ATTITUDE: self._update_attitude
        }

        # set the internal callbacks list to an empty dict
        self._callbacks = {}

        # configure this drone class to listen to all of the messages from the connection

        def on_message_receive(msg_name, msg):
            """Sorts incoming messages, updates the drone state variables and runs callbacks"""
            # print('Message received', msg_name, msg)
            if (((msg.time - self._message_time) > 0.0)):
                self._message_frequency = 1.0 / (msg.time - self._message_time)
                self._message_time = msg.time
                self._time_bias = msg.time - time.time()

            if msg_name == MsgID.CONNECTION_CLOSED:
                self.stop()
            if msg_name in self._update_property.keys():
                self._update_property[msg_name](msg)
            self.notify_callbacks(msg_name, msg)  # pass it along to these listeners
            self.log_telemetry(msg_name, msg)

        # add the above callback function as a listener for all connection messages
        self.connection.add_message_listener(MsgID.ANY, on_message_receive)