コード例 #1
0
    def __init__(self):
        rp.init_node("magdrone_node")

        # Create PID Controller
        self.pid_z = PIDcontroller(0.1, 0.0, 0.0, 1)

        # Create log file
        self.log_file = open("log.txt", 'a')

        # Set up Subscribers
        self.pose_sub = rp.Subscriber("/aruco_single/pose",
                                      PoseStamped,
                                      self.pose_callback,
                                      queue_size=1)

        # Set up Publishers

        # Connect to the Vehicle
        self.printAndLog('Connecting to Vehicle')
        self.vehicle = connect('/dev/serial0', wait_ready=True, baud=57600)

        # Variables
        self.cmds = None
        self.land = 0
        self.dsrm = 0
        self.arm = 0
        self.exit = 0

        # Create thread for publisher
        self.rate = 20
        t = threading.Thread(target=self.send_commands)
        t.start()

        rp.spin()
コード例 #2
0
    def __init__(self):
        rp.init_node("magdrone_node")

        # Create PID Controller
        self.pid_z = PIDcontroller(0.1, 0.0, 0.01, 1)

        # Create log file
        self.log_book = LogBook("test_flight")

        # Set up Subscribers
        self.pose_sub = rp.Subscriber("/aruco_single/pose", PoseStamped, self.pose_callback, queue_size=1)
        self.joy_sub = rp.Subscriber("/joy", Joy, self.joy_callback, queue_size=1)

	# Labeling the log file by controller
        self.log_book.printAndLog('Running aruco_joy_controller.py')

        # Connect to the Vehicle
        #self.log_book.printAndLog('Connecting to Vehicle')
        print('Connecting to Vehicle')
        self.vehicle = connect('/dev/serial0', wait_ready=True, baud=57600)

        # Variables
        self.cmds = None
        self.linear_z_cmd = 0.0
        self.land = 0
        self.dsrm = 0
        self.arm = 0
        self.exit = 0

        # Create thread for publisher
        self.rate = 30
        t = threading.Thread(target=self.send_commands)
        t.start()

        rp.spin()
コード例 #3
0
    def send_commands(self):
        print("Accepting Commands")
        r = rp.Rate(self.rate)
        while not rp.is_shutdown():
            if self.cmds is not None:
                # PID update error
                self.pid_z.updateError(self.z_error)
                self.pid_y.updateError(self.y_error)
                self.pid_x.updateError(self.x_error)

                # Generate commands
                self.linear_z_cmd = self.clipCommand(
                    self.pid_z.getCommand() + 0.5, 0.65, 0.35)
                self.linear_y_cmd = self.clipCommand(self.pid_y.getCommand(),
                                                     7.5, -7.5)
                self.linear_x_cmd = self.clipCommand(self.pid_x.getCommand(),
                                                     7.5, -7.5)
                self.set_attitude(roll_angle=self.linear_y_cmd,
                                  pitch_angle=-self.linear_x_cmd,
                                  yaw_angle=None,
                                  yaw_rate=-self.cmds.angular.z,
                                  use_yaw_rate=True,
                                  thrust=self.linear_z_cmd,
                                  duration=1.0 / self.rate)

                #msg = "thrust: " + str(self.linear_z_cmd) + " roll angle: " + str(self.cmds.linear.x) + " pitch angle: " + str(self.cmds.linear.y)
                #self.log_book.justLog(msg)

                if self.arm > 0:
                    self.log_book.printAndLog("Arming...")
                    self.arm_and_takeoff_nogps()
                if self.arm > 0:
                    self.log_book.printAndLog("Arming...")
                    self.arm_and_takeoff_nogps()
                if self.exit == 0:
                    msg = str(self.z_error) + "\t" + str(
                        self.linear_z_cmd) + "\t" + str(
                            self.y_error) + "\t" + str(
                                self.linear_y_cmd) + "\t" + str(
                                    self.x_error) + "\t" + str(
                                        self.linear_x_cmd)
                    self.log_book.justLog(msg)
                if self.z_error == 0.0:
                    print("Changing thrust PID")
                    self.pid_z = PIDcontroller(1.0, 0.0, 17.5, 3)
                if self.y_error == 0.0:
                    print("Changing roll PID")
                    self.pid_y = PIDcontroller(9.5, 0.03, 200.0, 30)
                if self.x_error == 0.0:
                    print("Changing pitch PID")
                    self.pid_x = PIDcontroller(9.5, 0.03, 200.0, 30)
            r.sleep()
