vehicle.py
Go to the documentation of this file.
1 # Copyright (c) 2016-2019 The UUV Simulator Authors.
2 # All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 from __future__ import print_function
16 import rospy
17 import numpy as np
18 from nav_msgs.msg import Odometry
19 from copy import deepcopy
20 from rospy.numpy_msg import numpy_msg
21 from tf_quaternion.transformations import quaternion_from_euler, euler_from_quaternion, \
22  quaternion_matrix, rotation_matrix, is_same_transform
23 from ._log import get_logger
24 try:
25  import casadi
26  casadi_exists = True
27 except ImportError:
28  casadi_exists = False
29 
30 
32  """Return a cross product operator for the given vector."""
33  S = np.array([[0, -x[2], x[1]],
34  [x[2], 0, -x[0]],
35  [-x[1], x[0], 0]])
36  return S
37 
38 
39 class Vehicle(object):
40  """Vehicle interface to be used by model-based controllers. It receives the
41  parameters necessary to compute the vehicle's motion according to Fossen's.
42  """
43 
44  _INSTANCE = None
45 
46  def __init__(self, inertial_frame_id='world'):
47  """Class constructor."""
48  assert inertial_frame_id in ['world', 'world_ned']
49  # Reading current namespace
50  self._namespace = rospy.get_namespace()
51 
52  self._inertial_frame_id = inertial_frame_id
53  self._body_frame_id = None
55 
56  if self._inertial_frame_id == 'world':
57  self._body_frame_id = 'base_link'
58  else:
59  self._body_frame_id = 'base_link_ned'
60 
61  try:
62  import tf2_ros
63 
64  tf_buffer = tf2_ros.Buffer()
65  listener = tf2_ros.TransformListener(tf_buffer)
66 
67  tf_trans_ned_to_enu = tf_buffer.lookup_transform(
68  'world', 'world_ned', rospy.Time(),
69  rospy.Duration(10))
70 
71  self.q_ned_to_enu = np.array(
72  [tf_trans_ned_to_enu.transform.rotation.x,
73  tf_trans_ned_to_enu.transform.rotation.y,
74  tf_trans_ned_to_enu.transform.rotation.z,
75  tf_trans_ned_to_enu.transform.rotation.w])
76  except Exception as ex:
77  self._logger.warning(
78  'Error while requesting ENU to NED transform'
79  ', message={}'.format(ex))
80  self.q_ned_to_enu = quaternion_from_euler(2 * np.pi, 0, np.pi)
81 
82  self.transform_ned_to_enu = quaternion_matrix(
83  self.q_ned_to_enu)[0:3, 0:3]
84 
85  if self.transform_ned_to_enu is not None:
86  self._logger.info('Transform world_ned (NED) to world (ENU)=\n' +
87  str(self.transform_ned_to_enu))
88 
89  self._mass = 0
90  if rospy.has_param('~mass'):
91  self._mass = rospy.get_param('~mass')
92  if self._mass <= 0:
93  raise rospy.ROSException('Mass has to be positive')
94 
95  self._inertial = dict(ixx=0, iyy=0, izz=0, ixy=0, ixz=0, iyz=0)
96  if rospy.has_param('~inertial'):
97  inertial = rospy.get_param('~inertial')
98  for key in self._inertial:
99  if key not in inertial:
100  raise rospy.ROSException('Invalid moments of inertia')
101  self._inertial = inertial
102 
103  self._cog = [0, 0, 0]
104  if rospy.has_param('~cog'):
105  self._cog = rospy.get_param('~cog')
106  if len(self._cog) != 3:
107  raise rospy.ROSException('Invalid center of gravity vector')
108 
109  self._cob = [0, 0, 0]
110  if rospy.has_param('~cog'):
111  self._cob = rospy.get_param('~cob')
112  if len(self._cob) != 3:
113  raise rospy.ROSException('Invalid center of buoyancy vector')
114 
115  self._body_frame = 'base_link'
116  if rospy.has_param('~base_link'):
117  self._body_frame = rospy.get_param('~base_link')
118 
119  self._volume = 0.0
120  if rospy.has_param('~volume'):
121  self._volume = rospy.get_param('~volume')
122  if self._volume <= 0:
123  raise rospy.ROSException('Invalid volume')
124 
125  # Fluid density
126  self._density = 1028.0
127  if rospy.has_param('~density'):
128  self._density = rospy.get_param('~density')
129  if self._density <= 0:
130  raise rospy.ROSException('Invalid fluid density')
131 
132  # Bounding box
133  self._height = 0.0
134  self._length = 0.0
135  self._width = 0.0
136  if rospy.has_param('~height'):
137  self._height = rospy.get_param('~height')
138  if self._height <= 0:
139  raise rospy.ROSException('Invalid height')
140 
141  if rospy.has_param('~length'):
142  self._length = rospy.get_param('~length')
143  if self._length <= 0:
144  raise rospy.ROSException('Invalid length')
145 
146  if rospy.has_param('~width'):
147  self._width = rospy.get_param('~width')
148  if self._width <= 0:
149  raise rospy.ROSException('Invalid width')
150 
151 
152  # Calculating the rigid-body mass matrix
153  self._M = np.zeros(shape=(6, 6), dtype=float)
154  self._M[0:3, 0:3] = self._mass * np.eye(3)
155  self._M[0:3, 3:6] = - self._mass * \
157  self._M[3:6, 0:3] = self._mass * \
159  self._M[3:6, 3:6] = self._calc_inertial_tensor()
160 
161  # Loading the added-mass matrix
162  self._Ma = np.zeros((6, 6))
163  if rospy.has_param('~Ma'):
164  self._Ma = np.array(rospy.get_param('~Ma'))
165  if self._Ma.shape != (6, 6):
166  raise rospy.ROSException('Invalid added mass matrix')
167 
168  # Sum rigid-body and added-mass matrices
169  self._Mtotal = np.zeros(shape=(6, 6))
170  self._calc_mass_matrix()
171 
172  # Acceleration of gravity
173  self._gravity = 9.81
174 
175  # Initialize the Coriolis and centripetal matrix
176  self._C = np.zeros((6, 6))
177 
178  # Vector of restoring forces
179  self._g = np.zeros(6)
180 
181  # Loading the linear damping coefficients
182  self._linear_damping = np.zeros(shape=(6, 6))
183  if rospy.has_param('~linear_damping'):
184  self._linear_damping = np.array(rospy.get_param('~linear_damping'))
185  if self._linear_damping.shape == (6,):
186  self._linear_damping = np.diag(self._linear_damping)
187  if self._linear_damping.shape != (6, 6):
188  raise rospy.ROSException('Linear damping must be given as a 6x6 matrix or the diagonal coefficients')
189 
190  # Loading the nonlinear damping coefficients
191  self._quad_damping = np.zeros(shape=(6,))
192  if rospy.has_param('~quad_damping'):
193  self._quad_damping = np.array(rospy.get_param('~quad_damping'))
194  if self._quad_damping.shape != (6,):
195  raise rospy.ROSException('Quadratic damping must be given defined with 6 coefficients')
196 
197  # Loading the linear damping coefficients proportional to the forward speed
198  self._linear_damping_forward_speed = np.zeros(shape=(6, 6))
199  if rospy.has_param('~linear_damping_forward_speed'):
200  self._linear_damping_forward_speed = np.array(
201  rospy.get_param('~linear_damping_forward_speed'))
202  if self._linear_damping_forward_speed.shape == (6,):
204  if self._linear_damping_forward_speed.shape != (6, 6):
205  raise rospy.ROSException(
206  'Linear damping proportional to the '
207  'forward speed must be given as a 6x6 '
208  'matrix or the diagonal coefficients')
209 
210  # Initialize damping matrix
211  self._D = np.zeros((6, 6))
212 
213  # Vehicle states
214  self._pose = dict(pos=np.zeros(3),
215  rot=quaternion_from_euler(0, 0, 0))
216  # Velocity in the body frame
217  self._vel = np.zeros(6)
218  # Acceleration in the body frame
219  self._acc = np.zeros(6)
220  # Generalized forces
221  self._gen_forces = np.zeros(6)
222 
223  @staticmethod
224  def q_to_matrix(q):
225  """Convert quaternion into orthogonal rotation matrix.
226 
227  > *Input arguments*
228 
229  * `q` (*type:* `numpy.array`): Quaternion vector as
230  `(qx, qy, qz, qw)`
231 
232  > *Returns*
233 
234  `numpy.array`: Rotation matrix
235  """
236  e1 = q[0]
237  e2 = q[1]
238  e3 = q[2]
239  eta = q[3]
240  R = np.array([[1 - 2 * (e2**2 + e3**2),
241  2 * (e1 * e2 - e3 * eta),
242  2 * (e1 * e3 + e2 * eta)],
243  [2 * (e1 * e2 + e3 * eta),
244  1 - 2 * (e1**2 + e3**2),
245  2 * (e2 * e3 - e1 * eta)],
246  [2 * (e1 * e3 - e2 * eta),
247  2 * (e2 * e3 + e1 * eta),
248  1 - 2 * (e1**2 + e2**2)]])
249  return R
250 
251  @property
252  def namespace(self):
253  """`str`: Return robot namespace."""
254  return self._namespace
255 
256  @property
257  def body_frame_id(self):
258  """`str`: Body frame ID"""
259  return self._body_frame_id
260 
261  @property
262  def inertial_frame_id(self):
263  """`str`: Inertial frame ID"""
264  return self._inertial_frame_id
265 
266  @property
267  def mass(self):
268  """`float`: Mass in kilograms"""
269  return self._mass
270 
271  @property
272  def volume(self):
273  """`float`: Volume of the vehicle in m^3"""
274  return self._volume
275 
276  @property
277  def gravity(self):
278  """`float`: Magnitude of acceleration of gravity m / s^2"""
279  return self._gravity
280 
281  @property
282  def density(self):
283  """`float`: Fluid density as kg / m^3"""
284  return self._density
285 
286  @property
287  def height(self):
288  """`float`: Height of the vehicle in meters"""
289  return self._height
290 
291  @property
292  def width(self):
293  """`float`: Width of the vehicle in meters"""
294  return self._width
295 
296  @property
297  def length(self):
298  """`float`: Length of the vehicle in meters"""
299  return self._length
300 
301  @property
302  def pos(self):
303  """`numpy.array`: Position of the vehicle in meters."""
304  return deepcopy(self._pose['pos'])
305 
306  @pos.setter
307  def pos(self, position):
308  pos = np.array(position)
309  if pos.size != 3:
310  self._logger.error('Invalid position vector')
311  else:
312  self._pose['pos'] = pos
313 
314  @property
315  def depth(self):
316  """`numpy.array`: Depth of the vehicle in meters."""
317  return deepcopy(np.abs(self._pose['pos'][2]))
318 
319  @property
320  def heading(self):
321  """`float`: Heading of the vehicle in radians."""
322  return deepcopy(self.euler[2])
323 
324  @property
325  def quat(self):
326  """`numpy.array`: Orientation quaternion as `(qx, qy, qz, qw)`."""
327  return deepcopy(self._pose['rot'])
328 
329  @quat.setter
330  def quat(self, q):
331  q_rot = np.array(q)
332  if q_rot.size != 4:
333  self._logger.error('Invalid quaternion')
334  else:
335  self._pose['rot'] = q_rot
336 
337  @property
338  def quat_dot(self):
339  """`numpy.array`: Time derivative of the quaternion vector."""
340  return np.dot(self.TBtoIquat, self.vel[3:6])
341 
342  @property
343  def vel(self):
344  """`numpy.array`: Linear and angular velocity vector."""
345  return deepcopy(self._vel)
346 
347  @vel.setter
348  def vel(self, velocity):
349  """Set the velocity vector in the BODY frame."""
350  v = np.array(velocity)
351  if v.size != 6:
352  self._logger.error('Invalid velocity vector')
353  else:
354  self._vel = v
355 
356  @property
357  def acc(self):
358  """`numpy.array`: Linear and angular acceleration vector."""
359  return deepcopy(self._acc)
360 
361  @property
362  def euler(self):
363  """`list`: Orientation in Euler angles in radians
364  as described in Fossen, 2011.
365  """
366  # Rotation matrix from BODY to INERTIAL
367  rot = self.rotBtoI
368  # Roll
369  roll = np.arctan2(rot[2, 1], rot[2, 2])
370  # Pitch, treating singularity cases
371  den = np.sqrt(1 - rot[2, 1]**2)
372  pitch = - np.arctan(rot[2, 1] / max(0.001, den))
373  # Yaw
374  yaw = np.arctan2(rot[1, 0], rot[0, 0])
375  return roll, pitch, yaw
376 
377  @property
378  def euler_dot(self):
379  """`numpy.array`: Time derivative of the Euler
380  angles in radians.
381  """
382  return np.dot(self.TItoBeuler, self.vel[3:6])
383 
384  @property
385  def restoring_forces(self):
386  """`numpy.array`: Restoring force vector in N."""
387  self._update_restoring()
388  return deepcopy(self._g)
389 
390  @property
391  def Mtotal(self):
392  """`numpy.array`: Combined system inertia
393  and added-mass matrices.
394  """
395  return deepcopy(self._Mtotal)
396 
397  @property
398  def Ctotal(self):
399  """`numpy.array`: Combined Coriolis matrix"""
400  return deepcopy(self._C)
401 
402  @property
403  def Dtotal(self):
404  """`numpy.array`: Linear and non-linear damping matrix"""
405  return deepcopy(self._D)
406 
407  @property
408  def pose_euler(self):
409  """`numpy.array`: Pose as a vector, orientation in Euler angles."""
410  roll, pitch, yaw = self.euler
411  pose = np.zeros(6)
412  pose[0:3] = self.pos
413  pose[3] = roll
414  pose[4] = pitch
415  pose[5] = yaw
416  return pose
417 
418  @property
419  def pose_quat(self):
420  """`numpy.array`: Pose as a vector, orientation as quaternion."""
421  pose = np.zeros(7)
422  pose[0:3] = self.pos
423  pose[3:7] = self.quat
424  return pose
425 
426  @property
427  def rotItoB(self):
428  """`numpy.array`: Rotation matrix from INERTIAL to BODY frame"""
429  return self.rotBtoI.T
430 
431  @property
432  def rotBtoI(self):
433  """`numpy.array`: Rotation from BODY to INERTIAL
434  frame using the zyx convention to retrieve Euler
435  angles from the quaternion vector (Fossen, 2011).
436  """
437  # Using the (x, y, z, w) format to describe quaternions
438  return self.q_to_matrix(self._pose['rot'])
439 
440  @property
441  def TItoBeuler(self):
442  r, p, y = self.euler
443  T = np.array([[1, 0, -np.sin(p)],
444  [0, np.cos(r), np.cos(p) * np.sin(r)],
445  [0, -np.sin(r), np.cos(p) * np.cos(r)]])
446  return T
447 
448  @property
449  def TBtoIeuler(self):
450  r, p, y = self.euler
451  cp = np.cos(p)
452  cp = np.sign(cp) * min(0.001, np.abs(cp))
453  T = 1 / cp * np.array(
454  [[0, np.sin(r) * np.sin(p), np.cos(r) * np.sin(p)],
455  [0, np.cos(r) * np.cos(p), -np.cos(p) * np.sin(r)],
456  [0, np.sin(r), np.cos(r)]])
457  return T
458 
459  @property
460  def TBtoIquat(self):
461  """
462  Return matrix for transformation of BODY-fixed angular velocities in the
463  BODY frame in relation to the INERTIAL frame into quaternion rate.
464  """
465  e1 = self._pose['rot'][0]
466  e2 = self._pose['rot'][1]
467  e3 = self._pose['rot'][2]
468  eta = self._pose['rot'][3]
469  T = 0.5 * np.array(
470  [[-e1, -e2, -e3],
471  [eta, -e3, e2],
472  [e3, eta, -e1],
473  [-e2, e1, eta]]
474  )
475  return T
476 
477  def to_SNAME(self, x):
478  if self._body_frame_id == 'base_link_ned':
479  return x
480  try:
481  if x.shape == (3,):
482  return np.array([x[0], -1 * x[1], -1 * x[2]])
483  elif x.shape == (6,):
484  return np.array([x[0], -1 * x[1], -1 * x[2],
485  x[3], -1 * x[4], -1 * x[5]])
486  except Exception as e:
487  self._logger.error('Invalid input vector, v=' + str(x))
488  self._logger.error('Message=' + str(e))
489  return None
490 
491  def from_SNAME(self, x):
492  if self._body_frame_id == 'base_link_ned':
493  return x
494  return self.to_SNAME(x)
495 
496  def print_info(self):
497  """Print the vehicle's parameters."""
498  print('Namespace: {}'.format(self._namespace))
499  print('Mass: {0:.3f} kg'.format(self._mass))
500  print('System inertia matrix:\n{}'.format(self._M))
501  print('Added-mass:\n{}'.format(self._Ma))
502  print('M:\n{}'.format(self._Mtotal))
503  print('Linear damping: {}'.format(self._linear_damping))
504  print('Quad. damping: {}'.format(self._quad_damping))
505  print('Center of gravity: {}'.format(self._cog))
506  print('Center of buoyancy: {}'.format(self._cob))
507  print('Inertial:\n{}'.format(self._calc_inertial_tensor()))
508 
509  def _calc_mass_matrix(self):
510  self._Mtotal = self._M + self._Ma
511 
512  def _update_coriolis(self, vel=None):
513  if vel is not None:
514  if vel.shape != (6,):
515  raise rospy.ROSException('Velocity vector has the wrong '
516  'dimension')
517  nu = vel
518  else:
519  nu = self.to_SNAME(self._vel)
520 
521  self._C = np.zeros((6, 6))
522 
523  S_12 = - cross_product_operator(
524  np.dot(self._Mtotal[0:3, 0:3], nu[0:3]) +
525  np.dot(self._Mtotal[0:3, 3:6], nu[3:6]))
526  S_22 = - cross_product_operator(
527  np.dot(self._Mtotal[3:6, 0:3], nu[0:3]) +
528  np.dot(self._Mtotal[3:6, 3:6], nu[3:6]))
529 
530  self._C[0:3, 3:6] = S_12
531  self._C[3:6, 0:3] = S_12
532  self._C[3:6, 3:6] = S_22
533 
534  def _update_damping(self, vel=None):
535  if vel is not None:
536  if vel.shape != (6,):
537  raise rospy.ROSException('Velocity vector has the wrong '
538  'dimension')
539  # Assume the input velocity is already given in the SNAME convention
540  nu = vel
541  else:
542  nu = self.to_SNAME(self._vel)
543 
544  self._D = -1 * self._linear_damping - nu[0] * self._linear_damping_forward_speed
545  for i in range(6):
546  self._D[i, i] += -1 * self._quad_damping[i] * np.abs(nu[i])
547 
549  return np.array(
550  [[self._inertial['ixx'], self._inertial['ixy'],
551  self._inertial['ixz']],
552  [self._inertial['ixy'], self._inertial['iyy'],
553  self._inertial['iyz']],
554  [self._inertial['ixz'], self._inertial['iyz'],
555  self._inertial['izz']]])
556 
557  def _update_restoring(self, q=None, use_sname=False):
558  """
559  Update the restoring forces for the current orientation.
560  """
561  if use_sname:
562  Fg = np.array([0, 0, -self._mass * self._gravity])
563  Fb = np.array([0, 0, self._volume * self._gravity * self._density])
564  else:
565  Fg = np.array([0, 0, self._mass * self._gravity])
566  Fb = np.array([0, 0, -self._volume * self._gravity * self._density])
567  if q is not None:
568  rotItoB = self.q_to_matrix(q).T
569  else:
570  rotItoB = self.rotItoB
571  self._g = np.zeros(6)
572 
573  self._g[0:3] = -1 * np.dot(rotItoB, Fg + Fb)
574  self._g[3:6] = -1 * np.dot(rotItoB,
575  np.cross(self._cog, Fg) + np.cross(self._cob, Fb))
576 
577  def set_added_mass(self, Ma):
578  """Set added-mass matrix coefficients."""
579  if Ma.shape != (6, 6):
580  self._logger.error('Added mass matrix must have dimensions 6x6')
581  return False
582  self._Ma = np.array(Ma, copy=True)
583  self._calc_mass_matrix()
584  return True
585 
586  def set_damping_coef(self, linear_damping, quad_damping):
587  """Set linear and quadratic damping coefficients."""
588  if linear_damping.size != 6 or quad_damping.size != 6:
589  self._logger.error('Invalid dimensions for damping coefficient vectors')
590  return False
591  self._linear_damping = np.array(linear_damping, copy=True)
592  self._quad_damping = np.array(quad_damping, copy=True)
593  return True
594 
595  def compute_force(self, acc=None, vel=None, with_restoring=True, use_sname=True):
596  """Return the sum of forces acting on the vehicle.
597 
598  Given acceleration and velocity vectors, this function returns the
599  sum of forces given the rigid-body and hydrodynamic models for the
600  marine vessel.
601  """
602  if acc is not None:
603  if acc.shape != (6,):
604  raise rospy.ROSException('Acceleration vector must have 6 '
605  'elements')
606  # It is assumed the input acceleration is given in the SNAME convention
607  nu_dot = acc
608  else:
609  # Convert the acceleration vector to the SNAME convention since the odometry is usually given using the
610  # ENU convention
611  nu_dot = self.to_SNAME(self._acc)
612 
613  if vel is not None:
614  if vel.shape != (6,):
615  raise rospy.ROSException('Velocity vector must have 6 '
616  'elements')
617  # It is assumed the input velocity is given in the SNAME convention
618  nu = vel
619  else:
620  nu = self.to_SNAME(self._vel)
621 
622  self._update_damping(nu)
623  self._update_coriolis(nu)
624  self._update_restoring(use_sname=True)
625 
626  if with_restoring:
627  g = deepcopy(self._g)
628  else:
629  g = np.zeros(6)
630 
631  f = np.dot(self._Mtotal, nu_dot) + np.dot(self._C, nu) + \
632  np.dot(self._D, nu) + g
633 
634  if not use_sname:
635  f = self.from_SNAME(f)
636 
637  return f
638 
639  def compute_acc(self, gen_forces=None, use_sname=True):
640  """Calculate inverse dynamics to obtain the acceleration vector."""
641  self._gen_forces = np.zeros(shape=(6,))
642  if gen_forces is not None:
643  # It is assumed the generalized forces are given in the SNAME convention
644  self._gen_forces = gen_forces
645  # Check if the mass and inertial parameters were set
646  if self._Mtotal.sum() == 0:
647  self._acc = np.zeros(6)
648  else:
649  nu = self.to_SNAME(self._vel)
650 
651  self._update_damping()
652  self._update_coriolis()
653  self._update_restoring(use_sname=True)
654  # Compute the vehicle's acceleration
655  self._acc = np.linalg.solve(self._Mtotal, self._gen_forces -
656  np.dot(self._C, nu) -
657  np.dot(self._D, nu) -
658  self._g)
659  if not use_sname:
660  self._acc = self.from_SNAME(self._acc)
661 
662  return self._acc
663 
664  def get_jacobian(self):
665  """
666  Return the Jacobian for the current orientation using transformations
667  from BODY to INERTIAL frame.
668  """
669  jac = np.zeros(shape=(6, 6))
670  # Build the Jacobian matrix
671  jac[0:3, 0:3] = self.rotBtoI
672  jac[3:6, 3:6] = self.TBtoIeuler
673  return jac
674 
675  def update_odometry(self, msg):
676  """Odometry topic subscriber callback function."""
677  # The frames of reference delivered by the odometry seems to be as
678  # follows
679  # position -> world frame
680  # orientation -> world frame
681  # linear velocity -> world frame
682  # angular velocity -> world frame
683 
684  if self._inertial_frame_id != msg.header.frame_id:
685  raise rospy.ROSException('The inertial frame ID used by the '
686  'vehicle model does not match the '
687  'odometry frame ID, vehicle=%s, odom=%s' %
688  (self._inertial_frame_id,
689  msg.header.frame_id))
690 
691  # Update the velocity vector
692  # Update the pose in the inertial frame
693  self._pose['pos'] = np.array([msg.pose.pose.position.x,
694  msg.pose.pose.position.y,
695  msg.pose.pose.position.z])
696 
697  # Using the (x, y, z, w) format for quaternions
698  self._pose['rot'] = np.array([msg.pose.pose.orientation.x,
699  msg.pose.pose.orientation.y,
700  msg.pose.pose.orientation.z,
701  msg.pose.pose.orientation.w])
702  # Linear velocity on the INERTIAL frame
703  lin_vel = np.array([msg.twist.twist.linear.x,
704  msg.twist.twist.linear.y,
705  msg.twist.twist.linear.z])
706  # Transform linear velocity to the BODY frame
707  lin_vel = np.dot(self.rotItoB, lin_vel)
708  # Angular velocity in the INERTIAL frame
709  ang_vel = np.array([msg.twist.twist.angular.x,
710  msg.twist.twist.angular.y,
711  msg.twist.twist.angular.z])
712  # Transform angular velocity to BODY frame
713  ang_vel = np.dot(self.rotItoB, ang_vel)
714  # Store velocity vector
715  self._vel = np.hstack((lin_vel, ang_vel))
716 
def _update_restoring(self, q=None, use_sname=False)
Definition: vehicle.py:557
def _update_damping(self, vel=None)
Definition: vehicle.py:534
def __init__(self, inertial_frame_id='world')
Definition: vehicle.py:46
def _update_coriolis(self, vel=None)
Definition: vehicle.py:512
def set_damping_coef(self, linear_damping, quad_damping)
Definition: vehicle.py:586
def compute_force(self, acc=None, vel=None, with_restoring=True, use_sname=True)
Definition: vehicle.py:595
def compute_acc(self, gen_forces=None, use_sname=True)
Definition: vehicle.py:639


uuv_trajectory_control
Author(s):
autogenerated on Thu Jun 18 2020 03:28:42