path_planning/cspace/leader_follower/
mod.rs

1/* Copyright (C) 2020 Dylan Staatz - All Rights Reserved. */
2
3pub mod polar;
4pub mod spherical;
5
6////////////////////////////////////////////////////////////////////////////////
7
8use nalgebra::constraint::{SameNumberOfRows, ShapeConstraint};
9use nalgebra::storage::Storage;
10use nalgebra::{
11  Const, Dim, SVector, Scalar, SimdBool, SimdRealField, SliceStorage, Vector,
12};
13use rand::distributions::{uniform::SampleUniform, Distribution};
14use rand::{thread_rng, SeedableRng};
15use serde::{de::DeserializeOwned, Deserialize, Serialize};
16
17use crate::error::{InvalidParamError, Result};
18use crate::params::FromParams;
19use crate::rng::{LinearCoordinates, RNG};
20use crate::trajectories::{FullTraj, LinearTrajectory};
21use crate::util::{bounds::Bounds, norm::NormCost};
22
23use super::CSpace;
24
25/// Mosly a marker, but implementor are free to override and implement differently
26///
27/// Default implementation has leader in top D spots and follower in bottom D slots
28/// and D*2 = N assertions
29pub trait LeaderFollowerCSpace<X, const D: usize, const N: usize>:
30  CSpace<X, N>
31{
32  /// Get the position of the leader within the dimensional space
33  fn get_leader<S: Storage<X, Const<N>>>(
34    state: &Vector<X, Const<N>, S>,
35  ) -> Vector<
36    X,
37    Const<D>,
38    SliceStorage<'_, X, Const<D>, Const<1_usize>, S::RStride, S::CStride>,
39  > {
40    debug_assert_eq!(D * 2, N);
41    state.fixed_rows::<D>(0)
42  }
43
44  /// Get the position of the follower within the dimensional space
45  fn get_follower<S: Storage<X, Const<N>>>(
46    state: &Vector<X, Const<N>, S>,
47  ) -> Vector<
48    X,
49    Const<D>,
50    SliceStorage<'_, X, Const<D>, Const<1_usize>, S::RStride, S::CStride>,
51  > {
52    debug_assert_eq!(D * 2, N);
53    state.fixed_rows::<D>(D)
54  }
55
56  /// Get the position of the follower within the dimensional space
57  fn get_state<S1, S2>(
58    leader: &Vector<X, Const<D>, S1>,
59    follower: &Vector<X, Const<D>, S2>,
60  ) -> SVector<X, N>
61  where
62    X: Scalar,
63    S1: Storage<X, Const<D>>,
64    S2: Storage<X, Const<D>>,
65  {
66    debug_assert_eq!(D * 2, N);
67    SVector::<X, N>::from_iterator(
68      leader.iter().chain(follower.iter()).cloned(),
69    )
70  }
71}
72
73#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
74#[serde(bound(
75  serialize = "X: Scalar + Serialize",
76  deserialize = "X: Scalar + DeserializeOwned"
77))]
78pub struct LeaderFollowerSpaceParams<X, const N: usize> {
79  pub bounds: Bounds<X, N>,
80  pub norm: NormCost,
81  pub seed: Option<u64>,
82  pub sensor_range: (X, X),
83}
84
85impl<X: Scalar + PartialEq, const N: usize> PartialEq
86  for LeaderFollowerSpaceParams<X, N>
87{
88  fn eq(&self, other: &Self) -> bool {
89    self.bounds == other.bounds
90      && self.norm == other.norm
91      && self.seed == other.seed
92      && self.sensor_range == other.sensor_range
93  }
94}
95
96/// A uniformly sampled Cuboid with normed cost function
97///
98/// N must be D*2
99pub struct LeaderFollowerSpace<X, const D: usize, const N: usize>
100where
101  X: SampleUniform,
102{
103  volume: X,
104  norm: NormCost,
105  rng: RNG,
106  distribution: LinearCoordinates<X, N>,
107  intial_sensor_range: (X, X),
108  sensor_range: (X, X),
109}
110
111impl<X, const D: usize, const N: usize> LeaderFollowerSpace<X, D, N>
112where
113  X: SimdRealField + SampleUniform + Copy,
114{
115  pub fn new(
116    bounds: Bounds<X::Element, N>,
117    norm: NormCost,
118    rng: RNG,
119    sensor_range: (X::Element, X::Element),
120  ) -> Result<Self>
121  where
122    X::Element: Scalar,
123  {
124    debug_assert_eq!(D * 2, N);
125
126    let bounds = Bounds::splat(bounds);
127    let sensor_range = (X::splat(sensor_range.0), X::splat(sensor_range.1));
128
129    if !bounds.is_valid() {
130      Err(InvalidParamError {
131        parameter_name: "bounds",
132        parameter_value: format!("{:?}", bounds),
133      })?;
134    }
135
136    let volume = bounds.volume();
137    let distribution = bounds.into();
138
139    if !sensor_range.0.simd_lt(sensor_range.1).all() {
140      Err(InvalidParamError {
141        parameter_name: "sensor_range",
142        parameter_value: format!("{:?}", sensor_range),
143      })?;
144    }
145
146    Ok(Self {
147      volume,
148      norm,
149      rng,
150      distribution,
151      intial_sensor_range: sensor_range,
152      sensor_range,
153    })
154  }
155
156  pub fn intial_sensor_range(&self) -> (X::Element, X::Element) {
157    (
158      self.intial_sensor_range.0.extract(0),
159      self.intial_sensor_range.1.extract(0),
160    )
161  }
162
163  pub fn get_sensor_range(&self) -> (X::Element, X::Element) {
164    (
165      self.sensor_range.0.extract(0),
166      self.sensor_range.1.extract(0),
167    )
168  }
169
170  pub fn set_sensor_range(
171    &mut self,
172    sensor_range: (X::Element, X::Element),
173  ) -> Option<()> {
174    let sensor_range = (X::splat(sensor_range.0), X::splat(sensor_range.1));
175    if sensor_range.0.simd_lt(sensor_range.1).all() {
176      // Valid
177      self.sensor_range = sensor_range;
178      Some(())
179    } else {
180      // Invalid value
181      None
182    }
183  }
184}
185
186impl<X, const D: usize, const N: usize> LeaderFollowerCSpace<X, D, N>
187  for LeaderFollowerSpace<X, D, N>
188where
189  X: SimdRealField + SampleUniform + Copy,
190  X::Element: Scalar,
191{
192  // Default implementation okay
193}
194
195impl<X, const D: usize, const N: usize> CSpace<X, N>
196  for LeaderFollowerSpace<X, D, N>
197where
198  X: SimdRealField + SampleUniform + Copy,
199  X::Element: Scalar,
200{
201  type Traj = LinearTrajectory<X, NormCost, N>;
202
203  fn volume(&self) -> X {
204    self.volume
205  }
206
207  fn cost<R1, R2, S1, S2>(
208    &self,
209    a: &Vector<X, R1, S1>,
210    b: &Vector<X, R2, S2>,
211  ) -> X
212  where
213    R1: Dim,
214    R2: Dim,
215    S1: Storage<X, R1>,
216    S2: Storage<X, R2>,
217    ShapeConstraint: SameNumberOfRows<R1, R2>,
218  {
219    self.norm.cost(a, b)
220  }
221
222  fn trajectory<S1, S2>(
223    &self,
224    start: Vector<X, Const<N>, S1>,
225    end: Vector<X, Const<N>, S2>,
226  ) -> Option<FullTraj<X, Self::Traj, S1, S2, N>>
227  where
228    X: Scalar,
229    S1: Storage<X, Const<N>>,
230    S2: Storage<X, Const<N>>,
231  {
232    // Assumes that points are only sampled and saturated in this space
233    Some(FullTraj::new(start, end, LinearTrajectory::new(self.norm)))
234  }
235
236  fn is_free<S>(&self, _: &Vector<X, Const<N>, S>) -> bool
237  where
238    S: Storage<X, Const<N>>,
239  {
240    // Assumes that points are only sampled and saturated in this space
241    true
242  }
243
244  fn saturate(&self, a: &mut SVector<X, N>, b: &SVector<X, N>, delta: X) {
245    let delta = delta / (X::one() + X::one());
246
247    // Saturate leader to be delta away
248    let mut leader_a = a.fixed_rows_mut::<D>(0);
249    let leader_b = b.fixed_rows::<D>(0);
250
251    let leader_scale = delta / self.norm.cost(&leader_a, &leader_b);
252
253    leader_a.set_column(0, &(&leader_a - &leader_b));
254    leader_a.set_column(0, &(&leader_a * leader_scale));
255    leader_a.set_column(0, &(&leader_a + &leader_b));
256
257    // Saturate follower to be delta away
258    let mut follower_a = a.fixed_rows_mut::<D>(D);
259    let follower_b = b.fixed_rows::<D>(D);
260
261    let follower_scale = delta / self.norm.cost(&follower_a, &follower_b);
262
263    follower_a.set_column(0, &(&follower_a - &follower_b));
264    follower_a.set_column(0, &(&follower_a * follower_scale));
265    follower_a.set_column(0, &(&follower_a + &follower_b));
266  }
267
268  fn sample(&mut self) -> SVector<X, N> {
269    self.distribution.sample(&mut self.rng)
270  }
271}
272
273impl<X, const D: usize, const N: usize> FromParams
274  for LeaderFollowerSpace<X, D, N>
275where
276  X: SimdRealField + SampleUniform + Copy,
277  X::Element: Scalar,
278{
279  type Params = LeaderFollowerSpaceParams<X::Element, N>;
280  fn from_params(params: Self::Params) -> Result<Self> {
281    let rng = match params.seed {
282      Some(seed) => RNG::seed_from_u64(seed),
283      None => RNG::from_rng(thread_rng())?,
284    };
285
286    LeaderFollowerSpace::new(
287      params.bounds,
288      params.norm,
289      rng,
290      params.sensor_range,
291    )
292  }
293}