コード例 #4
0
    def __init__(self):
        rp.init_node("magdrone_aruco")

        # Connect to the Vehicle
        rp.loginfo('Connecting to Vehicle')
        self.vehicle = connect('/dev/serial0', wait_ready=True, baud=57600)

        # Set up tf listener
        self.tf_listener = tf.TransformListener()

        # Set up Subscribers
        self.joy_sub = rp.Subscriber("/joy",
                                     Joy,
                                     self.joy_callback,
                                     queue_size=1)

        # Set up Publishers
        self.setpoint_pub = rp.Publisher("/setpoint",
                                         Vector3Stamped,
                                         queue_size=1)
        self.command_pub = rp.Publisher("/commands",
                                        TwistStamped,
                                        queue_size=1)

        # Create PID Controller
        self.pid_z = PIDcontroller(1.0, 0.0, 17.5, 3)
        self.pid_y = PIDcontroller(9.5, 0.0, 200.0, 30)
        self.pid_x = PIDcontroller(9.5, 0.0, 200.0, 30)
        self.pid_w = PIDcontroller(0.15, 0.0, 0.0, 30)

        # Variables
        self.arm = 0
        self.engage_controller = False

        # Create thread for publisher
        self.rate = 30
        t = threading.Thread(target=self.send_commands)
        t.start()

        rp.spin()
コード例 #5
0
    def __init__(self):
        rp.init_node("magdrone_node")

        # Create PID Controller
        self.pid_z = PIDcontroller(1.0, 0.0, 17.5, 3)
        self.pid_y = PIDcontroller(9.5, 0.0, 200.0, 30)
        self.pid_x = PIDcontroller(9.5, 0.0, 200.0, 30)

        # Create log file
        self.log_book = LogBook("test_flight")

        # Set up Subscribers
        self.pose_sub = rp.Subscriber("/vrpn_client_node/UAV/pose",
                                      PoseStamped,
                                      self.pose_callback,
                                      queue_size=1)
        self.joy_sub = rp.Subscriber("/joy",
                                     Joy,
                                     self.joy_callback,
                                     queue_size=1)

        # Labeling the log file by controller
        self.log_book.printAndLog('Running optitrack_controller.py')

        # Connect to the Vehicle
        #self.log_book.printAndLog('Connecting to Vehicle')
        print('Connecting to Vehicle')
        self.vehicle = connect('/dev/serial0', wait_ready=True, baud=57600)

        # Variables
        self.cmds = None
        self.linear_z_cmd = 0.0
        self.linear_y_cmd = 0.0
        self.land = 0
        self.change_PIDs = 0
        self.arm = 0
        self.exit = 0
        self.z_error = 0.0
        self.y_error = 0.0
        self.x_error = 0.0

        # Create thread for publisher
        self.rate = 30
        t = threading.Thread(target=self.send_commands)
        t.start()

        rp.spin()
