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