#!/usr/bin/env python import roslib; roslib.load_manifest("crazyflieROS") import rospy import random from sensor_msgs.msg import Joy as JoyMSG from crazyflieROS.msg import cmd as CFJoyMSG from crazyflieROS.msg import pid as PidMSG from crazyflieROS.cfg import cmdJoyPIDConfig as CFJoyCFG from dynamic_reconfigure.server import Server as DynamicReconfigureServer from geometry_msgs.msg import PoseStamped as PoseMSG import tf import tf.msg as TFMSG import os from optparse import OptionParser import time, sys from math import pi as PI from math import sqrt, sin, cos, degrees, radians, atan2, atan, copysign, asin, acos import numpy as np import time # print 'Python Path: %s' % os.environ['PYTHONPATH'].split(os.pathsep) def fATAN(dist, r): return degrees(atan(dist))*sqrt(r) def fASIN (dist, r): return degrees(asin(min(1.,max(-1, dist))))*r def fLIN(dist, mag): return dist*mag tid = 0 a = 0.75 y = -90 # cam simple with rotation trajectory = [(0.5,2.0,0.5,90), # take off (0.5,2.0,1.5,90), # init (0.5,2.0,1.0,90), # level - hover (1.5,2.0,1.0,90), # move x (0.5,2.0,1.0,90+45), #move back rotating 45 (1.5,2.0,1.0,45), # move x rotating -90 (0.5,2.0,1.0,90)] #move back rotaint 45 # updown, 1 meter trajectory = [(0,0,0.5,0), (0,0,1.5,0) ] # Left-right Trajectory. ALigned and 45degrees L=-1.0 R= -L F = 0.0 B = -F rot = 0.0 M=0.0 U=1.0 trajectory = [(L,F,U,rot),(R,F,U,rot)] # 3d circuit U = 1.5 D = 0.5 L = -1.25 R = -L F = 1.25 B = -F rot = 90 M = 0 trajectory = [(M,F,D,rot), (L,F,D,rot), (L,B,D,rot), (R,B,D,rot), (R,F,D,rot), (R,F,U,rot), (L,F,U,rot), (L,B,U,rot), (M,B,U,rot), (M,M,U,rot), (M,M,1.0,rot),(M,M,0.5,rot),(M,M,0.2,rot)] # yaw trajectory = [(M,M,D,0),(M,M,D,90),(M,M,D,0),(M,M,D,135),(M,M,D,0),(M,M,D,180)] # cam house trajectory = [(M,M,0.5,90), (M,M,1.0,90), (M,M,1.5,90),(M,M,1.0,90), (M,M,0.5,90), (M,F,D,rot), (L,F,D,rot), (L,B,D,rot), (R,B,D,rot), (R,F,D,rot), (R,F,U,rot), (L,F,U,rot), (L,B,U,rot), (M,B,U,rot), (M,M,U,rot), (M,M,1.0,rot),(M,M,0.5,rot),(M,M,0.2,rot)] # cam front ccrl xyz3 B = 0.0 F = 1.5 D = 0.75 UUD = 1.25 UDD = 1.0 U = 1.5 rot = 90 L = 1.5 R = -L M = 0 trajectory = [(M,F,D,rot),(M,F,U,rot),(L,F,U,rot),(L,F,D,rot),(R,F,D,rot),(R,F,U,rot),(M,F,U,rot),(M,B,U,rot),(L,B,U,rot),(L,B,D,rot),(R,B,D,rot),(R,B,U,rot),(M,B,U,rot),(M,F,U,rot),(M,F,D,rot),(M,F,0,rot)] #XYZ5 ccrl cam front 2 trajectory = [(R,F,0.0,rot),(R,F,D,rot),(R,F,U,rot),(L,F,U,rot),(L,B,U,rot),(R,B,U,rot),(R,B,UUD,rot),(R,B,UDD,rot),(R,B,D,rot),(R,F,D,rot),(L,F,D,rot),(L,B,D,rot),(R,B,D,rot),(R,B,0.25,rot)] F=2.0 #Y D=1.0 U=1.5 M=1.8 L=M-0.50 R=M+0.50 rot=90.0 trajectory = [(M,F,D,rot),(L,F,D,rot),(L,F,U,rot),(R,F,U,rot),(R,F,D,rot),(M,F,D,rot)] # Square with up/down in the middle X=0.5 Y=0.5 M=0.0 D=0.5 U=1.25 R=0 trajectory = [(M,M,U,R),(M,M,D,R),(X,Y,D,R),(-X,Y,D,R),(-X,-Y,D,R),(X,-Y,D,R),(X,Y,D,R),(M,M,D,R)] def enum(*sequential, **named): enums = dict(zip(sequential, range(len(sequential))), **named) reverse = dict((value, key) for key, value in enums.iteritems()) enums['to_str'] = reverse return type('Enum', (), enums) Button = enum(L1=10,R1=11,Select=0,Start=3,L2=8,R2=9,Up=4,Right=5,Down=6,Left=7,Square=15,Cross=14,Triangle=12,Circle=13) #Axes = enum(SLL=0,SLU=1,SRL=2,SRU=3,Up=4,Right=5,Down=6,Left=7,L2=8,R2=9,L1=10,R1=11,Triangle=12,Circle=13,Cross=14,Square=15,AccL=16,AccF=17,AccU=18,GyroY=19)#default bluez + sixaxis Axes = enum(SLL=0,SLU=1,SRL=2,SRU=3,Up=8,Right=9,Down=10,Left=11,L2=12,R2=13,L1=14,R1=15,Triangle=12,Circle=13,Cross=14,Square=15,AccL=4,AccF=5,AccU=6)#https://help.ubuntu.com/community/Sixaxis#Quick_Setup_Guide_for_12.10%3C/p%3E Source = enum(Qualisys=0,Cam=1,Synthetic=2) #Button = enum(L1=10,R1=11,Select=0,Start=3,L2=8,R2=9,Up=4,Right=5,Down=6,Left=7,Square=15,Cross=14,Triangle=12,Circle=13) #Axes = enum(SLL=0,SLU=1,SRL=2,SRU=3,Up=4,Right=5,Down=6,Left=7,L2=4+8,R2=4+9,L1=4+10,R1=4+11,Triangle=4+12,Circle=4+13,Cross=4+14,Square=4+15,AccL=16,AccF=17,AccU=18,GyroY=19) MAX_THRUST = 60000. def percentageToThrust( percentage): return int(MAX_THRUST*(percentage/100.0)) def deadband(value, threshold): if abs(value)<threshold: return 0.0 elif value>0: return value-threshold else: return value+threshold class PID: """ Classic PID Controller """ def __init__(self, P=1.0, I=0.0, D=0.0): self.P = P self.I = I self.D = D self.maxP = sys.float_info.max self.maxI = sys.float_info.max self.maxD = sys.float_info.max self.maxTotal = sys.float_info.max # Useful for I part self.error_sum = 0.0 # Useful for D part self.last_time = rospy.Time.now() self.last_error = 0.0# sys.float_info.max self.last_output = 0.0 return def get_command(self, error, rtime, d=None, i=None, f=lambda x: x): dt = (rtime-self.last_time).to_sec() if not dt>0: if dt==0: P = f(self.P * error) I = f(self.I * self.error_sum) self.last_time = rtime self.last_error = error total = P+I self.last_output = copysign(min(abs(total), self.maxTotal), total) return self.last_output, P, I, 0 rospy.logwarn("Negative time in PID controller: "+str(dt)+"s") return 0,0,0,0; #todo check -> probably reset stzff #TODO: add max limits P = f(self.P * error) if i is None: self.error_sum += (self.last_error+error)/2. * dt #trapazoidal integration I = f(self.I * self.error_sum) else: self.error_sum = i I = f(self.I * self.error_sum) if d is None: D = f(self.D * ((self.last_error-error) / dt)) alpha = 0.5 self.last_error = self.last_error*alpha + error*(1.-alpha) else: D = f(self.D * d) self.last_error = 0 # Cache time for computing dt in next step self.last_time = rtime # Compute command total = P+I-D self.last_output = copysign(min(abs(total), self.maxTotal), total) return self.last_output, P, I ,D def set_gains(self, P,I,D): self.P = P self.I = I self.D = D return def set_limits(self, maxTotal, maxP = float("inf"), maxI = float("inf"), maxD = float("inf")): self.maxP = maxP self.maxI = maxI self.maxD = maxD self.maxTotal = maxTotal def reset(self,time=None): # Useful for I part self.error_sum = 0.0 # Useful for D part if time is None: self.last_time = rospy.Time.now() else: self.last_time = time self.last_error = 0.0#sys.float_info.max #why max?? self.last_output = 0.0 def numpy2python(l): if isinstance(l,list): return [np.asscalar(e) if isinstance(e, np.generic) else e for e in l] if isinstance(l,tuple): return tuple([np.asscalar(e) if isinstance(e, np.generic) else e for e in l]) if isinstance(l,dict): d = {} for k, v in l.iteritems(): d[np.asscalar(k) if isinstance(k, np.generic) else k] = np.asscalar(v) if isinstance(v, np.generic) else v return d class JoyController: def __init__(self,options=None): self.options = options self.warntime = time.time() self.USB = False self.joy_scale = [-1,1,-1,1,1] #RPYTY self.trim_roll = 0 self.trim_pitch = 0 self.max_angle = 30 self.max_yawangle = 200 self.max_thrust = 80. self.min_thrust = 25. self.max_thrust_raw = percentageToThrust(self.max_thrust) self.min_thrust_raw = percentageToThrust(self.min_thrust) self.old_thurst_raw = 0 self.slew_limit = 45 self.slew_rate = 30 self.slew_limit_raw = percentageToThrust(self.slew_limit) self.slew_rate_raw = percentageToThrust(self.slew_rate) self.dynserver = None self.prev_cmd = None self.curr_cmd = None self.yaw_joy = True self.sub_joy = rospy.Subscriber("/joy", JoyMSG, self.new_joydata) self.sub_tf = tf.TransformListener() self.pub_tf = tf.TransformBroadcaster() self.pub_cfJoy = rospy.Publisher("/cfjoy", CFJoyMSG, queue_size=2) self.pub_PID = rospy.Publisher("/pid", PidMSG,queue_size=50) # PIDs for R,P,Y,THROTTLE self.PID = (PID(), PID(), PID(), PID()) # XY, Throttle, Yaw self.PIDActive = (True, True, True, True) # XY, YAw, Throttle on/off self.PIDDelay = 0.0 self.PIDSource = Source.Qualisys self.PIDControl = True self.goal = [0.0, 0.0, 0.0, 0.0] # X Y Z YAW self.prev_msg = None self.PIDSetCurrentAuto = True self.thrust = (40., 90., 66.) # Min, Max, Base thrust self.distFunc = fLIN self.wandControl = False # Dynserver self.dynserver = DynamicReconfigureServer(CFJoyCFG, self.reconfigure) def setXYRGoal(self, msg): new_settings = {} new_settings["x"] = msg.pose.position.x new_settings["y"] = msg.pose.position.y new_settings["rz"] = degrees(tf.transformations.euler_from_quaternion( [msg.pose.orientation.x, msg.pose.orientation.y, msg.pose.orientation.z, msg.pose.orientation.w])[2]) os.system("beep -f 6000 -l 35&") print new_settings self.dynserver.update_configuration(new_settings) def released(self, id): return self.prev_cmd.buttons[id] and not self.curr_cmd.buttons[id] def pressed(self, id): return not self.prev_cmd.buttons[id] and self.curr_cmd.buttons[id] def held(self, id): return self.prev_cmd.buttons[id] and self.curr_cmd.buttons[id] #Needed so the class can change the dynserver values with button presses def set_dynserver(self, server): self.dynserver = server def thurstToRaw(self, joy_thrust): # Deadman button or invalid thrust if not self.curr_cmd.buttons[Button.L1] or joy_thrust>1: return 0 raw_thrust = 0 if joy_thrust > 0.016: raw_thrust = self.min_thrust_raw + joy_thrust*(self.max_thrust_raw-self.min_thrust_raw) if self.slew_rate_raw>0 and self.slew_limit_raw > raw_thrust: if self.old_thurst_raw > self.slew_limit_raw: self.old_thurst_raw = self.slew_limit_raw if raw_thrust < (self.old_thurst_raw - (self.slew_rate_raw/100)): raw_thrust = self.old_thurst_raw - self.slew_rate_raw/100 if joy_thrust < 0 or raw_thrust < self.min_thrust_raw: raw_thrust = 0 self.old_thurst_raw = raw_thrust return raw_thrust def new_joydata(self, joymsg): global Button global Axes #Should run at 100hz. Use launch files roslaunch crazyflie_ros joy.launch if self.prev_cmd == None: self.prev_cmd = joymsg print len(joymsg.axes) #USB mode if len(joymsg.axes) == 27: rospy.logwarn("Experimental: Using USB mode. Axes/Buttons may be different") Button = enum(L1=10,R1=11,Select=0,Start=3,L2=8,R2=9,Up=4,Right=5,Down=6,Left=7,Square=15,Cross=14,Triangle=12,Circle=13) Axes = enum(SLL=0,SLU=1,SRL=2,SRU=3,Up=4+4,Right=5+4,Down=6+4,Left=7+4,L2=4+8,R2=4+9,L1=4+10,R1=4+11,Triangle=4+12,Circle=4+13,Cross=4+14,Square=4+15,AccL=16,AccF=17,AccU=18,GyroY=19) self.USB = True return # bp = [i for i in range(len(joymsg.buttons)) if joymsg.buttons[i]] # ap = [(i,round(joymsg.axes[i],2)) for i in range(len(joymsg.axes)) if abs(joymsg.axes[i])>0.51 ] # if len(bp)>0: # print "Buttons:", bp # if len(ap)>0: # print "Axes :", ap if self.USB: # USB buttons go from 1 to -1, Bluetooth go from 0 to -1, so normalise joymsg.axes =np.array(joymsg.axes, dtype=np.float32) joymsg.axes[8:19] -=1 joymsg.axes[8:19] /=2 # Strange bug: left button as axis doesnt work. So use button instead joymsg.axes[11] = -joymsg.buttons[Button.Left]/2. self.curr_cmd = joymsg hover = False x = 0 y = 0 z = 0 r = 0 r2 = 0 # Get stick positions [-1 1] if self.curr_cmd.buttons[Button.L1]: # Deadman pressed (=allow flight) x = self.joy_scale[0] * self.curr_cmd.axes[Axes.SLL] # Roll y = self.joy_scale[1] * self.curr_cmd.axes[Axes.SLU] # Pitch r = self.joy_scale[2] * self.curr_cmd.axes[Axes.SRL] # Yaw z = self.joy_scale[3] * self.curr_cmd.axes[Axes.SRU] # Thrust r2 = self.joy_scale[4] * (self.curr_cmd.axes[Axes.L2] - self.curr_cmd.axes[Axes.R2]) hover = self.curr_cmd.axes[Axes.L1]<-0.75 roll = x * self.max_angle pitch = y * self.max_angle yaw = 0 #TODO use dyn reconf to decide which to use if r2!=0: yaw = r2 * self.max_yawangle else: if self.yaw_joy: # Deadzone if r < -0.2 or r > 0.2: if r < 0: yaw = (r + 0.2) * self.max_yawangle * 1.25 else: yaw = (r - 0.2) * self.max_yawangle * 1.25 if hover: thrust = int(round(deadband(z,0.2)*32767 + 32767)) #Convert to uint16 else: thrust = self.thurstToRaw(z) trimmed_roll = roll + self.trim_roll trimmed_pitch = pitch + self.trim_pitch # Control trim manually new_settings = {} if joymsg.header.seq%10 == 0: if self.curr_cmd.buttons[Button.Left]: new_settings["trim_roll"] = max(self.trim_roll + self.curr_cmd.axes[Axes.Left], -20) if self.curr_cmd.buttons[Button.Right]: new_settings["trim_roll"] = min(self.trim_roll - self.curr_cmd.axes[Axes.Right], 20) if self.curr_cmd.buttons[Button.Down]: new_settings["trim_pitch"] = max(self.trim_pitch + self.curr_cmd.axes[Axes.Down], -20) if self.curr_cmd.buttons[Button.Up]: new_settings["trim_pitch"] = min(self.trim_pitch - self.curr_cmd.axes[Axes.Up], 20) msg = CFJoyMSG() msg.header.stamp = rospy.Time.now() msg.roll = trimmed_roll msg.pitch = trimmed_pitch msg.yaw = yaw msg.thrust = thrust msg.hover = hover #msg.calib = self.pressed(Button.Triangle) # TODO: rotation is wrong! Cannot simply decompose x,y into r,p. # TODO: roll, pitch should be dependent from the distance and the angle. if self.PIDControl: # and hover: try: rtime = rospy.Time.now() # Update goal position if hover and joymsg.header.seq%10 == 0: if self.PIDActive[0]: if abs(x)>0.016: new_settings["x"] = self.goal[0]-roll/70. if abs(y)>0.016: new_settings["y"] = self.goal[1]-pitch/70. if self.PIDActive[1]: if abs(z)>0.016: new_settings["z"] = self.goal[2]+z/5 if self.PIDActive[2]: if abs(yaw)>0: new_settings["rz"] = self.goal[3]-yaw/self.max_yawangle*20 if new_settings.has_key("x") or new_settings.has_key("y") or new_settings.has_key("z") or new_settings.has_key("rz"): os.system("beep -f 6000 -l 15&") # Get error from current position to goal if possible [px,py,alt,rz] = self.getErrorToGoal() # Starting control mode if hover and not self.prev_msg.hover: # Starting Goal should be from current position if self.PIDSetCurrentAuto: new_settings["SetCurrent"] = True # Reset PID controllers for p in range(4): self.PID[p].reset(rtime) # Set yaw difference as I part #self.PID[3].error_sum = 0 if hover: msg.hover = False #TODO should be an option pidmsg = PidMSG() pidmsg.header.stamp = rtime # Get PID control #XY control depends on the asin of the distance #cr, crp, cri, crd = self.PID[0].get_command(-py,rtime,f=lambda d: degrees(asin(d))/90.*self.max_angle) #cp, cpp, cpi, cpd = self.PID[1].get_command(px,rtime,f=lambda d: degrees(asin(d))/90.*self.max_angle) #cr, crp, cri, crd = self.PID[0].get_command(-py,rtime) #cp, cpp, cpi, cpd = self.PID[1].get_command(px,rtime) # Distance to angle (X direction = pitch, Y direction = roll) cr, crp, cri, crd = self.PID[0].get_command(-py,rtime,f=self.distFunc ) cp, cpp, cpi, cpd = self.PID[1].get_command(px,rtime,f=self.distFunc ) # THRUST ct, ctp, cti, ctd = self.PID[2].get_command(alt,rtime) # YAW VEL cy, cyp, cyi, cyd = self.PID[3].get_command(-rz,rtime)#,d=rz,i=copysign(1,-rz)) #TODO: normalise rotation # ROll/Pitch if self.PIDActive[0]: msg.roll = cr + self.trim_roll msg.pitch = cp + self.trim_pitch pidmsg.ypid = [cr, crp, cri, -crd, -py] pidmsg.xpid = [cp, cpp, cpi, -cpd, px] # THRUST if self.PIDActive[1]: # Add base thrust ct += self.thrust[2] # Add roll compensation # TODO: done on the flie itself now @ 250hz # Bound ct = max(self.thrust[0],ct) ct = min(self.thrust[1],ct) # ct in thrust percent msg.thrust = percentageToThrust(ct) pidmsg.zpid= [ct, ctp, cti, ctd, alt] # YAW if self.PIDActive[2]: msg.yaw = cy pidmsg.rzpid= [cy, cyp, cyi, cyd, -rz] self.pub_PID.publish(pidmsg) if self.pressed(Button.Cross): new_settings["SetCurrent"] = True q = tf.transformations.quaternion_from_euler(radians(msg.roll-self.trim_roll), radians(msg.pitch - self.trim_pitch), radians(-msg.yaw)) self.pub_tf.sendTransform((0,0,0), q, rospy.Time.now(), "/cmd", "/cf_gt2d") except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException): if time.time() - self.warntime>2: rospy.logwarn('Could not look up cf_gt2d -> goal') self.warntime = time.time() global tid if self.pressed(Button.Circle): tid = (tid +1)%len(trajectory) new_settings["x"], new_settings["y"], new_settings["z"], new_settings["rz"] = trajectory[tid] # previous if self.pressed(Button.Triangle): tid = (tid -1)%len(trajectory) new_settings["x"], new_settings["y"], new_settings["z"], new_settings["rz"] = trajectory[tid] # Set trim to current input if self.released(Button.R1): new_settings["trim_roll"] = min(20, max(msg.roll, -20)) new_settings["trim_pitch"] = min(20, max(msg.pitch, -20)) rospy.loginfo("Trim updated Roll/Pitch: %r/%r", round(new_settings["trim_roll"],2), round(new_settings["trim_pitch"],2)) # Reset Trim if self.released(Button.Square): new_settings["trim_roll"] = 0.0 new_settings["trim_pitch"] = 0.0 rospy.loginfo("Pitch reset to 0/0") if new_settings != {} and self.dynserver is not None: if self.USB: new_settings = numpy2python(new_settings) self.dynserver.update_configuration(new_settings) # Cache prev joy command self.pub_cfJoy.publish(msg) self.prev_cmd = self.curr_cmd self.prev_msg = msg self.prev_msg.hover = hover def lookupTransformInWorld(self, frame='/cf_gt', forceRealtime = False, poseNoiseMeters=0.0 ): now = rospy.Time(0) if self.PIDDelay > 0.00001 and not forceRealtime: #rospy.logwarn('Delay in TF Pose: %5.3s', self.PIDDelay) now = rospy.Time.now()-rospy.Duration(self.PIDDelay) (trans,rot) = self.sub_tf.lookupTransform('/world', frame, now) if poseNoiseMeters>0: trans = [trans[0]+random.normalvariate(0,poseNoiseMeters), trans[1]+random.normalvariate(0,poseNoiseMeters), trans[2]+random.normalvariate(0,poseNoiseMeters)] return (trans,rot) def getErrorToGoal(self): # Look up flie position, convert to 2d, publish, return error if self.wandControl: #update goal using wand try: (t,r) = self.lookupTransformInWorld(frame='/wand', forceRealtime=True) e = tf.transformations.euler_from_quaternion(r) self.goal= [t[0], t[1], t[2], degrees(e[2]-e[0])] except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException): rospy.loginfo('Could not find wand') # Publish GOAL from world frame q = tf.transformations.quaternion_from_euler(0, 0, radians(self.goal[3])) self.pub_tf.sendTransform((self.goal[0],self.goal[1],self.goal[2]), q, rospy.Time.now(), "/goal", "/world") # Look up 6D flie pose, publish 3d+yaw pose (trans,rot) = self.lookupTransformInWorld(frame='/cf_gt', poseNoiseMeters=0.0) euler = tf.transformations.euler_from_quaternion(rot) q = tf.transformations.quaternion_from_euler(0, 0, euler[2]) self.pub_tf.sendTransform(trans, q, rospy.Time.now(), "/cf_gt2d", "/world") # Look up error goal->cf (trans,rot) = self.sub_tf.lookupTransform('/cf_gt2d', '/goal', rospy.Time(0)) #self.pub_tf.sendTransform(trans, rot, rospy.Time.now(), "/goalCF", "/cf_gt2d") euler = tf.transformations.euler_from_quaternion(rot) return trans[0], trans[1], trans[2], degrees(euler[2]) def setGoalFromCurrent(self,config={}): try: (trans,rot) = self.lookupTransformInWorld(frame='/cf_gt', forceRealtime=True) euler = tf.transformations.euler_from_quaternion(rot) config['x'],config['y'],config['z'],config['rz']= trans[0], trans[1], trans[2], degrees(euler[2]) rospy.loginfo('Updated goal state: [%.2f %.2f %.2f %.1f]', trans[0], trans[1], trans[2], degrees(euler[2])) self.goal = [config['x'],config['y'],config['z'],config['rz']] except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException): rospy.logwarn('Could not look up transform world -> cf_gt') return config def reconfigure(self, config, level): # Manual control stuff self.trim_roll = config["trim_roll"] self.trim_pitch = config["trim_pitch"] self.max_angle = config["max_angle"] self.max_yawangle = config["max_yawangle"] self.max_thrust = config["max_thrust"] self.min_thrust = config["min_thrust"] self.slew_limit = config["slew_limit"] self.slew_rate = config["slew_rate"] self.max_thrust_raw = percentageToThrust(self.max_thrust) self.min_thrust_raw = percentageToThrust(self.min_thrust) self.slew_limit_raw = percentageToThrust(self.slew_limit) self.slew_rate_raw = percentageToThrust(self.slew_rate) self.yaw_joy = config["yaw_joy"] #PID control stuff if config["PIDPreset"]>0: if config["PIDPreset"]==1: # Aggressive config["DistFunc"] = 1 # ATAN config["Pxy"] = 1.0 config["Ixy"] = 0.0 config["Dxy"] = 0.2 config["Pyaw"] = 8 config["Iyaw"] = 0.0 config["Dyaw"] = 0.0 config["Pz"] = 20 config["Iz"] = 5 config["Dz"] = 8 config["RP_maxAngle"] = 35 config["Y_maxVel"] = 250 config["z_minThrust"] = 50 config["z_maxThrust"] = 100 config["z_baseThrust"] = 73 config["Response"] = 0.3 if config["PIDPreset"]==2: # Passive config["DistFunc"] = 2 # ASIN config["Pxy"] = 2.0 config["Ixy"] = 0.0 config["Dxy"] = 0.4 config["Pyaw"] = 2.0 config["Iyaw"] = 0.0 config["Dyaw"] = 0.0 config["Pz"] = 20 config["Iz"] = 5 config["Dz"] = 8 config["RP_maxAngle"] = 7 config["Y_maxVel"] = 80 config["z_minThrust"] = 50 config["z_maxThrust"] = 90 config["z_baseThrust"] = 73 config["Response"] = 0.175 if config["PIDPreset"]==3: # Linear config["DistFunc"] = 0 config["Pxy"] = 17 config["Ixy"] = 0.0 config["Dxy"] = 5 config["Pyaw"] = 3.3 config["Iyaw"] = 0.0 config["Dyaw"] = 0.0 config["Pz"] = 20 config["Iz"] = 5 config["Dz"] = 8 config["RP_maxAngle"] = 25 config["Y_maxVel"] = 300 config["z_minThrust"] = 50 config["z_maxThrust"] = 90 config["z_baseThrust"] = 73 config["Response"] = 1 rospy.loginfo("Preset Loaded") config["PIDPreset"] = 0 # Settings self.PIDDelay = config["Delay"] self.PIDSource = config["Source"] self.PIDControl = config["Control"] self.max_anglePID = config['RP_maxAngle'] # Gains self.PID[0].set_gains(config["Pxy"], config["Ixy"], config["Dxy"] ) self.PID[1].set_gains(config["Pxy"], config["Ixy"], config["Dxy"] ) self.PID[2].set_gains(config["Pz"], config["Iz"], config["Dz"] ) self.PID[3].set_gains(config["Pyaw"], config["Iyaw"], config["Dyaw"] ) # Goal self.wandControl = config['WandControl'] if config['SetCurrent']: config['SetCurrent'] = False config = self.setGoalFromCurrent(config) if config['Set'] or config['LiveUpdate']: config['Set'] = False if not self.wandControl: self.goal = [config['x'],config['y'],config['z'],config['rz']] # Limits #self.limits = (config['RP_maxAngle'], config['Y_maxVel'], config['z_maxAcc']) self.PID[0].set_limits(config['RP_maxAngle']) self.PID[1].set_limits(config['RP_maxAngle']) self.PID[2].set_limits(100) self.PID[3].set_limits(config['Y_maxVel']) self.thrust = (config['z_minThrust'],config['z_maxThrust'], config['z_baseThrust'] ) # On/OFF self.PIDActive = (config['xyControl'],config['thrustControl'],config['yawControl']) self.PIDSetCurrentAuto = config['SetCurrentAuto'] self.distFunc = [lambda x: fLIN(x,config["Response"]), lambda x: fATAN(x,config["Response"]), lambda x: fASIN(x,config["Response"])][config['DistFunc']] return config def run(args=None): parser = OptionParser(description='Crazyflie ROS Driver') # parser.add_option('--uri', '-u', # dest='uri', # default='radio://0/4', # help='URI to connect to') (options, leftargs) = parser.parse_args(args=args) #Load some DB depending on options rospy.init_node('CrazyFlieJoystickDriver') joydriver = JoyController(options) #START NODE try: rospy.loginfo("Starting CrazyFlie joystick driver") rospy.spin() except KeyboardInterrupt: rospy.loginfo('...exiting due to keyboard interrupt') if __name__ == "__main__": run(sys.argv)