コード例 #6
0
class magdroneControlNode():
    def __init__(self):
        rp.init_node("magdrone_node")

        # Create PID Controller
        self.pid_z = PIDcontroller(0.1, 0.0, 0.0, 1)

        # Create log file
        self.log_file = open("log.txt", 'a')

        # Set up Subscribers
        self.pose_sub = rp.Subscriber("/aruco_single/pose",
                                      PoseStamped,
                                      self.pose_callback,
                                      queue_size=1)

        # Set up Publishers

        # Connect to the Vehicle
        self.printAndLog('Connecting to Vehicle')
        self.vehicle = connect('/dev/serial0', wait_ready=True, baud=57600)

        # Variables
        self.cmds = None
        self.land = 0
        self.dsrm = 0
        self.arm = 0
        self.exit = 0

        # Create thread for publisher
        self.rate = 20
        t = threading.Thread(target=self.send_commands)
        t.start()

        rp.spin()

    def printAndLog(self, msg):
        print(msg)
        self.log_file.write(msg)
        self.log_file.write("\n")

    def arm_and_takeoff_nogps(self, aTargetAltitude=-1.0):
        """
        Arms vehicle and fly to aTargetAltitude without GPS data.
        """

        ##### CONSTANTS #####
        DEFAULT_TAKEOFF_THRUST = 0.55
        SMOOTH_TAKEOFF_THRUST = 0.52

        self.printAndLog("Basic pre-arm checks")
        # Don't let the user try to arm until autopilot is ready
        # If you need to disable the arming check,
        # just comment it with your own responsibility.
        # while not self.vehicle.is_armable:
        #   print(" Waiting for vehicle to initialise...")
        #  time.sleep(1)

        self.printAndLog("Arming motors")
        #  GUIDED_NOGPS mode is recommended by DroneKit
        self.vehicle.mode = VehicleMode("GUIDED_NOGPS")
        self.vehicle.armed = True

        while not self.vehicle.armed:
            self.printAndLog(" Waiting for arming...")
            self.vehicle.armed = True
            time.sleep(1)

        self.printAndLog("Armed!")

        if aTargetAltitude > 0:
            print("Taking off!")

            thrust = DEFAULT_TAKEOFF_THRUST
            while True:
                current_altitude = self.vehicle.location.global_relative_frame.alt
                print(" Altitude: %f  Desired: %f" %
                      (current_altitude, aTargetAltitude))
                # Trigger just below target alt.
                if current_altitude >= aTargetAltitude * 0.95:
                    print("Reached target altitude")
                    break
                elif current_altitude >= aTargetAltitude * 0.6:
                    thrust = SMOOTH_TAKEOFF_THRUST
                self.set_attitude(thrust=thrust)
                time.sleep(0.2)

    def send_attitude_target(self,
                             roll_angle=0.0,
                             pitch_angle=0.0,
                             yaw_angle=None,
                             yaw_rate=0.0,
                             use_yaw_rate=False,
                             thrust=0.5):
        """
        use_yaw_rate: the yaw can be controlled using yaw_angle OR yaw_rate.
                      When one is used, the other is ignored by Ardupilot.
        thrust: 0 <= thrust <= 1, as a fraction of maximum vertical thrust.
                Note that as of Copter 3.5, thrust = 0.5 triggers a special case in
                the code for maintaining current altitude.
        """
        if yaw_angle is None:
            # this value may be unused by the vehicle, depending on use_yaw_rate
            yaw_angle = self.vehicle.attitude.yaw
        # Thrust >  0.5: Ascend
        # Thrust == 0.5: Hold the altitude
        # Thrust <  0.5: Descend
        msg = self.vehicle.message_factory.set_attitude_target_encode(
            0,  # time_boot_ms
            1,  # Target system
            1,  # Target component
            0b00000000 if use_yaw_rate else 0b00000100,
            to_quaternion(roll_angle, pitch_angle, yaw_angle),  # Quaternion
            0,  # Body roll rate in radian
            0,  # Body pitch rate in radian
            math.radians(yaw_rate),  # Body yaw rate in radian/second
            thrust  # Thrust
        )
        self.vehicle.send_mavlink(msg)

    def set_attitude(self,
                     roll_angle=0.0,
                     pitch_angle=0.0,
                     yaw_angle=None,
                     yaw_rate=0.0,
                     use_yaw_rate=False,
                     thrust=0,
                     duration=0.05):
        """
        Note that from AC3.3 the message should be re-sent more often than every
        second, as an ATTITUDE_TARGET order has a timeout of 1s.
        In AC3.2.1 and earlier the specified attitude persists until it is canceled.
        The code below should work on either version.
        Sending the message multiple times is the recommended way.
        """
        self.send_attitude_target(roll_angle, pitch_angle, yaw_angle, yaw_rate,
                                  use_yaw_rate, thrust)
        start = time.time()
        while time.time() - start < duration:
            self.send_attitude_target(roll_angle, pitch_angle, yaw_angle,
                                      yaw_rate, use_yaw_rate, thrust)
            time.sleep(0.1)
        # Reset attitude, or it will persist for 1s more due to the timeout
        self.send_attitude_target(0, 0, 0, 0, True, thrust)

    def pose_callback(self, data):
        self.cmds = Twist()

        # Create Empty Commands
        self.cmds.linear.x = 0  # roll
        self.cmds.linear.y = 0  # pitch
        self.cmds.linear.z = 0  # thrust
        self.cmds.angular.z = 0  # yaw

        # Defining the desired positions
        self.z_des = 0  #thrust
        self.y_des = 0  #roll
        self.x_des = 1  #pitch
        """
	+ z error = + thrust
	- z error = - thrust
	+ y error = - roll
	- y error = + roll
	+ x error = + pitch
	- x error = - pitch 		
	"""

        # Position conversions where the reported position is in terms of the camera frame
        # z-error = x-tag - z_des = y-camera
        # y-error = y-tag - y_des = x-camera
        # x-error = z-tag - x_des = z-camera
        self.z_error = data.pose.position.y + self.z_des
        self.y_error = data.pose.position.x + self.y_des
        self.x_error = data.pose.position.z + self.x_des

        # PID update error
        self.pid_z.updateError(self.z_error)

        # generate thrust command
        self.cmds.linear.z = self.pid_z.getCommand() + 0.5
        if self.cmds.linear.z > 0.55:
            self.cmds.linear.z = 0.55
        if self.cmds.linear.z < 0.45:
            self.cmds.linear.z = 0.45
        msg = "error: " + str(self.z_error) + " read position: " + str(
            data.pose.position.y) + " thrust: " + str(self.cmds.linear.z)
        self.printAndLog(msg)

    def send_commands(self):
        self.printAndLog("Accepting Commands")
        self.printAndLog("Initiating Arming")
        self.arm_and_takeoff_nogps()

        r = rp.Rate(self.rate)
        while not rp.is_shutdown():
            # print(self.vehicle.attitude.yaw)
            if self.cmds is not None and self.vehicle.armed:
                self.set_attitude(roll_angle=0,
                                  pitch_angle=0,
                                  yaw_angle=None,
                                  yaw_rate=0,
                                  use_yaw_rate=True,
                                  thrust=self.cmds.linear.z)
                msg = "thrust: " + str(self.cmds.linear.z)
                self.printAndLog(msg)
                if self.dsrm > 0:
                    self.printAndLog("Disarming")
                    self.set_attitude(thrust=0, duration=8)
                    self.printAndLog("Disarm complete")
                if self.arm > 0:
                    self.printAndLog("Arming...")
                    self.arm_and_takeoff_nogps()
                if self.exit > 0:
                    self.printAndLog("Switched to manual controls")
            r.sleep()
