use crate::{
domain::{DefineTrait, Key, KeyedCloser, Lift, Lifted, Reversible, StateInto},
error::Anyhow,
graph::{Graph, SharedGraph},
motion::{
r2::{DirectTravelHeuristic, DiscreteSpaceTimeR2, Positioned, StateR2},
se2::*,
SpeedLimiter, TravelTimeCost,
},
templates::{GraphMotion, InformedSearch},
};
const DEFAULT_RES: u32 = 360;
pub type SearchSE2<G> = InformedSearch<
GraphMotion<
DiscreteSpaceTimeSE2<<G as Graph>::Key, DEFAULT_RES>,
SharedGraph<G>,
DifferentialDriveLineFollow,
>,
TravelTimeCost,
Lifted<
DefineTrait<StateSE2<<G as Graph>::Key, DEFAULT_RES>, Anyhow>,
StateInto<StateR2<<G as Graph>::Key>>,
DirectTravelHeuristic<SharedGraph<G>, TravelTimeCost>,
>,
KeyedCloser<DiscreteSpaceTimeSE2<<G as Graph>::Key, DEFAULT_RES>>,
InitializeSE2<SharedGraph<G>, DEFAULT_RES>,
SatisfySE2,
MergeIntoGoal<DEFAULT_RES>,
>;
impl<G> SearchSE2<G>
where
G: Graph + Reversible,
G::Key: Key + Clone,
G::Vertex: Positioned + MaybeOriented,
G::EdgeAttributes: SpeedLimiter,
{
pub fn new_se2(graph: SharedGraph<G>, motion: DifferentialDriveLineFollow) -> Self {
InformedSearch::new(
GraphMotion {
space: DiscreteSpaceTimeSE2::<G::Key, DEFAULT_RES>::new(),
graph: graph.clone(),
extrapolator: motion,
},
TravelTimeCost(1.0),
DefineTrait::<StateSE2<G::Key, DEFAULT_RES>>::new().lift(
StateInto::<StateR2<G::Key>>::new(),
DirectTravelHeuristic {
space: DiscreteSpaceTimeR2::<G::Key>::new(),
graph: graph.clone(),
weight: TravelTimeCost(1.0),
extrapolator: motion.into(),
},
),
KeyedCloser(DiscreteSpaceTimeSE2::<G::Key, DEFAULT_RES>::new()),
)
.with_initializer(InitializeSE2(graph))
.with_satisfier(SatisfySE2::from(motion))
.with_connector(MergeIntoGoal(motion))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{algorithm::AStarConnect, graph::SimpleGraph, motion::SpeedLimit, Planner};
use std::sync::Arc;
#[test]
fn test_simple_se2() {
let s = SpeedLimit(None);
let graph = SimpleGraph::from_iters(
[
Point::new(0.0, 0.0), Point::new(1.0, 0.0), Point::new(2.0, 0.0), Point::new(3.0, 0.0), Point::new(1.0, -1.0), Point::new(2.0, -1.0), Point::new(3.0, -1.0), Point::new(2.0, -2.0), Point::new(3.0, -2.0), ],
[
(0, 1, s),
(1, 0, s),
(1, 2, s),
(2, 1, s),
(2, 3, s),
(3, 2, s),
(2, 4, s),
(4, 2, s),
(3, 6, s),
(6, 3, s),
(4, 5, s),
(5, 4, s),
(5, 7, s),
(7, 5, s),
(7, 8, s),
(8, 7, s),
],
);
let planner = Planner::new(AStarConnect(InformedSearch::new_se2(
SharedGraph::new(graph),
DifferentialDriveLineFollow::new(2.0, 1.0).unwrap(),
)));
let solution = planner
.plan((0usize, 20_f64.to_radians()), 8usize)
.unwrap()
.solve()
.unwrap();
assert!(solution.solved());
let solution = planner
.plan(
(0usize, 20_f64.to_radians()),
GoalSE2::new(8usize)
.with_orientation(Some(Orientation::from_angle(90_f64.to_radians()))),
)
.unwrap()
.solve()
.unwrap();
println!("{solution:#?}");
assert!(solution.solved());
}
use crate::graph::occupancy::{Cell, NeighborhoodGraph, SparseGrid, Visibility};
#[test]
fn test_occupancy_se2() {
let mut visibility = Visibility::new(SparseGrid::new(0.5), 1.25);
visibility.change_cells(
&(0..=5)
.into_iter()
.flat_map(|i| (0..=5).into_iter().map(move |j| (Cell::new(i, j), true)))
.collect(),
);
let graph = NeighborhoodGraph::new(Arc::new(visibility), []);
let planner = Planner::new(AStarConnect(InformedSearch::new_se2(
SharedGraph::new(graph),
DifferentialDriveLineFollow::new(2.0, 1.0).unwrap(),
)));
let solution = planner
.plan(
(Cell::new(-3, -3), 20_f64.to_radians()),
GoalSE2::new(Cell::new(10, 10)),
)
.unwrap()
.solve()
.unwrap();
assert!(solution.solved());
let solution = planner
.plan(
(Cell::new(-3, -3), 20_f64.to_radians()),
GoalSE2::new(Cell::new(10, 10))
.with_orientation(Some(Orientation::from_angle(-60_f64.to_radians()))),
)
.unwrap()
.solve()
.unwrap();
assert!(solution.solved());
}
}