dynamics.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 # Basic ROS imports
00004 import roslib 
00005 roslib.load_manifest('underwater_vehicle_dynamics')
00006 import rospy
00007 import PyKDL
00008 import sys
00009 
00010 # import msgs
00011 from std_msgs.msg import Float64MultiArray 
00012 from geometry_msgs.msg import Pose 
00013 from geometry_msgs.msg import Quaternion
00014 from geometry_msgs.msg import WrenchStamped
00015 
00016 #import services
00017 from std_srvs.srv import Empty
00018 
00019 # More imports
00020 from numpy import *
00021 import tf
00022 
00023 class Dynamics :
00024 
00025     
00026     def getConfig(self) :
00027         """ Load parameters from the rosparam server """
00028         self.num_actuators = rospy.get_param(self.vehicle_name+"/num_actuators")
00029         
00030         self.period = rospy.get_param(self.vehicle_name + "/dynamics" + "/period")
00031         self.mass = rospy.get_param(self.vehicle_name + "/dynamics" + "/mass")
00032         self.gravity_center = array(rospy.get_param(self.vehicle_name + "/dynamics" + "/gravity_center"))
00033         self.g = rospy.get_param(self.vehicle_name + "/dynamics" + "/g")
00034         self.radius = rospy.get_param(self.vehicle_name + "/dynamics" + "/radius")
00035         self.ctf = rospy.get_param(self.vehicle_name + "/dynamics" + "/ctf")
00036         self.ctb = rospy.get_param(self.vehicle_name + "/dynamics" + "/ctb")
00037         self.actuators_tau = rospy.get_param(self.vehicle_name + "/dynamics" + "/actuators_tau")
00038         self.actuators_maxsat= rospy.get_param(self.vehicle_name + "/dynamics" + "/actuators_maxsat")
00039         self.actuators_minsat = rospy.get_param(self.vehicle_name + "/dynamics" + "/actuators_minsat")
00040         self.actuators_gain = rospy.get_param(self.vehicle_name + "/dynamics" + "/actuators_gain")
00041         self.dzv = rospy.get_param(self.vehicle_name + "/dynamics" + "/dzv")
00042         self.dv = rospy.get_param(self.vehicle_name + "/dynamics" + "/dv")
00043         self.dh = rospy.get_param(self.vehicle_name + "/dynamics" + "/dh")
00044         self.density = rospy.get_param(self.vehicle_name + "/dynamics" + "/density")
00045         self.tensor = array(rospy.get_param(self.vehicle_name + "/dynamics" + "/tensor"))
00046         self.damping = array(rospy.get_param(self.vehicle_name + "/dynamics" + "/damping"))
00047         self.quadratic_damping = array(rospy.get_param(self.vehicle_name + "/dynamics" + "/quadratic_damping"))
00048       
00049         self.am=rospy.get_param(self.vehicle_name + "/dynamics"+"/allocation_matrix")
00050   
00051         self.p_0 = array(rospy.get_param(self.vehicle_name + "/dynamics" + "/initial_pose"))
00052         self.v_0 = array(rospy.get_param(self.vehicle_name + "/dynamics" + "/initial_velocity"))
00053         self.frame_id = rospy.get_param(self.vehicle_name + "/dynamics" + "/frame_id")
00054         self.external_force_topic = rospy.get_param(self.vehicle_name + "/dynamics" + "/external_force_topic")
00055     
00056 #       Currents data
00057         self.current_mean = array( rospy.get_param("dynamics/current_mean") )
00058         self.current_sigma = array( rospy.get_param("dynamics/current_sigma") )
00059         self.current_min = array( rospy.get_param("dynamics/current_min") )
00060         self.current_max = array( rospy.get_param("dynamics/current_max") )
00061         
00062         self.uwsim_period = rospy.get_param(self.vehicle_name + "/dynamics/uwsim_period")
00063 
00064         
00065     def s(self, x) :
00066         """ Given a 3D vector computes the 3x3 antisymetric matrix """
00067 #        rospy.loginfo("s(): \n %s", x)
00068         ret = array([0.0, -x[2], x[1], x[2], 0.0, -x[0], -x[1], x[0], 0.0 ])
00069         return ret.reshape(3,3)
00070 
00071 
00072     def generalizedForce(self, du):
00073         """ Computes the generalized force as B*u, being B the allocation matrix and u the control input """
00074         ct = zeros(len(du))
00075         i1 = nonzero(du >= 0.0)
00076         i2 = nonzero(du <= 0.0)
00077         ct[i1] = self.ctf
00078         ct[i2] = self.ctb
00079         
00080         #Evaluates allocation matrix loaded as parameter
00081         b=eval(self.am)
00082         b=array(b).reshape(6,size(b)/6)
00083       
00084         # t = generalized force
00085         t = dot(b, du)
00086         t = squeeze(asarray(t)) #Transforms a matrix into an array
00087         return t
00088 
00089 
00090     def coriolisMatrix(self):
00091         s1 = self.s(dot(self.M[0:3,0:3], self.v[0:3]) + dot(self.M[0:3,3:6], self.v[3:6]))
00092         s2 = self.s(dot(self.M[3:6,0:3], self.v[0:3]) + dot(self.M[3:6,3:6], self.v[3:6])) 
00093         c = zeros((6, 6))
00094         c[0:3,3:6] = -s1
00095         c[3:6,0:3] = -s1
00096         c[3:6,3:6] = -s2
00097         return c
00098     
00099     def dumpingMatrix(self):
00100         # lineal hydrodynamic damping coeficients  
00101         Xu = self.damping[0]
00102         Yv = self.damping[1]
00103         Zw = self.damping[2]
00104         Kp = self.damping[3]
00105         Mq = self.damping[4]
00106         Nr = self.damping[5]
00107         
00108         # quadratic hydrodynamic damping coeficients
00109         Xuu = self.quadratic_damping[0]    #[Kg/m]
00110         Yvv = self.quadratic_damping[1]    #[Kg/m]
00111         Zww = self.quadratic_damping[2]    #[Kg/m]
00112         Kpp = self.quadratic_damping[3]    #[Kg*m*m]
00113         Mqq = self.quadratic_damping[4]    #[Kg*m*m]
00114         Nrr = self.quadratic_damping[5]    #[Kg*m*m]
00115     
00116         d = diag([Xu + Xuu*abs(self.v[0]), 
00117                   Yv + Yvv*abs(self.v[1]),
00118                   Zw + Zww*abs(self.v[2]),
00119                   Kp + Kpp*abs(self.v[3]),
00120                   Mq + Mqq*abs(self.v[4]),
00121                   Nr + Nrr*abs(self.v[5])])
00122         return d
00123 
00124     def gravity(self):
00125         """ Computes the gravity and buoyancy forces. Assumes a sphere model for now """
00126         #Weight and Flotability
00127         W = self.mass * self.g # [Kg]
00128         
00129         #If the vehicle moves out of the water the flotability decreases
00130         #FIXME: Assumes water surface at 0.0. Get this value from uwsim.
00131         if self.p[2] < 0.0: 
00132             r = self.radius + self.p[2]
00133             if r < 0.0:
00134                 r = 0.0
00135         else :
00136             r = self.radius
00137 
00138         #TODO: either set as parameter, since different functions may be desired for different vehicles             
00139         #      or define common models and let the user choose one by the name
00140         #      Eventually let this part to bullet inside uwsim (HfFluid)
00141         F = ((4 * math.pi * pow(r,3))/3)*self.density*self.g 
00142   
00143         # gravity center position in the robot fixed frame (x',y',z') [m]
00144         zg = self.gravity_center[2]
00145         
00146         g = array([(W - F) * sin(self.p[4]),
00147                    -(W - F) * cos(self.p[4]) * sin(self.p[3]),
00148                    -(W - F) * cos(self.p[4]) * cos(self.p[3]),
00149                    zg*W*cos(self.p[4])*sin(self.p[3]),
00150                    zg*W*sin(self.p[4]),
00151                    0.0])
00152         
00153         return g
00154         
00155         
00156     def inverseDynamic(self) :
00157         """ Given the setpoint for each thruster, the previous velocity and the 
00158             previous position computes the v_dot """
00159         du = self.thrustersDynamics(self.u)
00160         t = self.generalizedForce(du)
00161         c = self.coriolisMatrix()
00162         d = self.dumpingMatrix()
00163         g = self.gravity()
00164         c_v = dot((c-d), self.v)
00165         v_dot = dot(self.IM, (t-c_v-g+self.collisionForce)) #t-c_v-g+collisionForce
00166         v_dot = squeeze(asarray(v_dot)) #Transforms a matrix into an array
00167         self.collisionForce=[0,0,0,0,0,0]
00168         return v_dot
00169         
00170 #
00171     
00172     def integral(self, x_dot, x, t) :
00173         """ Computes the integral o x dt """
00174         return (x_dot * t) + x
00175     
00176     
00177     def kinematics(self) :
00178         """ Given the current velocity and the previous position computes the p_dot """
00179         roll = self.p[3]
00180         pitch = self.p[4]
00181         yaw = self.p[5]
00182         
00183         rec = [cos(yaw)*cos(pitch), -sin(yaw)*cos(roll)+cos(yaw)*sin(pitch)*sin(roll), sin(yaw)*sin(roll)+cos(yaw)*cos(roll)*sin(pitch),
00184                sin(yaw)*cos(pitch), cos(yaw)*cos(roll)+sin(roll)*sin(pitch)*sin(yaw), -cos(yaw)*sin(roll)+sin(pitch)*sin(yaw)*cos(roll),
00185                -sin(pitch), cos(pitch)*sin(roll), cos(pitch)*cos(roll)]
00186         rec = array(rec).reshape(3,3)
00187         
00188         to = [1.0, sin(roll)*tan(pitch), cos(roll)*tan(pitch),
00189               0.0, cos(roll), -sin(roll),
00190               0.0, sin(roll)/cos(pitch), cos(roll)/cos(pitch)]
00191         to = array(to).reshape(3,3)
00192         
00193         p_dot = zeros(6)
00194         p_dot[0:3] = dot(rec, self.v[0:3])
00195         p_dot[3:6] = dot(to, self.v[3:6])
00196         return p_dot
00197 
00198     def updateThrusters(self, thrusters) :
00199         """Receives the control input, saturates each component to maxsat or minsat, and multiplies each component by the actuator gain"""
00200         #TODO: Check the size of thrusters.data
00201         t = array(thrusters.data)
00202         for i in range(size(t)):
00203            if t[i]>self.actuators_maxsat[i]:
00204                 t[i]=self.actuators_maxsat[i]
00205            elif t[i]<self.actuators_minsat[i]:
00206                 t[i]=self.actuators_minsat[i]
00207         self.u=t
00208         for i in range(size(t)):
00209            self.u[i] = self.u[i]*self.actuators_gain[i]
00210         
00211         
00212     def thrustersDynamics(self, u):
00213         y = zeros(size(u))
00214         for i in range(size(u)):
00215             y[i] = (self.period * u[i] + self.actuators_tau[i] * self.y_1[i]) / (self.period + self.actuators_tau[i])
00216             
00217         self.y_1 = y
00218         return y
00219   
00220     def updateCollision(self, force) :
00221         self.collisionForce=[force.wrench.force.x,force.wrench.force.y,force.wrench.force.z,force.wrench.torque.x,force.wrench.torque.y,force.wrench.torque.z]        
00222     
00223     def pubPose(self, event):
00224         pose = Pose()
00225         
00226         pose.position.x = self.p[0]
00227         pose.position.y = self.p[1]
00228         pose.position.z = self.p[2]
00229         
00230         orientation = tf.transformations.quaternion_from_euler(self.p[3], self.p[4], self.p[5], 'sxyz')
00231         pose.orientation.x = orientation[0]
00232         pose.orientation.y = orientation[1]
00233         pose.orientation.z = orientation[2]
00234         pose.orientation.w = orientation[3]
00235             
00236         self.pub_pose.publish(pose)
00237      
00238         # Broadcast transform
00239         br = tf.TransformBroadcaster()
00240         br.sendTransform((self.p[0], self.p[1], self.p[2]), orientation, 
00241         rospy.Time.now(), "world", str(self.frame_id))
00242     
00243     def computeTf(self, tf):
00244         r = PyKDL.Rotation.RPY(math.radians(tf[3]), math.radians(tf[4]), math.radians(tf[5]))
00245         v = PyKDL.Vector(tf[0], tf[1], tf[2])
00246         frame = PyKDL.Frame(r, v)
00247         return frame
00248 
00249     def reset(self,req):
00250         self.v = self.v_0
00251         self.p = self.p_0
00252         return []
00253     
00254     def __init__(self):
00255         """ Simulates the dynamics of an AUV """
00256 
00257         if len(sys.argv) != 6: 
00258           sys.exit("Usage: "+sys.argv[0]+" <namespace> <input_topic> <output_topic>")
00259  
00260         self.namespace=sys.argv[1]
00261         self.vehicle_name=self.namespace
00262         self.input_topic=sys.argv[2]
00263         self.output_topic=sys.argv[3]
00264 
00265     #  Collision parameters
00266         self.collisionForce = [0,0,0,0,0,0]
00267 
00268     #   Load dynamic parameters
00269         self.getConfig()
00270         #self.altitude = -1.0 
00271         self.y_1 = zeros(5)
00272         
00273     #   Create publisher
00274         self.pub_pose= rospy.Publisher(self.output_topic, Pose)
00275         rospy.init_node("dynamics_"+self.vehicle_name)
00276                 
00277     #   Init pose and velocity and period
00278         self.v = self.v_0
00279         self.p = self.p_0
00280         
00281         # Inertia Tensor. Principal moments of inertia, and products of inertia [kg*m*m]
00282         Ixx = self.tensor[0]
00283         Ixy = self.tensor[1] 
00284         Ixz = self.tensor[2]
00285         Iyx = self.tensor[3]
00286         Iyy = self.tensor[4]
00287         Iyz = self.tensor[5]
00288         Izx = self.tensor[6]
00289         Izy = self.tensor[7]
00290         Izz = self.tensor[8] 
00291         m = self.mass
00292         xg = self.gravity_center[0]
00293         yg = self.gravity_center[1]
00294         zg = self.gravity_center[2]
00295         
00296         Mrb = rospy.get_param(self.vehicle_name + "/dynamics" + "/Mrb")
00297         Mrb = array(Mrb).reshape(6, 6)
00298              
00299         # Inertia matrix of the rigid body
00300         # Added Mass derivative
00301         Ma = rospy.get_param(self.vehicle_name + "/dynamics" + "/Ma")
00302         Ma = array(Ma).reshape(6, 6) 
00303         
00304         self.M = Mrb + Ma    # mass matrix: Mrb + Ma
00305         self.IM = matrix(self.M).I
00306 #        rospy.loginfo("Inverse Mass Matrix: \n%s", str(self.IM))
00307               
00308         #Init currents
00309         random.seed()
00310         self.e_vc = self.current_mean 
00311         #The number of zeros will depend on the number of actuators
00312         self.u = array(zeros(self.num_actuators)) # Initial thrusters setpoint
00313 
00314         #Publish pose to UWSim
00315         rospy.Timer(rospy.Duration(self.uwsim_period), self.pubPose)
00316         
00317     #   Create Subscribers for thrusters and collisions
00318         #TODO: set the topic names as parameters
00319         rospy.Subscriber(self.input_topic, Float64MultiArray, self.updateThrusters)
00320         rospy.Subscriber(self.external_force_topic, WrenchStamped, self.updateCollision)
00321 
00322 
00323         s = rospy.Service('/dynamics/reset',Empty, self.reset)
00324         
00325     def iterate(self):
00326         t1 = rospy.Time.now()
00327 
00328         # Main loop operations
00329         self.v_dot = self.inverseDynamic()
00330         self.v = self.integral(self.v_dot, self.v, self.period)
00331         self.p_dot = self.kinematics()
00332         self.p = self.integral(self.p_dot, self.p, self.period)
00333 
00334         t2 = rospy.Time.now()
00335         p = self.period - (t2-t1).to_sec()
00336         if p < 0.0 : p = 0.0
00337         rospy.sleep(p)
00338         
00339 
00340 if __name__ == '__main__':
00341     try:
00342         dynamics = Dynamics() 
00343         while not rospy.is_shutdown():
00344             dynamics.iterate()
00345 
00346     except rospy.ROSInterruptException: pass
00347     


underwater_vehicle_dynamics
Author(s): Arnau Carrera, Narcis Palomeras, Mario Prats
autogenerated on Fri Aug 28 2015 13:28:52