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


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