Advertisement
Guest User

bebop

a guest
May 21st, 2018
88
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 12.36 KB | None | 0 0
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3.  
  4. import rospy
  5. import math
  6. from geometry_msgs.msg import Vector3
  7. from mav_msgs.msg import Actuators
  8. from nav_msgs.msg import Odometry
  9. from std_msgs.msg import Float64
  10. from pid import PID
  11. from trajectory_msgs.msg import MultiDOFJointTrajectory
  12.  
  13.  
  14. class LaunchBebop:
  15.  
  16.     def __init__(self):
  17.         """Constructor initializes all needed variables"""
  18.  
  19.         self.mass = 0.5  # kg           --> mass of the quadcopter
  20.         self.Ix = 0.00389  # kg m^2     --> Quadrotor moment of inertia in body x direction
  21.         self.Iy = 0.00389  # kg m^2     --> Quadrotor moment of inertia in body y direction
  22.         self.Iz = 0.0078  # kg m^2      --> Quadrotor moment of inertia in body z direction
  23.         self.Tm = 0.0125  # s           --> Time constant of a motor
  24.         self.bf = 8.548e-6  # kg m      --> Thrust constant of a motor
  25.         self.bm = 0.016  # m            --> Moment constant of a motor
  26.         self.l = 0.12905  # m           --> The distance of a motor from a center of mass
  27.         self.gravity = 9.81  # m/s^2    --> Gravity value
  28.         self.sleep_sec = 2
  29.  
  30.         self.hover_speed = math.sqrt(4.905 / self.bf / 4)
  31.  
  32.         self.first_measurement = False
  33.         self.controller_info = rospy.get_param("~verbose", False)
  34.         self.wind_controller = rospy.get_param("~wind", False)
  35.         self.hoover = rospy.get_param("~hoover", False)
  36.  
  37.         self.odom_subscriber = rospy.Subscriber(
  38.             "bebop/odometry",
  39.             Odometry,
  40.             self.odometry_callback)
  41.         self.pose_subscriber = rospy.Subscriber(
  42.             "bebop/pos_ref",
  43.             Vector3,
  44.             self.setpoint_cb)
  45.         self.odom_gt_subscriber = rospy.Subscriber(
  46.             "bebop/odometry_gt",
  47.             Odometry,
  48.             self.odometry_gt_callback)
  49.         self.angle_subscriber = rospy.Subscriber(
  50.             "bebop/angle_ref",
  51.             Vector3,
  52.             self.angle_cb)
  53.         self.velocity_subscriber = rospy.Subscriber(
  54.             "bebop/lin_vel_pub",
  55.             Vector3,
  56.             self.linvel_cb)
  57.  
  58.         # initialize publishers
  59.         self.motor_pub = rospy.Publisher(
  60.             '/gazebo/command/motor_speed',
  61.             Actuators,
  62.             queue_size=10)
  63.         self.error_pub = rospy.Publisher(
  64.             '/bebop/pos_error',
  65.             Float64,
  66.             queue_size=10)
  67.         self.error_vel_pub = rospy.Publisher(
  68.             '/bebop/vel_error',
  69.             Float64,
  70.             queue_size=10)
  71.         self.motor_pub = rospy.Publisher(
  72.             '/gazebo/command/motor_speed',
  73.             Actuators,
  74.             queue_size=10)
  75.         self.actuator_msg = Actuators()
  76.        
  77.         # define vector for measured and setopint values
  78.         if self.hoover == True:
  79.             self.pose_sp = Vector3(0., 0., 1.)
  80.         else:
  81.             self.pose_sp = Vector3(0., 0., 0.)
  82.         self.euler_sp = Vector3(0., 0., 0.)
  83.         self.euler_mv = Vector3(0., 0., 0.)
  84.         self.euler_rate_mv = Vector3(0., 0., 0.)
  85.         self.t_old = 0
  86.  
  87.         # define PID for height control
  88.         self.z_mv = 0
  89.  
  90.         # Crontroller rate
  91.         self.controller_rate = 50
  92.         self.rate = rospy.Rate(self.controller_rate)
  93.  
  94.         # define PID for height rate control
  95.         self.vz_sp = 0          # vz velocity set point
  96.         self.vz_mv = 0          # vz velocity measured value
  97.  
  98.         # Height controller
  99.         self.pid_vz = PID(195.8, 0, 1.958, 300, -300)
  100.  
  101.         # Position loop
  102.         if self.wind_controller:
  103.             # TODO: Tune paramters
  104.             print("LaunchBebop.init() - Wind parameters active")
  105.             self.pid_z = PID(4, 0.05, 0.1, 10, -10)
  106.             self.pid_x = PID(0.5, 0.06, 0.03, 0.35, -0.35)
  107.             self.pid_y = PID(0.5, 0.06, 0.03, 0.35, -0.35)
  108.  
  109.         else:
  110.             print("LaunchBebop.init() - Non Wind parameters active")
  111.             self.pid_z = PID(4, 0.05, 0.1, 10, -10)
  112.             self.pid_x = PID(0.25, 0.05, 0.055, 0.18, -0.18)
  113.             self.pid_y = PID(0.25, 0.05, 0.055, 0.18, -0.18)
  114.  
  115.         # outer_loops
  116.         self.pitch_PID = PID(4.44309, 0.1, 0.2, 100, -100)
  117.         self.roll_PID = PID(4.44309, 0.1, 0.2, 100, -100)
  118.         self.yaw_PID = PID(25, 0, 0.0, 150, -150)
  119.  
  120.         # inner_loops
  121.         self.pitch_rate_PID = PID(16.61, 0, 0, 100, -100)
  122.         self.roll_rate_PID = PID(16.61, 0, 0, 100, -100)
  123.  
  124.         # Pre-filter constants
  125.         self.filt_const_x = 0.5
  126.         self.filt_const_y = 0.5
  127.         self.filt_const_z = 0.1
  128.  
  129.         # Post filter values
  130.         self.z_filt_sp = 0
  131.         self.y_filt_sp = 0
  132.         self.x_filt_sp = 0
  133.  
  134.         # Define magic thrust number :-)
  135.         self.magic_number = 0.908
  136.  
  137.         # Velocity refs at start
  138.         self.linvel_x = 0
  139.         self.linvel_y = 0
  140.         self.linvel_z = 0
  141.  
  142.     def setpoint_cb(self, data):
  143.  
  144.         self.pose_sp.x = data.x
  145.         self.pose_sp.y = data.y
  146.         self.pose_sp.z = data.z
  147.  
  148.     def linvel_cb(self, data):
  149.  
  150.         self.linvel_x = data.x
  151.         self.linvel_y = data.y
  152.         self.linvel_z = data.z
  153.  
  154.     def angle_cb(self, data):
  155.  
  156.         self.euler_sp = Vector3(data.x, data.y, data.z)
  157.  
  158.     def odometry_callback(self, data):
  159.         """Callback function for odometry subscriber"""
  160.  
  161.         self.first_measurement = True
  162.  
  163.         self.x_mv = data.pose.pose.position.x
  164.         self.y_mv = data.pose.pose.position.y
  165.         self.z_mv = data.pose.pose.position.z
  166.  
  167.         self.vx_mv = data.twist.twist.linear.x
  168.         self.vy_mv = data.twist.twist.linear.y
  169.         self.vz_mv = data.twist.twist.linear.z
  170.  
  171.         self.p = data.twist.twist.angular.x
  172.         self.q = data.twist.twist.angular.y
  173.         self.r = data.twist.twist.angular.z
  174.  
  175.         self.qx = data.pose.pose.orientation.x
  176.         self.qy = data.pose.pose.orientation.y
  177.         self.qz = data.pose.pose.orientation.z
  178.         self.qw = data.pose.pose.orientation.w
  179.  
  180.     def odometry_gt_callback(self, data):
  181.         self.x_gt_mv = data.pose.pose.position.x
  182.         self.y_gt_mv = data.pose.pose.position.y
  183.         self.z_gt_mv = data.pose.pose.position.z
  184.  
  185.     def convert_to_euler(self, qx, qy, qz, qw):
  186.         """Calculate roll, pitch and yaw angles/rates with quaternions"""
  187.  
  188.         # conversion quaternion to euler (yaw - pitch - roll)
  189.         self.euler_mv.x = math.atan2(2 * (qw * qx + qy * qz), qw * qw
  190.                                      - qx * qx - qy * qy + qz * qz)
  191.         self.euler_mv.y = math.asin(2 * (qw * qy - qx * qz))
  192.         self.euler_mv.z = math.atan2(2 * (qw * qz + qx * qy), qw * qw
  193.                                      + qx * qx - qy * qy - qz * qz)
  194.  
  195.         # gyro measurements (p,q,r)
  196.         p = self.p
  197.         q = self.q
  198.         r = self.r
  199.  
  200.         sx = math.sin(self.euler_mv.x)  # sin(roll)
  201.         cx = math.cos(self.euler_mv.x)  # cos(roll)
  202.         cy = math.cos(self.euler_mv.y)  # cos(pitch)
  203.         ty = math.tan(self.euler_mv.y)  # cos(pitch)
  204.  
  205.         # conversion gyro measurements to roll_rate, pitch_rate, yaw_rate
  206.         self.euler_rate_mv.x = p + sx * ty * q + cx * ty * r
  207.         self.euler_rate_mv.y = cx * q - sx * r
  208.         self.euler_rate_mv.z = sx / cy * q + cx / cy * r
  209.  
  210.     def run(self):
  211.         """ Run ROS node - computes PID algorithms for z and vz control """
  212.  
  213.         while not self.first_measurement:
  214.             print("LaunchBebop.run() - Waiting for first measurement.")
  215.             rospy.sleep(self.sleep_sec)
  216.  
  217.         print("LaunchBebop.run() - Starting position control")
  218.         self.t_old = rospy.Time.now()
  219.  
  220.         while not rospy.is_shutdown():
  221.             self.rate.sleep()
  222.  
  223.             t = rospy.Time.now()
  224.             dt = (t - self.t_old).to_sec()
  225.             self.t_old = t
  226.  
  227.             if dt < 1.0/self.controller_rate:
  228.                 continue
  229.  
  230.             self.convert_to_euler(self.qx, self.qy, self.qz, self.qw)
  231.  
  232.             # HEIGHT CONTROL
  233.             self.z_filt_sp = prefilter(self.pose_sp.z, self.z_filt_sp, self.filt_const_z)
  234.             vz_sp = self.pid_z.compute(self.z_filt_sp, self.z_mv, dt)
  235.             u_height = self.pid_vz.compute(vz_sp, self.vz_mv, dt)
  236.  
  237.             # PITCH CONTROL OUTER LOOP
  238.             # x - position control
  239.             self.x_filt_sp = prefilter(self.pose_sp.x, self.x_filt_sp, self.filt_const_x)
  240.             pitch_sp = self.pid_x.compute(self.pose_sp.x, self.x_mv, dt)
  241.  
  242.             # ROLL CONTROL OUTER LOOP
  243.             # y position control
  244.             self.y_filt_sp = prefilter(self.pose_sp.y, self.y_filt_sp, self.filt_const_y)
  245.             roll_sp = - self.pid_y.compute(self.pose_sp.y, self.y_mv, dt)
  246.  
  247.             # PITCH AND ROLL YAW ADJUSTMENT
  248.             roll_sp_2 = math.cos(self.euler_mv.z) * roll_sp + \
  249.                       math.sin(self.euler_mv.z) * pitch_sp
  250.             pitch_sp = math.cos(self.euler_mv.z) * pitch_sp - \
  251.                        math.sin(self.euler_mv.z) * roll_sp
  252.             roll_sp = roll_sp_2
  253.  
  254.             # PITCH CONTROL INNER LOOP
  255.             pitch_rate_sp = self.pitch_PID.compute(pitch_sp, self.euler_mv.y, dt)
  256.             u_pitch = self.pitch_rate_PID.compute(pitch_rate_sp,  self.euler_rate_mv.y, dt)
  257.  
  258.             # ROLL CONTROL INNER LOOP
  259.             roll_rate_sp = self.roll_PID.compute(roll_sp, self.euler_mv.x, dt)
  260.             u_roll = self.roll_rate_PID.compute(roll_rate_sp, self.euler_rate_mv.x, dt)
  261.  
  262.             # YAW CONTROL
  263.             error_yrc = self.euler_sp.z - self.euler_mv.z
  264.             if math.fabs(error_yrc) > math.pi:
  265.                 self.euler_sp.z = (self.euler_mv.z/math.fabs(self.euler_mv.z))*\
  266.                                   (2*math.pi - math.fabs(self.euler_sp.z))
  267.             u_yaw = self.yaw_PID.compute(self.euler_sp.z, self.euler_mv.z, dt)
  268.  
  269.             # Calculate position error
  270.             error = math.sqrt((self.pose_sp.x - self.x_gt_mv)**2 +
  271.                               (self.pose_sp.y - self.y_gt_mv)**2 +
  272.                               (self.pose_sp.z - self.z_gt_mv)**2)
  273.             self.error_pub.publish(error)
  274.  
  275.             # Calculate velocity error
  276.             error_vel = math.sqrt((self.linvel_x - self.vx_mv)**2 +
  277.                                   (self.linvel_y - self.vy_mv)**2 +
  278.                                   (self.linvel_z - self.vz_mv)**2)
  279.             self.error_vel_pub.publish(error_vel)
  280.  
  281.             # angular velocity of certain rotor
  282.             motor_speed1 = self.hover_speed + u_height - u_roll - u_pitch - u_yaw
  283.             motor_speed2 = self.hover_speed + u_height + u_roll - u_pitch + u_yaw
  284.             motor_speed3 = self.hover_speed + u_height + u_roll + u_pitch - u_yaw
  285.             motor_speed4 = self.hover_speed + u_height - u_roll + u_pitch + u_yaw
  286.             self.actuator_msg.angular_velocities = \
  287.                 [self.magic_number * motor_speed1, self.magic_number * motor_speed2,
  288.                  motor_speed3, motor_speed4]
  289.  
  290.             # Print out controller information
  291.             if self.controller_info:
  292.                 print(dt)
  293.                 print("Comparison x:{}\nx_m:{}\ny:{}\ny_m:{}\nz:{}\nz_m{}\nyaw:{}\nyaw_m:{}".format(
  294.                     self.pose_sp.x,
  295.                     self.x_mv,
  296.                     self.pose_sp.y,
  297.                     self.y_mv,
  298.                     self.pose_sp.z,
  299.                     self.z_mv,
  300.                     self.euler_sp.z,
  301.                     self.euler_mv.z))
  302.                 print("Motor speeds are {}".format(self.actuator_msg.angular_velocities))
  303.                 print("Current quadcopter height is: {}".format(self.z_mv))
  304.                 print("Hover speed is: {}\n"
  305.                       "Pitch PID output is:{}\n"
  306.                       "Roll PID output is:{}\n"
  307.                       "Yaw PID output is:{}\n"
  308.                       "pitch_sp: {}, roll_sp: {}\n"
  309.                       "Error: {}\n".format(
  310.                     self.hover_speed, u_pitch, u_roll, u_yaw, pitch_sp, roll_sp, error))
  311.  
  312.             self.motor_pub.publish(self.actuator_msg)
  313.  
  314.  
  315. def prefilter(x_k, x_k_1, a):
  316.     """
  317.    First order filter.
  318.    (1 - a) * xK-1 + a * xK
  319.  
  320.    :param x_k: Current value
  321.    :param x_k_1: Previous value
  322.    :param a: Filter constant
  323.    :return:
  324.    """
  325.     return (1 - a) * x_k_1 + a * x_k
  326.  
  327.  
  328. if __name__ == "__main__":
  329.     rospy.init_node('bebop_launch', anonymous=True)
  330.     try:
  331.         launch_bebop = LaunchBebop()
  332.         launch_bebop.run()
  333.     except rospy.ROSInterruptException:
  334.         pass
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement