cs_interpolator.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 
16 from scipy.interpolate import splrep, splev
17 import numpy as np
18 from copy import deepcopy
19 from visualization_msgs.msg import MarkerArray
20 from uuv_waypoints import Waypoint, WaypointSet
21 from tf_quaternion.transformations import quaternion_multiply, \
22  quaternion_about_axis, quaternion_conjugate, \
23  quaternion_from_matrix, euler_from_matrix
24 
25 from ..trajectory_point import TrajectoryPoint
26 from .line_segment import LineSegment
27 from .bezier_curve import BezierCurve
28 from .path_generator import PathGenerator
29 
30 
31 class CSInterpolator(PathGenerator):
32  """Interpolator that will generate [cubic Bezier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve)
33  segments for a set of waypoints. The full algorithm can
34  be seen in `Biagiotti and Melchiorri, 2008`.
35 
36  !!! note
37 
38  Biagiotti, Luigi, and Claudio Melchiorri. Trajectory planning for
39  automatic machines and robots. Springer Science & Business Media, 2008.
40  """
41  LABEL = 'cubic'
42 
43  def __init__(self):
44  super(CSInterpolator, self).__init__(self)
45 
46  # Set of interpolation functions for each degree of freedom
47  # The heading function interpolates the given heading offset and its
48  # value is added to the heading computed from the trajectory
49  self._interp_fcns = dict(pos=None,
50  heading=None)
51  self._heading_spline = None
52 
53  def init_interpolator(self):
54  """Initialize the interpolator. To have the path segments generated,
55  `init_waypoints()` must be called beforehand by providing a set of
56  waypoints as `uuv_waypoints.WaypointSet` type.
57 
58  > *Returns*
59 
60  `True` if the path segments were successfully generated.
61  """
62  if self._waypoints is None:
63  return False
64 
65  self._markers_msg = MarkerArray()
66  self._marker_id = 0
67 
68  self._interp_fcns['pos'] = list()
69  self._segment_to_wp_map = [0]
70  if self._waypoints.num_waypoints == 2:
71  self._interp_fcns['pos'].append(
72  LineSegment(self._waypoints.get_waypoint(0).pos,
73  self._waypoints.get_waypoint(1).pos))
74  self._segment_to_wp_map.append(1)
75  elif self._waypoints.num_waypoints > 2:
76  self._interp_fcns['pos'], tangents = BezierCurve.generate_cubic_curve(
77  [self._waypoints.get_waypoint(i).pos for i in range(self._waypoints.num_waypoints)])
78  else:
79  return False
80 
81  # Reparametrizing the curves
82  lengths = [seg.get_length() for seg in self._interp_fcns['pos']]
83  lengths = [0] + lengths
84  self._s = np.cumsum(lengths) / np.sum(lengths)
85  mean_vel = np.mean(
86  [self._waypoints.get_waypoint(k).max_forward_speed for k in range(self._waypoints.num_waypoints)])
87  if self._duration is None:
88  self._duration = np.sum(lengths) / mean_vel
89  if self._start_time is None:
90  self._start_time = 0.0
91 
92  if self._waypoints.num_waypoints == 2:
93  head_offset_line = deepcopy(self._waypoints.get_waypoint(1).heading_offset)
94  self._interp_fcns['heading'] = lambda x: head_offset_line
95  else:
96  # Set a simple spline to interpolate heading offset, if existent
97  heading = [self._waypoints.get_waypoint(i).heading_offset for i in range(self._waypoints.num_waypoints)]
98  self._heading_spline = splrep(self._s, heading, k=3, per=False)
99  self._interp_fcns['heading'] = lambda x: splev(x, self._heading_spline)
100  return True
101 
102  return True
103 
104  def set_parameters(self, params):
105  """Not implemented for this interpolator."""
106  return True
107 
108  def get_samples(self, max_time, step=0.001):
109  """Sample the full path for position and quaternion vectors.
110  `step` is represented in the path's parametric space.
111 
112  > *Input arguments*
113 
114  * `step` (*type:* `float`, *default:* `0.001`): Parameter description
115 
116  > *Returns*
117 
118  List of `uuv_trajectory_generator.TrajectoryPoint`.
119  """
120  if self._waypoints is None:
121  return None
122  if self._interp_fcns['pos'] is None:
123  return None
124  s = np.arange(0, 1 + step, step)
125 
126  pnts = list()
127  for i in s:
128  pnt = TrajectoryPoint()
129  pnt.pos = self.generate_pos(i).tolist()
130  pnt.t = 0.0
131  pnts.append(pnt)
132  return pnts
133 
134  def generate_pos(self, s):
135  """Generate a position vector for the path sampled point
136  interpolated on the position related to `s`, `s` being
137  represented in the curve's parametric space.
138 
139  > *Input arguments*
140 
141  * `s` (*type:* `float`): Curve's parametric input expressed in the
142  interval of [0, 1]
143 
144  > *Returns*
145 
146  3D position vector as a `numpy.array`.
147  """
148  if self._interp_fcns['pos'] is None:
149  return None
150  idx = self.get_segment_idx(s)
151  if idx == 0:
152  u_k = 0
153  pos = self._interp_fcns['pos'][idx].interpolate(u_k)
154  else:
155  u_k = (s - self._s[idx - 1]) / (self._s[idx] - self._s[idx - 1])
156  pos = self._interp_fcns['pos'][idx - 1].interpolate(u_k)
157  return pos
158 
159  def generate_pnt(self, s, t, *args):
160  """Compute a point that belongs to the path on the
161  interpolated space related to `s`, `s` being represented
162  in the curve's parametric space.
163 
164  > *Input arguments*
165 
166  * `s` (*type:* `float`): Curve's parametric input expressed in the
167  interval of [0, 1]
168  * `t` (*type:* `float`): Trajectory point's timestamp
169 
170  > *Returns*
171 
172  `uuv_trajectory_generator.TrajectoryPoint` including position
173  and quaternion vectors.
174  """
175  pnt = TrajectoryPoint()
176  # Trajectory time stamp
177  pnt.t = t
178  # Set position vector
179  pnt.pos = self.generate_pos(s).tolist()
180  # Set rotation quaternion
181  pnt.rotq = self.generate_quat(s)
182  return pnt
183 
184  def generate_quat(self, s):
185  """Compute the quaternion of the path reference for a interpolated
186  point related to `s`, `s` being represented in the curve's parametric
187  space.
188  The quaternion is computed assuming the heading follows the direction
189  of the path towards the target. Roll and pitch can also be computed
190  in case the `full_dof` is set to `True`.
191 
192  > *Input arguments*
193 
194  * `s` (*type:* `float`): Curve's parametric input expressed in the
195  interval of [0, 1]
196 
197  > *Returns*
198 
199  Rotation quaternion as a `numpy.array` as `(x, y, z, w)`
200  """
201  s = max(0, s)
202  s = min(s, 1)
203 
204  if s == 0:
205  self._last_rot = deepcopy(self._init_rot)
206  return self._init_rot
207 
208  last_s = max(0, s - self._s_step)
209 
210  this_pos = self.generate_pos(s)
211  last_pos = self.generate_pos(last_s)
212 
213  dx = this_pos[0] - last_pos[0]
214  dy = this_pos[1] - last_pos[1]
215  dz = this_pos[2] - last_pos[2]
216 
217  rotq = self._compute_rot_quat(dx, dy, dz)
218  self._last_rot = rotq
219  # Calculating the step for the heading offset
220  q_step = quaternion_about_axis(
221  self._interp_fcns['heading'](s),
222  np.array([0, 0, 1]))
223  # Adding the heading offset to the rotation quaternion
224  rotq = quaternion_multiply(rotq, q_step)
225  return rotq


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