コード例 #7
0
class magdroneControlNode():
    def __init__(self):
        rp.init_node("magdrone_node")

        # Create PID Controller
        self.pid_z = PIDcontroller(1.0, 0.0, 17.5, 3)
        self.pid_y = PIDcontroller(9.5, 0.0, 200.0, 30)
        self.pid_x = PIDcontroller(9.5, 0.0, 200.0, 30)

        # Create log file
        self.log_book = LogBook("test_flight")

        # Set up Subscribers
        self.pose_sub = rp.Subscriber("/vrpn_client_node/UAV/pose",
                                      PoseStamped,
                                      self.pose_callback,
                                      queue_size=1)
        self.joy_sub = rp.Subscriber("/joy",
                                     Joy,
                                     self.joy_callback,
                                     queue_size=1)

        # Labeling the log file by controller
        self.log_book.printAndLog('Running optitrack_controller.py')

        # Connect to the Vehicle
        #self.log_book.printAndLog('Connecting to Vehicle')
        print('Connecting to Vehicle')
        self.vehicle = connect('/dev/serial0', wait_ready=True, baud=57600)

        # Variables
        self.cmds = None
        self.linear_z_cmd = 0.0
        self.linear_y_cmd = 0.0
        self.land = 0
        self.change_PIDs = 0
        self.arm = 0
        self.exit = 0
        self.z_error = 0.0
        self.y_error = 0.0
        self.x_error = 0.0

        # Create thread for publisher
        self.rate = 30
        t = threading.Thread(target=self.send_commands)
        t.start()

        rp.spin()

    def clipCommand(self, cmd, upperBound, lowerBound):
        if cmd < lowerBound:
            cmd = lowerBound
        elif cmd > upperBound:
            cmd = upperBound

        return cmd

    def arm_and_takeoff_nogps(self, aTargetAltitude=-1.0):
        """
        Arms vehicle and fly to aTargetAltitude without GPS data.
        """

        ##### CONSTANTS #####
        DEFAULT_TAKEOFF_THRUST = 0.55
        SMOOTH_TAKEOFF_THRUST = 0.52

        #self.log_book.printAndLog("Basic pre-arm checks")
        # Don't let the user try to arm until autopilot is ready
        # If you need to disable the arming check,
        # just comment it with your own responsibility.
        # while not self.vehicle.is_armable:
        #   print(" Waiting for vehicle to initialise...")
        #  time.sleep(1)

        #self.log_book.printAndLog("Arming motors")
        #  GUIDED_NOGPS mode is recommended by DroneKit
        self.vehicle.mode = VehicleMode("GUIDED_NOGPS")
        self.vehicle.armed = True

        while not self.vehicle.armed:
            #self.log_book.printAndLog(" Waiting for arming...")
            self.vehicle.armed = True
            time.sleep(1)

        #self.log_book.printAndLog("Armed!")
        print('Armed')

        if aTargetAltitude > 0:
            print("Taking off!")

            thrust = DEFAULT_TAKEOFF_THRUST
            while True:
                current_altitude = self.vehicle.location.global_relative_frame.alt
                print(" Altitude: %f  Desired: %f" %
                      (current_altitude, aTargetAltitude))
                # Trigger just below target alt.
                if current_altitude >= aTargetAltitude * 0.95:
                    print("Reached target altitude")
                    break
                elif current_altitude >= aTargetAltitude * 0.6:
                    thrust = SMOOTH_TAKEOFF_THRUST
                self.set_attitude(thrust=thrust)
                time.sleep(0.2)

    def send_attitude_target(self,
                             roll_angle=0.0,
                             pitch_angle=0.0,
                             yaw_angle=None,
                             yaw_rate=0.0,
                             use_yaw_rate=False,
                             thrust=0.5):
        """
        use_yaw_rate: the yaw can be controlled using yaw_angle OR yaw_rate.
                      When one is used, the other is ignored by Ardupilot.
        thrust: 0 <= thrust <= 1, as a fraction of maximum vertical thrust.
                Note that as of Copter 3.5, thrust = 0.5 triggers a special case in
                the code for maintaining current altitude.
        """
        if yaw_angle is None:
            # this value may be unused by the vehicle, depending on use_yaw_rate
            yaw_angle = self.vehicle.attitude.yaw

        # Thrust >  0.5: Ascend
        # Thrust == 0.5: Hold the altitude
        # Thrust <  0.5: Descend
        msg = self.vehicle.message_factory.set_attitude_target_encode(
            0,  # time_boot_ms
            1,  # Target system
            1,  # Target component
            0b00000000 if use_yaw_rate else 0b00000100,
            to_quaternion(roll_angle, pitch_angle, yaw_angle),  # Quaternion
            0,  # Body roll rate in radian
            0,  # Body pitch rate in radian
            math.radians(yaw_rate),  # Body yaw rate in radian/second
            thrust  # Thrust
        )
        self.vehicle.send_mavlink(msg)

    def set_attitude(self,
                     roll_angle=0.0,
                     pitch_angle=0.0,
                     yaw_angle=None,
                     yaw_rate=0.0,
                     use_yaw_rate=False,
                     thrust=0,
                     duration=0.05):
        """
        Note that from AC3.3 the message should be re-sent more often than every
        second, as an ATTITUDE_TARGET order has a timeout of 1s.
        In AC3.2.1 and earlier the specified attitude persists until it is canceled.
        The code below should work on either version.
        Sending the message multiple times is the recommended way.
        """
        self.send_attitude_target(roll_angle, pitch_angle, yaw_angle, yaw_rate,
                                  use_yaw_rate, thrust)
        start = time.time()
        while time.time() - start < duration:
            self.send_attitude_target(roll_angle, pitch_angle, yaw_angle,
                                      yaw_rate, use_yaw_rate, thrust)
            time.sleep(duration)
        # Reset attitude, or it will persist for 1s more due to the timeout
        self.send_attitude_target(0, 0, 0, 0, True, thrust)

    def pose_callback(self, data):
        # Empty Commands
        # self.cmds.linear.x = 0   # roll
        # self.cmds.linear.y = 0   # pitch
        # self.cmds.angular.z = 0  # yaw
        # self.linear_z_cmd = 0   # thrust
        """
        + z error = + thrust
        - z error = - thrust
        + y error = - roll
        - y error = + roll
        + x error = + pitch
        - x error = - pitch
        """
        # Define the desired position
        self.z_des = 1.0  # thrust
        self.y_des = 0.0  # roll
        self.x_des = 0.0  # pitch

        # Position conversions where the reported position is in terms of the camera frame
        # z-error = x-tag - z_des = y-camera
        # y-error = y-tag - y_des = x-camera
        # x-error = z-tag - x_des = z-camera
        self.z_error = self.z_des - data.pose.position.z
        self.y_error = self.y_des - data.pose.position.y
        self.x_error = data.pose.position.x - self.x_des

    def joy_callback(self, data):
        # Empty Command
        self.cmds = Twist()

        # Joystick Controls
        self.cmds.linear.x = data.axes[2] * 10  # roll
        self.cmds.linear.y = data.axes[3] * 10  # pitch
        self.cmds.angular.z = data.axes[0] * 10  # yaw

        # Button Controls
        self.change_PIDs = data.buttons[0]
        self.land = data.buttons[1]
        self.arm = data.buttons[9]
        self.mag = data.axes[5]
        self.exit = data.buttons[2]

    def send_commands(self):
        print("Accepting Commands")
        r = rp.Rate(self.rate)
        while not rp.is_shutdown():
            if self.cmds is not None:
                # PID update error
                self.pid_z.updateError(self.z_error)
                self.pid_y.updateError(self.y_error)
                self.pid_x.updateError(self.x_error)

                # Generate commands
                self.linear_z_cmd = self.clipCommand(
                    self.pid_z.getCommand() + 0.5, 0.65, 0.35)
                self.linear_y_cmd = self.clipCommand(self.pid_y.getCommand(),
                                                     7.5, -7.5)
                self.linear_x_cmd = self.clipCommand(self.pid_x.getCommand(),
                                                     7.5, -7.5)
                self.set_attitude(roll_angle=self.linear_y_cmd,
                                  pitch_angle=-self.linear_x_cmd,
                                  yaw_angle=None,
                                  yaw_rate=-self.cmds.angular.z,
                                  use_yaw_rate=True,
                                  thrust=self.linear_z_cmd,
                                  duration=1.0 / self.rate)

                #msg = "thrust: " + str(self.linear_z_cmd) + " roll angle: " + str(self.cmds.linear.x) + " pitch angle: " + str(self.cmds.linear.y)
                #self.log_book.justLog(msg)

                if self.arm > 0:
                    self.log_book.printAndLog("Arming...")
                    self.arm_and_takeoff_nogps()
                if self.arm > 0:
                    self.log_book.printAndLog("Arming...")
                    self.arm_and_takeoff_nogps()
                if self.exit == 0:
                    msg = str(self.z_error) + "\t" + str(
                        self.linear_z_cmd) + "\t" + str(
                            self.y_error) + "\t" + str(
                                self.linear_y_cmd) + "\t" + str(
                                    self.x_error) + "\t" + str(
                                        self.linear_x_cmd)
                    self.log_book.justLog(msg)
                if self.z_error == 0.0:
                    print("Changing thrust PID")
                    self.pid_z = PIDcontroller(1.0, 0.0, 17.5, 3)
                if self.y_error == 0.0:
                    print("Changing roll PID")
                    self.pid_y = PIDcontroller(9.5, 0.03, 200.0, 30)
                if self.x_error == 0.0:
                    print("Changing pitch PID")
                    self.pid_x = PIDcontroller(9.5, 0.03, 200.0, 30)
            r.sleep()
