UFO 1.0.0
An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown
Loading...
Searching...
No Matches
nav_map.hpp
1#ifndef UFO_PLAN_GRAPH_HPP
2#define UFO_PLAN_GRAPH_HPP
3
4// UFO
5#include <ufo/container/tree_map.hpp>
6#include <ufo/map/occupancy/predicate.hpp>
7#include <ufo/numeric/vec.hpp>
8
9// STL
10#include <cstdint>
11#include <limits>
12#include <map>
13#include <queue>
14#include <unordered_map>
15#include <unordered_set>
16#include <utility>
17#include <vector>
18
19namespace ufo
20{
21template <std::size_t Dim = 3>
22class NavMap
23{
24 public:
25 using Coord = Vec<Dim, float>;
26
27 using node_t = std::uint32_t;
28 using edge_t = std::uint32_t;
29
30 static constexpr node_t const NULL_NODE = std::numeric_limits<node_t>::max();
31 static constexpr edge_t const NULL_EDGE = std::numeric_limits<edge_t>::max();
32 static constexpr float const NULL_COST = std::numeric_limits<float>::infinity();
33 static constexpr float const NULL_DISTANCE = std::numeric_limits<float>::infinity();
34
35 private:
36 struct Node : public Coord {
37 float cost{};
38
39 edge_t first_edge{};
40 edge_t last_edge{};
41
42 constexpr Node() = default;
43
44 constexpr Node(Coord const& coord, float cost) : Coord(coord), cost(cost) {}
45 };
46
47 struct Edge {
48 node_t node = NULL_NODE;
49
50 float cost = NULL_COST;
51 float distance = NULL_DISTANCE;
52
53 constexpr Edge() = default;
54
55 constexpr Edge(node_t node, float cost, float distance)
56 : node(node), cost(cost), distance(distance)
57 {
58 }
59 };
60
61 public:
62 struct Path {
63 std::vector<node_t> nodes;
64 float cost{};
65 float distance{};
66 };
67
68 // Iterators
69 using iterator = typename TreeMap<Dim, node_t>::iterator;
70 using const_iterator = typename TreeMap<Dim, node_t>::const_iterator;
71
72 [[nodiscard]] Path plan(node_t start, node_t goal, float distance_weight = 1.0f,
73 float edge_cost_weight = 1.0f, float node_cost_weight = 1.0f,
74 float heuristic_weight = 1.0f) const
75 {
76 std::vector<node_t> starts{start};
77 return plan(starts, goal, distance_weight, edge_cost_weight, node_cost_weight,
78 heuristic_weight);
79 }
80
81 [[nodiscard]] Path plan(std::vector<node_t> const& starts, node_t goal,
82 float distance_weight = 1.0f, float edge_cost_weight = 1.0f,
83 float node_cost_weight = 1.0f,
84 float heuristic_weight = 1.0f) const
85 {
86 auto cost_f = [this, distance_weight, edge_cost_weight,
87 node_cost_weight](edge_t edge) {
88 auto const& e = edges_[edge];
89 auto const& n = nodes_[e.node];
90 return distance_weight * e.distance + edge_cost_weight * e.cost +
91 node_cost_weight * n.cost;
92 };
93
94 return plan(starts, goal, cost_f, heuristic_weight);
95 }
96
97 template <class CostFun>
98 [[nodiscard]] Path plan(node_t start, node_t goal, CostFun cost_f,
99 float heuristic_weight = 1.0f) const
100 {
101 std::vector<node_t> starts{start};
102 return plan(starts, goal, cost_f, heuristic_weight);
103 }
104
105 template <class CostFun>
106 [[nodiscard]] Path plan(std::vector<node_t> const& starts, node_t goal, CostFun cost_f,
107 float heuristic_weight = 1.0f) const
108 {
109 auto heuristic_f = [this, heuristic_weight](node_t cur, node_t goal) {
110 return heuristic_weight * ufo::distance(coord(cur), coord(goal));
111 };
112
113 return plan(starts, goal, cost_f, heuristic_f);
114 }
115
116 template <class CostFun, class HeuristicFun>
117 [[nodiscard]] Path plan(node_t start, node_t goal, CostFun cost_f,
118 HeuristicFun heuristic_f) const
119 {
120 std::vector<node_t> starts{start};
121 return plan(starts, goal, cost_f, heuristic_f);
122 }
123
124 template <class CostFun, class HeuristicFun>
125 [[nodiscard]] Path plan(std::vector<node_t> const& starts, node_t const& goal,
126 CostFun cost_f, HeuristicFun heuristic_f) const
127 {
128 assert(!starts.empty());
129 assert(std::all_of(starts.begin(), starts.end(),
130 [this](node_t n) { return NULL_NODE != n && nodes_.size() > n; }));
131 assert(NULL_NODE != goal && nodes_.size() > goal);
132
133 auto comp = [](auto const& a, auto const& b) { return a.second > b.second; };
134
135 using OpenSet =
136 std::priority_queue<std::pair<node_t, float>,
137 std::vector<std::pair<node_t, float>>, decltype(comp)>;
138 using ClosedSet = std::unordered_set<node_t>;
139 using CameFrom = std::unordered_map<node_t, node_t>;
140 using GScore = std::unordered_map<node_t, float>;
141
142 OpenSet open(comp);
143 ClosedSet closed;
144 CameFrom came_from;
145 GScore g_score;
146
147 for (node_t start : starts) {
148 g_score[start] = 0.0f;
149 float f_score = heuristic_f(start, goal);
150 open.emplace(start, f_score);
151 }
152
153 node_t cur = NULL_NODE;
154 while (!open.empty() && goal != (cur = open.top().first)) {
155 open.pop();
156
157 if (!closed.insert(cur).second) {
158 // We have already processed this node
159 continue;
160 }
161
162 Node const& n = nodes_[cur];
163 float gs = g_score[cur];
164 for (edge_t e = n.first_edge; n.last_edge > e; ++e) {
165 node_t neighbor = edges_[e].node;
166
167 if (NULL_DISTANCE == edges_[e].distance || 0 < closed.count(neighbor)) {
168 continue;
169 }
170
171 float tentative_g_score = gs + cost_f(e);
172 if (auto [it, inserted] = g_score.try_emplace(neighbor, tentative_g_score);
173 inserted || tentative_g_score < it->second) {
174 came_from[neighbor] = cur;
175 it->second = tentative_g_score;
176 float f_score = tentative_g_score + heuristic_f(neighbor, goal);
177 open.emplace(neighbor, f_score);
178 }
179 }
180 }
181
182 Path path{};
183
184 if (goal != cur) {
185 // Could not find a path
186 path.distance = -1.0f;
187 return path;
188 }
189
190 path.cost = g_score[goal];
191
192 // Reconstruct paths
193 path.nodes.push_back(goal);
194 for (auto it = came_from.find(goal); came_from.end() != it;
195 it = came_from.find(path.nodes.back())) {
196 path.nodes.push_back(it->second);
197 }
198
199 std::reverse(path.nodes.begin(), path.nodes.end());
200
201 for (std::size_t i = 1; path.nodes.size() > i; ++i) {
202 Edge const& e = edges_[edge(path.nodes[i - 1], path.nodes[i])];
203 path.distance += e.distance;
204 }
205
206 return path;
207 }
208
209 template <class Map>
210 void update(Map const& map, float robot_radius)
211 {
212 unsigned min_depth{};
213 for (; map.numDepthLevels() > min_depth; ++min_depth) {
214 if (min(map.length(min_depth)) > robot_radius) {
215 break;
216 }
217 }
218
219 std::vector<TreeIndex> candidate_nodes;
220 auto pred = pred::Free() && pred::Depth() >= min_depth;
221 for (auto node : map.query(pred)) {
222 candidate_nodes.push_back(node);
223 }
224
225 std::vector<TreeCoord<Dim>> nodes;
226 nodes.reserve(candidate_nodes.size());
227 for (auto node : candidate_nodes) {
228 auto bb = map.boundingBox(node) + robot_radius;
229 auto p = !pred::Free() && pred::Intersects(bb);
230 for (auto n : map.query(p)) {
231 // TODO: Implement
232 }
233 }
234
235 map_.clear(map.length(min_depth), map.resolution(min_depth));
236
237 for (auto node : nodes) {
238 Coord coord = map.center(node);
239 auto p = !pred::Free() && pred::Intersects(Sphere(coord, robot_radius));
240 for (auto n : map.query(p)) {
241 continue;
242 }
243
244 // TODO: Implement
245 }
246
247 // TODO: Implement
248 }
249
250 template <class Map>
251 void updateModified(Map const& map)
252 {
253 // TODO: Implement
254 }
255
256 void clear()
257 {
258 map_.clear();
259 nodes_.clear();
260 edges_.clear();
261 free_nodes_.clear();
262 free_edges_.clear();
263 num_nodes_ = {};
264 num_edges_ = {};
265 }
266
267 [[nodiscard]] std::size_t numNeighbors(node_t node) const
268 {
269 Node const& n = nodes_[node];
270 std::size_t num{};
271 for (edge_t e = n.first_edge; n.last_edge > e; ++e) {
272 if (NULL_DISTANCE != edges_[e].distance) {
273 ++num;
274 }
275 }
276 return num;
277 }
278
279 [[nodiscard]] std::vector<node_t> neighbors(node_t node) const
280 {
281 assert(NULL_NODE != node && nodes_.size() > node);
282 auto const& n = nodes_[node];
283 std::vector<node_t> res;
284 res.reserve(n.last_edge - n.first_edge);
285 for (edge_t e = n.first_edge; n.last_edge > e; ++e) {
286 if (NULL_DISTANCE != edges_[e].distance) {
287 res.push_back(edges_[e].node);
288 }
289 }
290 return res;
291 }
292
293 [[nodiscard]] Coord const& coord(node_t node) const
294 {
295 assert(NULL_NODE != node && nodes_.size() > node);
296 return static_cast<Coord const&>(nodes_[node]);
297 }
298
299 [[nodiscard]] float& cost(node_t node)
300 {
301 assert(NULL_NODE != node && nodes_.size() > node);
302 return nodes_[node].cost;
303 }
304
305 [[nodiscard]] float cost(node_t node) const
306 {
307 assert(NULL_NODE != node && nodes_.size() > node);
308 return nodes_[node].cost;
309 }
310
311 [[nodiscard]] float& cost(node_t node, node_t neighbor)
312 {
313 auto e = edge(node, neighbor);
314 assert(NULL_EDGE != e);
315 return edges_[e].cost;
316 }
317
318 [[nodiscard]] float cost(node_t node, node_t neighbor) const
319 {
320 auto e = edge(node, neighbor);
321 assert(NULL_EDGE != e);
322 return edges_[e].cost;
323 }
324
325 [[nodiscard]] float& distance(node_t node, node_t neighbor)
326 {
327 auto e = edge(node, neighbor);
328 assert(NULL_EDGE != e);
329 return edges_[e].distance;
330 }
331
332 [[nodiscard]] float distance(node_t node, node_t neighbor) const
333 {
334 auto e = edge(node, neighbor);
335 assert(NULL_EDGE != e);
336 return edges_[e].distance;
337 }
338
339 node_t addNode(Coord const& position, float cost)
340 {
341 node_t node = nodes_.size();
342 nodes_.emplace_back(position, cost);
343 map_.emplace(position, node);
344 ++num_nodes_;
345 return node;
346 }
347
348 bool eraseNode(node_t const& node)
349 {
350 assert(validNode(node));
351
352 // Remove from map
353 Coord coord(node);
354 for (auto it = map_.beginNearest(coord), last = map_.endNearest(); last != it; ++it) {
355 if (it->second == node) {
356 map_.erase(it);
357 break;
358 } else if (it->first != coord) {
359 return false;
360 }
361 }
362
363 --num_nodes_;
364
365 // Remove edges
366 // TODO: Implement
367 // auto const& n = nodes_[node];
368 // if (n.first_edge != n.last_edge) {
369 // for (edge_t edge = n.first_edge; n.last_edge > edge; ++edge) {
370 // // TODO: Implement
371 // eraseBiEdge(node, edges_[edge].node);
372 // }
373
374 // for (edge_t edge = n.first_edge; n.last_edge > edge; edge += edges_mul_) {
375 // free_edges_.push_back(edge);
376 // }
377 // }
378
379 // Remove node
380 free_nodes_.push_back(node);
381
382 return false;
383 }
384
385 bool addEdge(node_t start, node_t end, float cost = 0.0f)
386 {
387 assert(validNode(start));
388 assert(validNode(end));
389 return addEdge(start, end, cost, ufo::distance(coord(start), coord(end)));
390 }
391
392 bool addEdge(node_t start, node_t end, float cost, float distance)
393 {
394 assert(validNode(start));
395 assert(validNode(end));
396
397 edge_t e = edge(end, start);
398 float cost_2 = NULL_EDGE == e ? NULL_COST : edges_[e].cost;
399 float distance_2 = NULL_EDGE == e ? NULL_DISTANCE : edges_[e].distance;
400
401 return 0 != addBiEdge(start, end, cost, cost_2, distance, distance_2);
402 }
403
404 int addBiEdge(node_t node_1, node_t node_2, float cost = 0.0f)
405 {
406 assert(validNode(node_1));
407 assert(validNode(node_2));
408 return addBiEdge(node_1, node_2, cost, ufo::distance(coord(node_1), coord(node_2)));
409 }
410
411 int addBiEdge(node_t node_1, node_t node_2, float cost, float distance)
412 {
413 assert(validNode(node_1));
414 assert(validNode(node_2));
415 return addBiEdge(node_1, node_2, cost, cost, distance, distance);
416 }
417
418 int addBiEdge(node_t node_1, node_t node_2, float cost_1, float cost_2,
419 float distance_1, float distance_2)
420 {
421 assert(validNode(node_1));
422 assert(validNode(node_2));
423
424 int num_added{};
425 num_added += NULL_DISTANCE == distance_1 ? 0 : 1;
426 num_added += NULL_DISTANCE == distance_2 ? 0 : 1;
427
428 if (auto e_1 = edge(node_1, node_2); NULL_EDGE != e_1) {
429 // Edge already exists, update cost and distance
430 edge_t e_2 = edge(node_2, node_1);
431 assert(validEdge(e_2));
432
433 num_added -= NULL_DISTANCE == edges_[e_1].distance ? 0 : 1;
434 num_added -= NULL_DISTANCE == edges_[e_2].distance ? 0 : 1;
435
436 edges_[e_1].cost = cost_1;
437 edges_[e_2].cost = cost_2;
438 edges_[e_1].distance = distance_1;
439 edges_[e_2].distance = distance_2;
440
441 return num_added;
442 }
443
444 Node& n_1 = nodes_[node_1];
445 Node& n_2 = nodes_[node_2];
446
447 std::size_t const s = edges_.size();
448 std::size_t const s_1 = numEdges(node_1);
449 std::size_t const s_2 = numEdges(node_2);
450
451 std::size_t const a_1 = numAllocatedEdges(node_1);
452 std::size_t const a_2 = numAllocatedEdges(node_2);
453
454 std::size_t n_a_1 = a_1;
455 std::size_t n_a_2 = a_2;
456
457 edge_t e_1_first = n_1.first_edge;
458 edge_t e_2_first = n_2.first_edge;
459
460 std::size_t num_new_edges{};
461
462 if (s_1 == a_1) {
463 n_a_1 += edges_mul_;
464
465 if (auto it = free_edges_.find(n_a_1);
466 free_edges_.end() != it && !it->second.empty()) {
467 e_1_first = it->second.back();
468 it->second.pop_back();
469 } else {
470 e_1_first = s;
471 num_new_edges += n_a_1;
472 }
473 }
474 if (s_2 == a_2) {
475 n_a_2 += edges_mul_;
476
477 if (auto it = free_edges_.find(n_a_2);
478 free_edges_.end() != it && !it->second.empty()) {
479 e_2_first = it->second.back();
480 it->second.pop_back();
481 } else {
482 e_2_first = e_1_first == s ? s + n_a_1 : s;
483 num_new_edges += n_a_2;
484 }
485 }
486
487 if (0 != num_new_edges) {
488 edges_.resize(edges_.size() + num_new_edges);
489 }
490
491 if (e_1_first != n_1.first_edge) {
492 std::copy(edges_.begin() + n_1.first_edge, edges_.begin() + n_1.last_edge,
493 edges_.begin() + e_1_first);
494 if (0 != a_1) {
495 free_edges_[a_1].push_back(n_1.first_edge);
496 }
497 n_1.first_edge = e_1_first;
498 }
499 if (e_2_first != n_2.first_edge) {
500 std::copy(edges_.begin() + n_2.first_edge, edges_.begin() + n_2.last_edge,
501 edges_.begin() + e_2_first);
502 if (0 != a_2) {
503 free_edges_[a_2].push_back(n_2.first_edge);
504 }
505 n_2.first_edge = e_2_first;
506 }
507
508 n_1.last_edge = e_1_first + s_1 + 1;
509 n_2.last_edge = e_2_first + s_2 + 1;
510
511 edges_[n_1.last_edge - 1] = Edge{node_2, cost_1, distance_1};
512 edges_[n_2.last_edge - 1] = Edge{node_1, cost_2, distance_2};
513
514 return num_added;
515 }
516
517 bool eraseEdge(node_t start, node_t end)
518 {
519 assert(validNode(start));
520 assert(validNode(end));
521
522 if (!isNeighbor(start, end)) {
523 return false;
524 }
525
526 if (isNeighbor(end, start)) {
527 // Bi-directional edge, so do not purge
528 edge_t e = edge(start, end);
529 edges_[e] = Edge{end, NULL_COST, NULL_DISTANCE};
530 return true;
531 }
532
533 // Directional edge, so purge
534
535 // TODO: Implement
536
537 return true;
538 }
539
540 int eraseBiEdge(node_t node_1, node_t node_2)
541 {
542 int num_erased{};
543 num_erased += eraseEdge(node_1, node_2) ? 1 : 0;
544 num_erased += eraseEdge(node_2, node_1) ? 1 : 0;
545 return num_erased;
546 }
547
548 [[nodiscard]] node_t nearestNode(node_t node) const
549 {
550 assert(validNode(node));
551 for (auto const& [_, n] : map_.nearest(coord(node))) {
552 if (node == n) {
553 continue;
554 }
555 return n;
556 }
557 return NULL_NODE;
558 }
559
560 template <class Geometry>
561 [[nodiscard]] node_t nearestNode(Geometry const& geometry) const
562 {
563 for (auto const& [_, node] : map_.nearest(geometry)) {
564 return node;
565 }
566 return NULL_NODE;
567 }
568
569 [[nodiscard]] std::vector<node_t> kNearestNodes(node_t node, unsigned k) const
570 {
571 assert(validNode(node));
572 std::vector<node_t> res;
573 res.reserve(k);
574 for (auto const& [_, n] : map_.nearest(coord(node))) {
575 if (node == n) {
576 continue;
577 }
578 if (0 == k) {
579 break;
580 }
581 --k;
582 res.push_back(n);
583 }
584 return res;
585 }
586
587 template <class Geometry>
588 [[nodiscard]] std::vector<node_t> kNearestNodes(Geometry const& geometry,
589 unsigned k) const
590 {
591 std::vector<node_t> res;
592 res.reserve(k);
593 for (auto const& [_, node] : map_.nearest(geometry)) {
594 if (0 == k) {
595 break;
596 }
597 --k;
598 res.push_back(node);
599 }
600 return res;
601 }
602
603 template <class Geometry>
604 [[nodiscard]] std::vector<node_t> intersectingNodes(Geometry const& geometry) const
605 {
606 std::vector<node_t> res;
607 for (auto const& [_, node] : map_.query(pred::Intersects(geometry))) {
608 res.push_back(node);
609 }
610 return res;
611 }
612
613 iterator begin() { return map_.begin(); }
614
615 iterator end() { return map_.end(); }
616
617 const_iterator begin() const { return map_.cbegin(); }
618
619 const_iterator end() const { return map_.cend(); }
620
621 const_iterator cbegin() const { return begin(); }
622
623 const_iterator cend() const { return end(); }
624
625 [[nodiscard]] bool validNode(node_t node) const
626 {
627 return NULL_NODE != node && nodes_.size() > node;
628 }
629
630 [[nodiscard]] bool isNeighbor(node_t node, node_t neighbor) const
631 {
632 edge_t e = edge(node, neighbor);
633 return NULL_EDGE != e && NULL_DISTANCE != edges_[e].distance;
634 }
635
636 [[nodiscard]] std::size_t numNodes() const { return num_nodes_; }
637
638 [[nodiscard]] std::size_t numEdges() const { return num_edges_; }
639
640 private:
641 [[nodiscard]] edge_t edge(node_t node, node_t neighbor) const
642 {
643 assert(validNode(node));
644 assert(validNode(neighbor));
645 auto const& n = nodes_[node];
646 for (edge_t e = n.first_edge; n.last_edge > e; ++e) {
647 if (edges_[e].node == neighbor) {
648 return e;
649 }
650 }
651 return NULL_EDGE;
652 }
653
654 [[nodiscard]] bool validEdge(edge_t edge) const
655 {
656 return NULL_EDGE != edge && edges_.size() > edge;
657 }
658
659 [[nodiscard]] std::size_t numEdges(node_t node) const
660 {
661 assert(validNode(node));
662 Node const& n = nodes_[node];
663 return n.last_edge - n.first_edge;
664 }
665
666 [[nodiscard]] std::size_t numAllocatedEdges(node_t node) const
667 {
668 std::size_t const s = numEdges(node);
669 return edges_mul_ * ((s + (edges_mul_ - 1)) / edges_mul_);
670 }
671
672 private:
673 TreeMap<Dim, node_t> map_;
674
675 std::vector<Node> nodes_;
676 std::vector<Edge> edges_;
677
678 std::size_t edges_mul_ = 10;
679
680 std::vector<node_t> free_nodes_;
681 std::map<std::size_t, std::vector<node_t>> free_edges_;
682
683 std::size_t num_nodes_{};
684 std::size_t num_edges_{};
685};
686} // namespace ufo
687
688#endif // UFO_PLAN_GRAPH_HPP
All vision-related classes and functions.
Definition cloud.hpp:49
constexpr T b(Lab< T, Flags > color) noexcept
Returns the un-weighted blue–yellow axis value.
Definition lab.hpp:326
constexpr T a(Lab< T, Flags > color) noexcept
Returns the un-weighted green–red axis value.
Definition lab.hpp:310
constexpr Vec< Geometry::dimension(), typename Geometry::value_type > min(Geometry const &g)
Returns the minimum coordinate of the minimum spanning axis-aligned bounding box of a geometry.
Definition fun.hpp:58
constexpr auto distance(A const &a, B const &b)
Computes the minimum distance between two shapes.
Definition distance.hpp:61