Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
navfn.cpp
1 // Copyright (c) 2008, Willow Garage, Inc.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 //
9 // * Redistributions in binary form must reproduce the above copyright
10 // notice, this list of conditions and the following disclaimer in the
11 // documentation and/or other materials provided with the distribution.
12 //
13 // * Neither the name of the copyright holder nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
28 
29 //
30 // Navigation function computation
31 // Uses Dijkstra's method
32 // Modified for Euclidean-distance computation
33 //
34 // Path calculation uses no interpolation when pot field is at max in
35 // nearby cells
36 //
37 // Path calc has sanity check that it succeeded
38 //
39 
40 #include "nav2_navfn_planner/navfn.hpp"
41 
42 #include <algorithm>
43 #include "nav2_core/planner_exceptions.hpp"
44 #include "rclcpp/logger.hpp"
45 #include "rclcpp/logging.hpp"
46 
47 namespace nav2_navfn_planner
48 {
49 
50 //
51 // function to perform nav fn calculation
52 // keeps track of internal buffers, will be more efficient
53 // if the size of the environment does not change
54 //
55 
56 // Example usage:
57 /*
58 int
59 create_nav_plan_astar(
60  COSTTYPE * costmap, int nx, int ny,
61  int * goal, int * start,
62  float * plan, int nplan)
63 {
64  static NavFn * nav = NULL;
65 
66  if (nav == NULL) {
67  nav = new NavFn(nx, ny);
68  }
69 
70  if (nav->nx != nx || nav->ny != ny) { // check for compatibility with previous call
71  delete nav;
72  nav = new NavFn(nx, ny);
73  }
74 
75  nav->setGoal(goal);
76  nav->setStart(start);
77 
78  nav->costarr = costmap;
79  nav->setupNavFn(true);
80 
81  // calculate the nav fn and path
82  nav->priInc = 2 * COST_NEUTRAL;
83  nav->propNavFnAstar(std::max(nx * ny / 20, nx + ny));
84 
85  // path
86  int len = nav->calcPath(nplan);
87 
88  if (len > 0) { // found plan
89  RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[NavFn] Path found, %d steps\n", len);
90  } else {
91  RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[NavFn] No path found\n");
92  }
93 
94  if (len > 0) {
95  for (int i = 0; i < len; i++) {
96  plan[i * 2] = nav->pathx[i];
97  plan[i * 2 + 1] = nav->pathy[i];
98  }
99  }
100 
101  return len;
102 }
103 */
104 
105 //
106 // create nav fn buffers
107 //
108 
109 NavFn::NavFn(int xs, int ys)
110 {
111  // create cell arrays
112  costarr = NULL;
113  potarr = NULL;
114  pending = NULL;
115  gradx = grady = NULL;
116  setNavArr(xs, ys);
117 
118  // priority buffers
119  pb1 = new int[PRIORITYBUFSIZE];
120  pb2 = new int[PRIORITYBUFSIZE];
121  pb3 = new int[PRIORITYBUFSIZE];
122 
123  // for Dijkstra (breadth-first), set to COST_NEUTRAL
124  // for A* (best-first), set to COST_NEUTRAL
125  priInc = 2 * COST_NEUTRAL;
126 
127  // goal and start
128  goal[0] = goal[1] = 0;
129  start[0] = start[1] = 0;
130 
131  // display function
132  // displayFn = NULL;
133  // displayInt = 0;
134 
135  // path buffers
136  npathbuf = npath = 0;
137  pathx = pathy = NULL;
138  pathStep = 0.5;
139 }
140 
141 
142 NavFn::~NavFn()
143 {
144  if (costarr) {
145  delete[] costarr;
146  }
147  if (potarr) {
148  delete[] potarr;
149  }
150  if (pending) {
151  delete[] pending;
152  }
153  if (gradx) {
154  delete[] gradx;
155  }
156  if (grady) {
157  delete[] grady;
158  }
159  if (pathx) {
160  delete[] pathx;
161  }
162  if (pathy) {
163  delete[] pathy;
164  }
165  if (pb1) {
166  delete[] pb1;
167  }
168  if (pb2) {
169  delete[] pb2;
170  }
171  if (pb3) {
172  delete[] pb3;
173  }
174 }
175 
176 
177 //
178 // set goal, start positions for the nav fn
179 //
180 
181 void
183 {
184  goal[0] = g[0];
185  goal[1] = g[1];
186  RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[NavFn] Setting goal to %d,%d\n", goal[0], goal[1]);
187 }
188 
189 void
191 {
192  start[0] = g[0];
193  start[1] = g[1];
194  RCLCPP_DEBUG(
195  rclcpp::get_logger("rclcpp"), "[NavFn] Setting start to %d,%d\n", start[0],
196  start[1]);
197 }
198 
199 //
200 // Set/Reset map size
201 //
202 
203 void
204 NavFn::setNavArr(int xs, int ys)
205 {
206  RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[NavFn] Array is %d x %d\n", xs, ys);
207 
208  nx = xs;
209  ny = ys;
210  ns = nx * ny;
211 
212  if (costarr) {
213  delete[] costarr;
214  }
215  if (potarr) {
216  delete[] potarr;
217  }
218  if (pending) {
219  delete[] pending;
220  }
221 
222  if (gradx) {
223  delete[] gradx;
224  }
225  if (grady) {
226  delete[] grady;
227  }
228 
229  costarr = new COSTTYPE[ns]; // cost array, 2d config space
230  memset(costarr, 0, ns * sizeof(COSTTYPE));
231  potarr = new float[ns]; // navigation potential array
232  pending = new bool[ns];
233  memset(pending, 0, ns * sizeof(bool));
234  gradx = new float[ns];
235  grady = new float[ns];
236 }
237 
238 
239 //
240 // set up cost array, usually from ROS
241 //
242 
243 void
244 NavFn::setCostmap(const COSTTYPE * cmap, bool isROS, bool allow_unknown)
245 {
246  COSTTYPE * cm = costarr;
247  if (isROS) { // ROS-type cost array
248  for (int i = 0; i < ny; i++) {
249  int k = i * nx;
250  for (int j = 0; j < nx; j++, k++, cmap++, cm++) {
251  // This transforms the incoming cost values:
252  // COST_OBS -> COST_OBS (incoming "lethal obstacle")
253  // COST_OBS_ROS -> COST_OBS (incoming "inscribed inflated obstacle")
254  // values in range 0 to 252 -> values from COST_NEUTRAL to COST_OBS_ROS.
255  *cm = COST_OBS;
256  int v = *cmap;
257  if (v < COST_OBS_ROS) {
258  v = COST_NEUTRAL + COST_FACTOR * v;
259  if (v >= COST_OBS) {
260  v = COST_OBS - 1;
261  }
262  *cm = v;
263  } else if (v == COST_UNKNOWN_ROS && allow_unknown) {
264  v = COST_OBS - 1;
265  *cm = v;
266  }
267  }
268  }
269  } else { // not a ROS map, just a PGM
270  for (int i = 0; i < ny; i++) {
271  int k = i * nx;
272  for (int j = 0; j < nx; j++, k++, cmap++, cm++) {
273  *cm = COST_OBS;
274  if (i < 7 || i > ny - 8 || j < 7 || j > nx - 8) {
275  continue; // don't do borders
276  }
277  int v = *cmap;
278  if (v < COST_OBS_ROS) {
279  v = COST_NEUTRAL + COST_FACTOR * v;
280  if (v >= COST_OBS) {
281  v = COST_OBS - 1;
282  }
283  *cm = v;
284  } else if (v == COST_UNKNOWN_ROS) {
285  v = COST_OBS - 1;
286  *cm = v;
287  }
288  }
289  }
290  }
291 }
292 
293 bool
294 NavFn::calcNavFnDijkstra(std::function<bool()> cancelChecker, bool atStart)
295 {
296  setupNavFn(true);
297 
298  // calculate the nav fn and path
299  return propNavFnDijkstra(std::max(nx * ny / 20, nx + ny), cancelChecker, atStart);
300 }
301 
302 
303 //
304 // calculate navigation function, given a costmap, goal, and start
305 //
306 
307 bool
308 NavFn::calcNavFnAstar(std::function<bool()> cancelChecker)
309 {
310  setupNavFn(true);
311 
312  // calculate the nav fn and path
313  return propNavFnAstar(std::max(nx * ny / 20, nx + ny), cancelChecker);
314 }
315 
316 //
317 // returning values
318 //
319 
320 float * NavFn::getPathX() {return pathx;}
321 float * NavFn::getPathY() {return pathy;}
322 int NavFn::getPathLen() {return npath;}
323 
324 // inserting onto the priority blocks
325 #define push_cur(n) {if (n >= 0 && n < ns && !pending[n] && \
326  costarr[n] < COST_OBS && curPe < PRIORITYBUFSIZE) \
327  {curP[curPe++] = n; pending[n] = true;}}
328 #define push_next(n) {if (n >= 0 && n < ns && !pending[n] && \
329  costarr[n] < COST_OBS && nextPe < PRIORITYBUFSIZE) \
330  {nextP[nextPe++] = n; pending[n] = true;}}
331 #define push_over(n) {if (n >= 0 && n < ns && !pending[n] && \
332  costarr[n] < COST_OBS && overPe < PRIORITYBUFSIZE) \
333  {overP[overPe++] = n; pending[n] = true;}}
334 
335 
336 // Set up navigation potential arrays for new propagation
337 
338 void
339 NavFn::setupNavFn(bool keepit)
340 {
341  // reset values in propagation arrays
342  for (int i = 0; i < ns; i++) {
343  potarr[i] = POT_HIGH;
344  if (!keepit) {
345  costarr[i] = COST_NEUTRAL;
346  }
347  gradx[i] = grady[i] = 0.0;
348  }
349 
350  // outer bounds of cost array
351  COSTTYPE * pc;
352  pc = costarr;
353  for (int i = 0; i < nx; i++) {
354  *pc++ = COST_OBS;
355  }
356  pc = costarr + (ny - 1) * nx;
357  for (int i = 0; i < nx; i++) {
358  *pc++ = COST_OBS;
359  }
360  pc = costarr;
361  for (int i = 0; i < ny; i++, pc += nx) {
362  *pc = COST_OBS;
363  }
364  pc = costarr + nx - 1;
365  for (int i = 0; i < ny; i++, pc += nx) {
366  *pc = COST_OBS;
367  }
368 
369  // priority buffers
370  curT = COST_OBS;
371  curP = pb1;
372  curPe = 0;
373  nextP = pb2;
374  nextPe = 0;
375  overP = pb3;
376  overPe = 0;
377  memset(pending, 0, ns * sizeof(bool));
378 
379  // set goal
380  int k = goal[0] + goal[1] * nx;
381  initCost(k, 0);
382 
383  // find # of obstacle cells
384  pc = costarr;
385  int ntot = 0;
386  for (int i = 0; i < ns; i++, pc++) {
387  if (*pc >= COST_OBS) {
388  ntot++; // number of cells that are obstacles
389  }
390  }
391  nobs = ntot;
392 }
393 
394 
395 // initialize a goal-type cost for starting propagation
396 
397 void
398 NavFn::initCost(int k, float v)
399 {
400  potarr[k] = v;
401  push_cur(k + 1);
402  push_cur(k - 1);
403  push_cur(k - nx);
404  push_cur(k + nx);
405 }
406 
407 
408 //
409 // Critical function: calculate updated potential value of a cell,
410 // given its neighbors' values
411 // Planar-wave update calculation from two lowest neighbors in a 4-grid
412 // Quadratic approximation to the interpolated value
413 // No checking of bounds here, this function should be fast
414 //
415 
416 #define INVSQRT2 0.707106781
417 
418 inline void
420 {
421  // get neighbors
422  const float l = potarr[n - 1];
423  const float r = potarr[n + 1];
424  const float u = potarr[n - nx];
425  const float d = potarr[n + nx];
426  // ROS_INFO("[Update] c: %0.1f l: %0.1f r: %0.1f u: %0.1f d: %0.1f\n",
427  // potarr[n], l, r, u, d);
428  // ROS_INFO("[Update] cost: %d\n", costarr[n]);
429 
430  // find lowest, and its lowest neighbor
431  float ta, tc;
432  if (l < r) {tc = l;} else {tc = r;}
433  if (u < d) {ta = u;} else {ta = d;}
434 
435  // do planar wave update
436  if (costarr[n] < COST_OBS) { // don't propagate into obstacles
437  float hf = static_cast<float>(costarr[n]); // traversability factor
438  float dc = tc - ta; // relative cost between ta,tc
439  if (dc < 0) { // ta is lowest
440  dc = -dc;
441  ta = tc;
442  }
443 
444  // calculate new potential
445  float pot;
446  if (dc >= hf) { // if too large, use ta-only update
447  pot = ta + hf;
448  } else { // two-neighbor interpolation update
449  // use quadratic approximation
450  // might speed this up through table lookup, but still have to
451  // do the divide
452  const float div = dc / hf;
453  const float v = -0.2301 * div * div + 0.5307 * div + 0.7040;
454  pot = ta + hf * v;
455  }
456 
457  // ROS_INFO("[Update] new pot: %d\n", costarr[n]);
458 
459  // now add affected neighbors to priority blocks
460  if (pot < potarr[n]) {
461  float le = INVSQRT2 * static_cast<float>(costarr[n - 1]);
462  float re = INVSQRT2 * static_cast<float>(costarr[n + 1]);
463  float ue = INVSQRT2 * static_cast<float>(costarr[n - nx]);
464  float de = INVSQRT2 * static_cast<float>(costarr[n + nx]);
465  potarr[n] = pot;
466  if (pot < curT) { // low-cost buffer block
467  if (l > pot + le) {push_next(n - 1);}
468  if (r > pot + re) {push_next(n + 1);}
469  if (u > pot + ue) {push_next(n - nx);}
470  if (d > pot + de) {push_next(n + nx);}
471  } else { // overflow block
472  if (l > pot + le) {push_over(n - 1);}
473  if (r > pot + re) {push_over(n + 1);}
474  if (u > pot + ue) {push_over(n - nx);}
475  if (d > pot + de) {push_over(n + nx);}
476  }
477  }
478  }
479 }
480 
481 //
482 // Use A* method for setting priorities
483 // Critical function: calculate updated potential value of a cell,
484 // given its neighbors' values
485 // Planar-wave update calculation from two lowest neighbors in a 4-grid
486 // Quadratic approximation to the interpolated value
487 // No checking of bounds here, this function should be fast
488 //
489 
490 #define INVSQRT2 0.707106781
491 
492 inline void
494 {
495  // get neighbors
496  float l = potarr[n - 1];
497  float r = potarr[n + 1];
498  float u = potarr[n - nx];
499  float d = potarr[n + nx];
500  // ROS_INFO("[Update] c: %0.1f l: %0.1f r: %0.1f u: %0.1f d: %0.1f\n",
501  // potarr[n], l, r, u, d);
502  // ROS_INFO("[Update] cost of %d: %d\n", n, costarr[n]);
503 
504  // find lowest, and its lowest neighbor
505  float ta, tc;
506  if (l < r) {tc = l;} else {tc = r;}
507  if (u < d) {ta = u;} else {ta = d;}
508 
509  // do planar wave update
510  if (costarr[n] < COST_OBS) { // don't propagate into obstacles
511  float hf = static_cast<float>(costarr[n]); // traversability factor
512  float dc = tc - ta; // relative cost between ta,tc
513  if (dc < 0) { // ta is lowest
514  dc = -dc;
515  ta = tc;
516  }
517 
518  // calculate new potential
519  float pot;
520  if (dc >= hf) { // if too large, use ta-only update
521  pot = ta + hf;
522  } else { // two-neighbor interpolation update
523  // use quadratic approximation
524  // might speed this up through table lookup, but still have to
525  // do the divide
526  const float div = dc / hf;
527  const float v = -0.2301 * div * div + 0.5307 * div + 0.7040;
528  pot = ta + hf * v;
529  }
530 
531  // ROS_INFO("[Update] new pot: %d\n", costarr[n]);
532 
533  // now add affected neighbors to priority blocks
534  if (pot < potarr[n]) {
535  float le = INVSQRT2 * static_cast<float>(costarr[n - 1]);
536  float re = INVSQRT2 * static_cast<float>(costarr[n + 1]);
537  float ue = INVSQRT2 * static_cast<float>(costarr[n - nx]);
538  float de = INVSQRT2 * static_cast<float>(costarr[n + nx]);
539 
540  // calculate distance
541  int x = n % nx;
542  int y = n / nx;
543  float dist = hypot(x - start[0], y - start[1]) * static_cast<float>(COST_NEUTRAL);
544 
545  potarr[n] = pot;
546  pot += dist;
547  if (pot < curT) { // low-cost buffer block
548  if (l > pot + le) {push_next(n - 1);}
549  if (r > pot + re) {push_next(n + 1);}
550  if (u > pot + ue) {push_next(n - nx);}
551  if (d > pot + de) {push_next(n + nx);}
552  } else {
553  if (l > pot + le) {push_over(n - 1);}
554  if (r > pot + re) {push_over(n + 1);}
555  if (u > pot + ue) {push_over(n - nx);}
556  if (d > pot + de) {push_over(n + nx);}
557  }
558  }
559  }
560 }
561 
562 
563 //
564 // main propagation function
565 // Dijkstra method, breadth-first
566 // runs for a specified number of cycles,
567 // or until it runs out of cells to update,
568 // or until the Start cell is found (atStart = true)
569 //
570 
571 bool
572 NavFn::propNavFnDijkstra(int cycles, std::function<bool()> cancelChecker, bool atStart)
573 {
574  int nwv = 0; // max priority block size
575  int nc = 0; // number of cells put into priority blocks
576  int cycle = 0; // which cycle we're on
577 
578  // set up start cell
579  int startCell = start[1] * nx + start[0];
580 
581  for (; cycle < cycles; cycle++) { // go for this many cycles, unless interrupted
582  if (cycle % terminal_checking_interval == 0 && cancelChecker()) {
583  throw nav2_core::PlannerCancelled("Planner was cancelled");
584  }
585 
586  if (curPe == 0 && nextPe == 0) { // priority blocks empty
587  break;
588  }
589 
590  // stats
591  nc += curPe;
592  if (curPe > nwv) {
593  nwv = curPe;
594  }
595 
596  // reset pending flags on current priority buffer
597  int * pb = curP;
598  int i = curPe;
599  while (i-- > 0) {
600  pending[*(pb++)] = false;
601  }
602 
603  // process current priority buffer
604  pb = curP;
605  i = curPe;
606  while (i-- > 0) {
607  updateCell(*pb++);
608  }
609 
610  // if (displayInt > 0 && (cycle % displayInt) == 0) {
611  // displayFn(this);
612  // }
613 
614  // swap priority blocks curP <=> nextP
615  curPe = nextPe;
616  nextPe = 0;
617  pb = curP; // swap buffers
618  curP = nextP;
619  nextP = pb;
620 
621  // see if we're done with this priority level
622  if (curPe == 0) {
623  curT += priInc; // increment priority threshold
624  curPe = overPe; // set current to overflow block
625  overPe = 0;
626  pb = curP; // swap buffers
627  curP = overP;
628  overP = pb;
629  }
630 
631  // check if we've hit the Start cell
632  if (atStart) {
633  if (potarr[startCell] < POT_HIGH) {
634  break;
635  }
636  }
637  }
638 
639  RCLCPP_DEBUG(
640  rclcpp::get_logger("rclcpp"),
641  "[NavFn] Used %d cycles, %d cells visited (%d%%), priority buf max %d\n",
642  cycle, nc, (int)((nc * 100.0) / (ns - nobs)), nwv);
643 
644  return (cycle < cycles) ? true : false;
645 }
646 
647 //
648 // main propagation function
649 // A* method, best-first
650 // uses Euclidean distance heuristic
651 // runs for a specified number of cycles,
652 // or until it runs out of cells to update,
653 // or until the Start cell is found (atStart = true)
654 //
655 
656 bool
657 NavFn::propNavFnAstar(int cycles, std::function<bool()> cancelChecker)
658 {
659  int nwv = 0; // max priority block size
660  int nc = 0; // number of cells put into priority blocks
661  int cycle = 0; // which cycle we're on
662 
663  // set initial threshold, based on distance
664  float dist = hypot(goal[0] - start[0], goal[1] - start[1]) * static_cast<float>(COST_NEUTRAL);
665  curT = dist + curT;
666 
667  // set up start cell
668  int startCell = start[1] * nx + start[0];
669 
670  // do main cycle
671  for (; cycle < cycles; cycle++) { // go for this many cycles, unless interrupted
672  if (cycle % terminal_checking_interval == 0 && cancelChecker()) {
673  throw nav2_core::PlannerCancelled("Planner was cancelled");
674  }
675 
676  if (curPe == 0 && nextPe == 0) { // priority blocks empty
677  break;
678  }
679 
680  // stats
681  nc += curPe;
682  if (curPe > nwv) {
683  nwv = curPe;
684  }
685 
686  // reset pending flags on current priority buffer
687  int * pb = curP;
688  int i = curPe;
689  while (i-- > 0) {
690  pending[*(pb++)] = false;
691  }
692 
693  // process current priority buffer
694  pb = curP;
695  i = curPe;
696  while (i-- > 0) {
697  updateCellAstar(*pb++);
698  }
699 
700  // if (displayInt > 0 && (cycle % displayInt) == 0) {
701  // displayFn(this);
702  // }
703 
704  // swap priority blocks curP <=> nextP
705  curPe = nextPe;
706  nextPe = 0;
707  pb = curP; // swap buffers
708  curP = nextP;
709  nextP = pb;
710 
711  // see if we're done with this priority level
712  if (curPe == 0) {
713  curT += priInc; // increment priority threshold
714  curPe = overPe; // set current to overflow block
715  overPe = 0;
716  pb = curP; // swap buffers
717  curP = overP;
718  overP = pb;
719  }
720 
721  // check if we've hit the Start cell
722  if (potarr[startCell] < POT_HIGH) {
723  break;
724  }
725  }
726 
727  last_path_cost_ = potarr[startCell];
728 
729  RCLCPP_DEBUG(
730  rclcpp::get_logger("rclcpp"),
731  "[NavFn] Used %d cycles, %d cells visited (%d%%), priority buf max %d\n",
732  cycle, nc, (int)((nc * 100.0) / (ns - nobs)), nwv);
733 
734  if (potarr[startCell] < POT_HIGH) {
735  return true; // finished up here}
736  } else {
737  return false;
738  }
739 }
740 
741 
743 {
744  return last_path_cost_;
745 }
746 
747 
748 //
749 // Path construction
750 // Find gradient at array points, interpolate path
751 // Use step size of pathStep, usually 0.5 pixel
752 //
753 // Some sanity checks:
754 // 1. Stuck at same index position
755 // 2. Doesn't get near goal
756 // 3. Surrounded by high potentials
757 //
758 
759 int
760 NavFn::calcPath(int n, int * st)
761 {
762  // test write
763  // savemap("test");
764 
765  // check path arrays
766  if (npathbuf < n) {
767  if (pathx) {delete[] pathx;}
768  if (pathy) {delete[] pathy;}
769  pathx = new float[n];
770  pathy = new float[n];
771  npathbuf = n;
772  }
773 
774  // set up start position at cell
775  // st is always upper left corner for 4-point bilinear interpolation
776  if (st == NULL) {st = start;}
777  int stc = st[1] * nx + st[0];
778 
779  // set up offset
780  float dx = 0;
781  float dy = 0;
782  npath = 0;
783 
784  // go for <n> cycles at most
785  for (int i = 0; i < n; i++) {
786  // check if near goal
787  int nearest_point = std::max(
788  0,
789  std::min(
790  nx * ny - 1, stc + static_cast<int>(round(dx)) +
791  static_cast<int>(nx * round(dy))));
792  if (potarr[nearest_point] < COST_NEUTRAL) {
793  pathx[npath] = static_cast<float>(goal[0]);
794  pathy[npath] = static_cast<float>(goal[1]);
795  return ++npath; // done!
796  }
797 
798  if (stc < nx || stc > ns - nx) { // would be out of bounds
799  RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[PathCalc] Out of bounds");
800  return 0;
801  }
802 
803  // add to path
804  pathx[npath] = stc % nx + dx;
805  pathy[npath] = stc / nx + dy;
806  npath++;
807 
808  bool oscillation_detected = false;
809  if (npath > 2 &&
810  pathx[npath - 1] == pathx[npath - 3] &&
811  pathy[npath - 1] == pathy[npath - 3])
812  {
813  RCLCPP_DEBUG(
814  rclcpp::get_logger("rclcpp"),
815  "[PathCalc] oscillation detected, attempting fix.");
816  oscillation_detected = true;
817  }
818 
819  int stcnx = stc + nx;
820  int stcpx = stc - nx;
821 
822  // check for potentials at eight positions near cell
823  if (potarr[stc] >= POT_HIGH ||
824  potarr[stc + 1] >= POT_HIGH ||
825  potarr[stc - 1] >= POT_HIGH ||
826  potarr[stcnx] >= POT_HIGH ||
827  potarr[stcnx + 1] >= POT_HIGH ||
828  potarr[stcnx - 1] >= POT_HIGH ||
829  potarr[stcpx] >= POT_HIGH ||
830  potarr[stcpx + 1] >= POT_HIGH ||
831  potarr[stcpx - 1] >= POT_HIGH ||
832  oscillation_detected)
833  {
834  RCLCPP_DEBUG(
835  rclcpp::get_logger("rclcpp"),
836  "[Path] Pot fn boundary, following grid (%0.1f/%d)", potarr[stc], npath);
837 
838  // check eight neighbors to find the lowest
839  int minc = stc;
840  int minp = potarr[stc];
841  int sti = stcpx - 1;
842  if (potarr[sti] < minp) {minp = potarr[sti]; minc = sti;}
843  sti++;
844  if (potarr[sti] < minp) {minp = potarr[sti]; minc = sti;}
845  sti++;
846  if (potarr[sti] < minp) {minp = potarr[sti]; minc = sti;}
847  sti = stc - 1;
848  if (potarr[sti] < minp) {minp = potarr[sti]; minc = sti;}
849  sti = stc + 1;
850  if (potarr[sti] < minp) {minp = potarr[sti]; minc = sti;}
851  sti = stcnx - 1;
852  if (potarr[sti] < minp) {minp = potarr[sti]; minc = sti;}
853  sti++;
854  if (potarr[sti] < minp) {minp = potarr[sti]; minc = sti;}
855  sti++;
856  if (potarr[sti] < minp) {minp = potarr[sti]; minc = sti;}
857  stc = minc;
858  dx = 0;
859  dy = 0;
860 
861  RCLCPP_DEBUG(
862  rclcpp::get_logger("rclcpp"), "[Path] Pot: %0.1f pos: %0.1f,%0.1f",
863  potarr[stc], pathx[npath - 1], pathy[npath - 1]);
864 
865  if (potarr[stc] >= POT_HIGH) {
866  RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[PathCalc] No path found, high potential");
867  // savemap("navfn_highpot");
868  return 0;
869  }
870  } else { // have a good gradient here
871  // get grad at four positions near cell
872  gradCell(stc);
873  gradCell(stc + 1);
874  gradCell(stcnx);
875  gradCell(stcnx + 1);
876 
877 
878  // get interpolated gradient
879  float x1 = (1.0 - dx) * gradx[stc] + dx * gradx[stc + 1];
880  float x2 = (1.0 - dx) * gradx[stcnx] + dx * gradx[stcnx + 1];
881  float x = (1.0 - dy) * x1 + dy * x2; // interpolated x
882  float y1 = (1.0 - dx) * grady[stc] + dx * grady[stc + 1];
883  float y2 = (1.0 - dx) * grady[stcnx] + dx * grady[stcnx + 1];
884  float y = (1.0 - dy) * y1 + dy * y2; // interpolated y
885 
886 #if 0
887  // show gradients
888  RCLCPP_DEBUG(
889  rclcpp::get_logger("rclcpp"),
890  "[Path] %0.2f,%0.2f %0.2f,%0.2f %0.2f,%0.2f %0.2f,%0.2f; final x=%.3f, y=%.3f\n",
891  gradx[stc], grady[stc], gradx[stc + 1], grady[stc + 1],
892  gradx[stcnx], grady[stcnx], gradx[stcnx + 1], grady[stcnx + 1],
893  x, y);
894 #endif
895 
896  // check for zero gradient, failed
897  if (x == 0.0 && y == 0.0) {
898  RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[PathCalc] Zero gradient");
899  return 0;
900  }
901 
902  // move in the right direction
903  float ss = pathStep / hypot(x, y);
904  dx += x * ss;
905  dy += y * ss;
906 
907  // check for overflow
908  if (dx > 1.0) {stc++; dx -= 1.0;}
909  if (dx < -1.0) {stc--; dx += 1.0;}
910  if (dy > 1.0) {stc += nx; dy -= 1.0;}
911  if (dy < -1.0) {stc -= nx; dy += 1.0;}
912  }
913 
914  // ROS_INFO("[Path] Pot: %0.1f grad: %0.1f,%0.1f pos: %0.1f,%0.1f\n",
915  // potarr[stc], x, y, pathx[npath-1], pathy[npath-1]);
916  }
917 
918  // return npath; // out of cycles, return failure
919  RCLCPP_WARN(
920  rclcpp::get_logger("rclcpp"),
921  "[PathCalc] Path extraction exhausted: npath = %d, max_cycles = %d",
922  npath, n);
923  // savemap("navfn_pathlong");
924  return 0; // out of cycles, return failure
925 }
926 
927 
928 //
929 // gradient calculations
930 //
931 
932 // calculate gradient at a cell
933 // positive value are to the right and down
934 float
936 {
937  if (gradx[n] + grady[n] > 0.0) { // check this cell
938  return 1.0;
939  }
940 
941  if (n < nx || n > ns - nx) { // would be out of bounds
942  return 0.0;
943  }
944 
945  float cv = potarr[n];
946  float dx = 0.0;
947  float dy = 0.0;
948 
949  // check for in an obstacle
950  if (cv >= POT_HIGH) {
951  if (potarr[n - 1] < POT_HIGH) {
952  dx = -COST_OBS;
953  } else if (potarr[n + 1] < POT_HIGH) {
954  dx = COST_OBS;
955  }
956  if (potarr[n - nx] < POT_HIGH) {
957  dy = -COST_OBS;
958  } else if (potarr[n + nx] < POT_HIGH) {
959  dy = COST_OBS;
960  }
961  } else { // not in an obstacle
962  // dx calc, average to sides
963  if (potarr[n - 1] < POT_HIGH) {
964  dx += potarr[n - 1] - cv;
965  }
966  if (potarr[n + 1] < POT_HIGH) {
967  dx += cv - potarr[n + 1];
968  }
969 
970  // dy calc, average to sides
971  if (potarr[n - nx] < POT_HIGH) {
972  dy += potarr[n - nx] - cv;
973  }
974  if (potarr[n + nx] < POT_HIGH) {
975  dy += cv - potarr[n + nx];
976  }
977  }
978 
979  // normalize
980  float norm = hypot(dx, dy);
981  if (norm > 0) {
982  norm = 1.0 / norm;
983  gradx[n] = norm * dx;
984  grady[n] = norm * dy;
985  }
986  return norm;
987 }
988 
989 
990 //
991 // display function setup
992 // <n> is the number of cycles to wait before displaying,
993 // use 0 to turn it off
994 
995 // void
996 // NavFn::display(void fn(NavFn * nav), int n)
997 // {
998 // displayFn = fn;
999 // displayInt = n;
1000 // }
1001 
1002 
1003 //
1004 // debug writes
1005 // saves costmap and start/goal
1006 //
1007 
1008 // void
1009 // NavFn::savemap(const char * fname)
1010 // {
1011 // char fn[4096];
1012 
1013 // RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[NavFn] Saving costmap and start/goal points");
1014 // // write start and goal points
1015 // snprintf(fn, sizeof(fn), "%s.txt", fname);
1016 // FILE * fp = fopen(fn, "w");
1017 // if (!fp) {
1018 // RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Can't open file %s", fn);
1019 // return;
1020 // }
1021 // fprintf(fp, "Goal: %d %d\nStart: %d %d\n", goal[0], goal[1], start[0], start[1]);
1022 // fclose(fp);
1023 
1024 // // write cost array
1025 // if (!costarr) {
1026 // return;
1027 // }
1028 // snprintf(fn, sizeof(fn), "%s.pgm", fname);
1029 // fp = fopen(fn, "wb");
1030 // if (!fp) {
1031 // RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Can't open file %s", fn);
1032 // return;
1033 // }
1034 // fprintf(fp, "P5\n%d\n%d\n%d\n", nx, ny, 0xff);
1035 // fwrite(costarr, 1, nx * ny, fp);
1036 // fclose(fp);
1037 // }
1038 
1039 } // namespace nav2_navfn_planner
int getPathLen()
Accessor for the length of a path.
Definition: navfn.cpp:322
float * getPathX()
Accessor for the x-coordinates of a path.
Definition: navfn.cpp:320
bool calcNavFnAstar(std::function< bool()> cancelChecker)
Calculates a plan using the A* heuristic, returns true if one is found.
Definition: navfn.cpp:308
float gradCell(int n)
Calculate gradient at a cell.
Definition: navfn.cpp:935
void updateCell(int n)
Updates the cell at index n.
Definition: navfn.cpp:419
bool propNavFnDijkstra(int cycles, std::function< bool()> cancelChecker, bool atStart=false)
Run propagation for <cycles> iterations, or until start is reached using breadth-first Dijkstra metho...
Definition: navfn.cpp:572
int calcPath(int n, int *st=NULL)
Calculates the path for at most <n> cycles.
Definition: navfn.cpp:760
float getLastPathCost()
Gets the cost of the path found the last time a navigation function was computed.
Definition: navfn.cpp:742
bool calcNavFnDijkstra(std::function< bool()> cancelChecker, bool atStart=false)
Calculates the full navigation function using Dijkstra.
Definition: navfn.cpp:294
bool propNavFnAstar(int cycles, std::function< bool()> cancelChecker)
Run propagation for <cycles> iterations, or until start is reached using the best-first A* method wit...
Definition: navfn.cpp:657
void updateCellAstar(int n)
Updates the cell at index n using the A* heuristic.
Definition: navfn.cpp:493
void initCost(int k, float v)
Initialize cell k with cost v for propagation.
Definition: navfn.cpp:398
void setNavArr(int nx, int ny)
Sets or resets the size of the map.
Definition: navfn.cpp:204
NavFn(int nx, int ny)
Constructs the planner.
Definition: navfn.cpp:109
void setCostmap(const COSTTYPE *cmap, bool isROS=true, bool allow_unknown=true)
Set up the cost array for the planner, usually from ROS.
Definition: navfn.cpp:244
void setupNavFn(bool keepit=false)
Set up navigation potential arrays for new propagation.
Definition: navfn.cpp:339
void setGoal(int *goal)
Sets the goal position for the planner. Note: the navigation cost field computed gives the cost to ge...
Definition: navfn.cpp:182
float * getPathY()
Accessor for the y-coordinates of a path.
Definition: navfn.cpp:321
void setStart(int *start)
Sets the start position for the planner. Note: the navigation cost field computed gives the cost to g...
Definition: navfn.cpp:190