コード例 #8
0
class magdroneControlNode():

    def __init__(self):
        rp.init_node("magdrone_aruco")

        # Connect to the Vehicle
        rp.loginfo('Connecting to Vehicle')
        self.vehicle = connect('/dev/serial0', wait_ready=True, baud=57600)

        # Set up tf listener
        self.tf_listener = tf.TransformListener()

        # Set up Subscribers
        self.joy_sub = rp.Subscriber(
            "/joy", Joy, self.joy_callback, queue_size=1)

        # Set up Publishers
        self.setpoint_pub = rp.Publisher(
            "/setpoint", Vector3Stamped, queue_size=1)
        self.command_pub = rp.Publisher(
            "/commands", TwistStamped, queue_size=1)

        # Create PID Controller
        self.pid_z = PIDcontroller(1.0, 0.0, 17.5, 3)
        self.pid_y = PIDcontroller(9.5, 0.0, 200.0, 30)
        self.pid_x = PIDcontroller(9.5, 0.0, 200.0, 30)
        self.pid_w = PIDcontroller(0.15, 0.0, 0.0, 30)

        # Variables
        self.arm = 0
        self.engage_controller = False

        # Create thread for publisher
        self.rate = 30
        t = threading.Thread(target=self.send_commands)
        t.start()

        rp.spin()

    '''
        DroneKit Functions
    '''

    def arm_and_takeoff_nogps(self, aTargetAltitude=-1.0):
        """
        Arms vehicle and fly to aTargetAltitude without GPS data.
        """

        ##### CONSTANTS #####
        DEFAULT_TAKEOFF_THRUST = 0.55
        SMOOTH_TAKEOFF_THRUST = 0.52

        rp.loginfo("Basic pre-arm checks")
        # Don't let the user try to arm until autopilot is ready
        # If you need to disable the arming check,
        # just comment it with your own responsibility.
        while not self.vehicle.is_armable:
            rp.loginfo(" Waiting for vehicle to initialise...")
            time.sleep(1)

        rp.loginfo("Arming motors")
        #  GUIDED_NOGPS mode is recommended by DroneKit
        self.vehicle.mode = VehicleMode("GUIDED_NOGPS")
        self.vehicle.armed = True

        while not self.vehicle.armed:
            rp.loginfo(" Waiting for arming...")
            self.vehicle.armed = True
            time.sleep(1)

        rp.loginfo('Armed')

        if aTargetAltitude > 0:
            rp.loginfo("Taking off!")

            thrust = DEFAULT_TAKEOFF_THRUST
            while True:
                current_altitude = self.vehicle.location.global_relative_frame.alt
                rp.loginfo(" Altitude: %f  Desired: %f" %
                           (current_altitude, aTargetAltitude))
                # Trigger just below target alt.
                if current_altitude >= aTargetAltitude*0.95:
                    rp.loginfo("Reached target altitude")
                    break
                elif current_altitude >= aTargetAltitude*0.6:
                    thrust = SMOOTH_TAKEOFF_THRUST
                self.set_attitude(thrust=thrust)
                time.sleep(0.2)

    def send_attitude_target(self, roll_angle=0.0, pitch_angle=0.0,
                             yaw_angle=None, yaw_rate=0.0, use_yaw_rate=False,
                             thrust=0.5):
        """
        use_yaw_rate: the yaw can be controlled using yaw_angle OR yaw_rate.
                      When one is used, the other is ignored by Ardupilot.
        thrust: 0 <= thrust <= 1, as a fraction of maximum vertical thrust.
                Note that as of Copter 3.5, thrust = 0.5 triggers a special case in
                the code for maintaining current altitude.
        """
        if yaw_angle is None:
            # this value may be unused by the vehicle, depending on use_yaw_rate
            yaw_angle = self.vehicle.attitude.yaw

        # Thrust >  0.5: Ascend
        # Thrust == 0.5: Hold the altitude
        # Thrust <  0.5: Descend
        msg = self.vehicle.message_factory.set_attitude_target_encode(
            0,  # time_boot_ms
            1,  # Target system
            1,  # Target component
            0b00000000 if use_yaw_rate else 0b00000100,
            to_quaternion(roll_angle, pitch_angle, yaw_angle),  # Quaternion
            0,  # Body roll rate in radian
            0,  # Body pitch rate in radian
            math.radians(yaw_rate),  # Body yaw rate in radian/second
            thrust  # Thrust
        )
        self.vehicle.send_mavlink(msg)

    def set_attitude(self, roll_angle=0.0, pitch_angle=0.0,
                     yaw_angle=None, yaw_rate=0.0, use_yaw_rate=False,
                     thrust=0, duration=0.05):
        """
        Note that from AC3.3 the message should be re-sent more often than every
        second, as an ATTITUDE_TARGET order has a timeout of 1s.
        In AC3.2.1 and earlier the specified attitude persists until it is canceled.
        The code below should work on either version.
        Sending the message multiple times is the recommended way.
        """
        self.send_attitude_target(roll_angle, pitch_angle,
                                  yaw_angle, yaw_rate, use_yaw_rate,
                                  thrust)
        start = time.time()
        while time.time() - start < duration:
            self.send_attitude_target(roll_angle, pitch_angle,
                                      yaw_angle, yaw_rate, use_yaw_rate,
                                      thrust)
            time.sleep(duration)
        # Reset attitude, or it will persist for 1s more due to the timeout
        self.send_attitude_target(0, 0, 0, 0, True, thrust)

    '''
        Callbacks
    '''

    def joy_callback(self, data):
        # Empty Command
        self.cmds = Twist()

        # Joystick Controls
        self.cmds.linear.x = data.axes[2] * 10.0               # roll
        self.cmds.linear.y = data.axes[3] * 10.0               # pitch
        self.cmds.angular.z = data.axes[0] * 10.0               # yaw
        self.cmds.linear.z = 0.25 * (data.axes[1] / 2.0) + 0.5  # thrust

        # Button Controls
        self.arm = data.buttons[9]

        if data.buttons[4] == 1.0:
            if not self.engage_controller:
                rp.loginfo("Controller Engaged")
            else:
                rp.loginfo("Controller Disengaged")
            self.engage_controller = not self.engage_controller

    def clipCommand(self, cmd, upperBound, lowerBound):
        if cmd < lowerBound:
            cmd = lowerBound
        elif cmd > upperBound:
            cmd = upperBound

        return cmd

    '''
        Main Loop
    '''

    def send_commands(self):
        rp.loginfo("Accepting Commands")
        r = rp.Rate(self.rate)
        while not rp.is_shutdown():
            # Arm motors
            if self.arm > 0:
                rp.loginfo("Arming...")
                self.arm_and_takeoff_nogps()

            if self.engage_controller:
                # Get latest transform
                try:
                    (T, R) = self.tf_listener.lookupTransform('UAV', 'bundle', rp.Time())
                except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):
                    continue

                lastTFtime = self.tf_listener.getLatestCommonTime('raspicam', 'bundle')

                # Get current pose wrt Tag
                qx = R[0]
                qy = R[1]
                qz = R[2]
                qw = R[3]
                orientation = to_rpy(qw, qx, qy, qz)

                # Drone body system is Front-Left-Down
                w_drone = orientation[2]
                x_drone = T[0]
                y_drone = T[1]
                z_drone = T[2]

                # Set desired position
                des_position = [0, 0, -0.5]  # x, y, and z

                # Publish setpoint
                setpoint = Vector3Stamped()
                setpoint.header.stamp = rp.Time.now()
                setpoint.vector.x = des_position[0]
                setpoint.vector.y = des_position[1]
                setpoint.vector.z = des_position[2]
                self.setpoint_pub.publish(setpoint)

                """
                + z error = + thrust
                - z error = - thrust
                + y error = - roll
                - y error = + roll
                + x error = + pitch
                - x error = - pitch 		
                """

                # Position conversions where the reported position is in terms of the camera frame
                # z-error = x-tag - z_des = y-camera
                # y-error = y-tag - y_des = x-camera
                # x-error = z-tag - x_des = z-camera

                # Calculate error
                x_error = des_position[0] - x_drone
                y_error = des_position[1] - y_drone
                z_error = des_position[2] - z_drone
                dw = w_drone

                if dw > 180.0:
                    w_error = dw - 360.0
                else:
                    w_error = dw

                # Update errors
                self.pid_w.updateError(w_error)
                self.pid_x.updateError(x_error)
                self.pid_y.updateError(y_error)
                self.pid_z.updateError(z_error)

                # Get commands
                dt_ros = rp.get_rostime() - lastTFtime
                dt = dt_ros.secs + dt_ros.nsecs * 1e-9

                if dt < 2.0:
                    angular_z_cmd = self.clipCommand(self.pid_w.getCommand(), 5, -5)
                    linear_z_cmd  = self.clipCommand(self.pid_z.getCommand() + 0.5, 0.65, 0.35)
                    linear_y_cmd  = self.clipCommand(self.pid_y.getCommand() - 1.3, 7.5, -7.5)
                    linear_x_cmd  = self.clipCommand(self.pid_x.getCommand() + 0.45, 7.5, -7.5)
                else:
                    angular_z_cmd = 0.0
                    linear_z_cmd  = 0.5
                    linear_y_cmd  = -1.3
                    linear_x_cmd  = 0.45
                    rp.logwarn("Marker lost for more than 2 seconds!!! Hovering")

                # Apply commands
                self.set_attitude(roll_angle=-linear_y_cmd,
                                  pitch_angle=-linear_x_cmd,
                                  yaw_angle=None,
                                  yaw_rate=angular_z_cmd, use_yaw_rate=True,
                                  thrust=linear_z_cmd,
                                  duration=1.0/self.rate)

                # Publish commands for logging
                cmd = TwistStamped()
                cmd.header.stamp = rp.Time.now()
                cmd.twist.angular.x = -linear_y_cmd
                cmd.twist.angular.y = -linear_x_cmd
                cmd.twist.angular.z = angular_z_cmd
                cmd.twist.linear.z  = linear_z_cmd
                self.command_pub.publish(cmd)
            r.sleep()