navfn规划器源码介绍
代码navfn.cpp(自己参考的,备份)
// Copyright (c) 2008, Willow Garage, Inc.
// All rights reserved.
//
// Software License Agreement (BSD License 2.0)
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of the Willow Garage nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Navigation function computation
// Uses Dijkstra's method
// Modified for Euclidean-distance computation
//
// Path calculation uses no interpolation when pot field is at max in
// nearby cells
//
// Path calc has sanity check that it succeeded
//
#include "nav2_navfn_planner/navfn.hpp"
#include <algorithm>
#include "rclcpp/rclcpp.hpp"
namespace nav2_navfn_planner
{
//
// function to perform nav fn calculation
// keeps track of internal buffers, will be more efficient
// if the size of the environment does not change
//
// Example usage:
/*
int
create_nav_plan_astar(
COSTTYPE * costmap, int nx, int ny,
int * goal, int * start,
float * plan, int nplan)
{
static NavFn * nav = NULL;
if (nav == NULL) {
nav = new NavFn(nx, ny);
}
if (nav->nx != nx || nav->ny != ny) { // check for compatibility with previous call
delete nav;
nav = new NavFn(nx, ny);
}
nav->setGoal(goal);
nav->setStart(start);
nav->costarr = costmap;
nav->setupNavFn(true);
// calculate the nav fn and path
nav->priInc = 2 * COST_NEUTRAL;
nav->propNavFnAstar(std::max(nx * ny / 20, nx + ny));
// path
int len = nav->calcPath(nplan);
if (len > 0) { // found plan
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[NavFn] Path found, %d steps\n", len);
} else {
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[NavFn] No path found\n");
}
if (len > 0) {
for (int i = 0; i < len; i++) {
plan[i * 2] = nav->pathx[i];
plan[i * 2 + 1] = nav->pathy[i];
}
}
return len;
}
*/
//
// create nav fn buffers
//
NavFn::NavFn(int xs, int ys)
{
// create cell arrays
costarr = NULL;
potarr = NULL;
pending = NULL;
gradx = grady = NULL;
setNavArr(xs, ys);
// priority buffers
pb1 = new int[PRIORITYBUFSIZE];
pb2 = new int[PRIORITYBUFSIZE];
pb3 = new int[PRIORITYBUFSIZE];
// for Dijkstra (breadth-first), set to COST_NEUTRAL
// for A* (best-first), set to COST_NEUTRAL
priInc = 2 * COST_NEUTRAL;
// goal and start
goal[0] = goal[1] = 0;
start[0] = start[1] = 0;
// display function
// displayFn = NULL;
// displayInt = 0;
// path buffers
npathbuf = npath = 0;
pathx = pathy = NULL;
pathStep = 0.5;
}
NavFn::~NavFn()
{
if (costarr) {
delete[] costarr;
}
if (potarr) {
delete[] potarr;
}
if (pending) {
delete[] pending;
}
if (gradx) {
delete[] gradx;
}
if (grady) {
delete[] grady;
}
if (pathx) {
delete[] pathx;
}
if (pathy) {
delete[] pathy;
}
if (pb1) {
delete[] pb1;
}
if (pb2) {
delete[] pb2;
}
if (pb3) {
delete[] pb3;
}
}
//
// set goal, start positions for the nav fn
//
void
NavFn::setGoal(int * g)
{
goal[0] = g[0];
goal[1] = g[1];
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[NavFn] Setting goal to %d,%d\n", goal[0], goal[1]);
}
void
NavFn::setStart(int * g)
{
start[0] = g[0];
start[1] = g[1];
RCLCPP_DEBUG(
rclcpp::get_logger("rclcpp"), "[NavFn] Setting start to %d,%d\n", start[0],
start[1]);
}
//
// Set/Reset map size
//
void
NavFn::setNavArr(int xs, int ys)
{
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[NavFn] Array is %d x %d\n", xs, ys);
nx = xs;
ny = ys;
ns = nx * ny;
if (costarr) {
delete[] costarr;
}
if (potarr) {
delete[] potarr;
}
if (pending) {
delete[] pending;
}
if (gradx) {
delete[] gradx;
}
if (grady) {
delete[] grady;
}
costarr = new COSTTYPE[ns]; // cost array, 2d config space
memset(costarr, 0, ns * sizeof(COSTTYPE));
potarr = new float[ns]; // navigation potential array
pending = new bool[ns];
memset(pending, 0, ns * sizeof(bool));
gradx = new float[ns];
grady = new float[ns];
}
//
// set up cost array, usually from ROS
//
//用指针指向数组costarr,COSTTYPE宏定义为unsigned char,即ROS中使用的地图Costmap2D中用于储存地图数据的成员类型。
void
NavFn::setCostmap(const COSTTYPE * cmap, bool isROS, bool allow_unknown)
{
COSTTYPE * cm = costarr;
if (isROS) { // ROS-type cost array
for (int i = 0; i < ny; i++) {
//k值记录二重迭代的次数
int k = i * nx;
//cmap指向costmap元素,cm指向costarr
for (int j = 0; j < nx; j++, k++, cmap++, cm++) {
// This transforms the incoming cost values:
// COST_OBS -> COST_OBS (incoming "lethal obstacle")
// COST_OBS_ROS -> COST_OBS (incoming "inscribed inflated obstacle")
// values in range 0 to 252 -> values from COST_NEUTRAL to COST_OBS_ROS.
//默认最小权重值为COST_NEUTRAL=50,最大权重值为COST_OBS=254
//注:最小权重值即行走单个free(无障碍物影响)栅格所付出的权重代价
//最大权重值即行走单个障碍物栅格所付出的权重代价
*cm = COST_OBS;
int v = *cmap;//cmap中储存这各个节点的权重值
//若当前cell的代价小于障碍物类型(253),实际上253是膨胀型障碍
if (v < COST_OBS_ROS) {
//重新将其赋值为50+cost地图上的障碍物值×比例0.8
v = COST_NEUTRAL + COST_FACTOR * v;
//若值>=COST_OBS(254,致命层障碍)
if (v >= COST_OBS) {
//统一设置为253,确保不要超出范围
v = COST_OBS - 1;
}
//赋值给当前全局规划要使用的地图costarr
*cm = v;
//若当前cell的值为COST_UNKNOWN_ROS(255),未知区域
} else if (v == COST_UNKNOWN_ROS && allow_unknown) {
v = COST_OBS - 1;
//统一设置为253
*cm = v;
}
}
}
} else { // not a ROS map, just a PGM
for (int i = 0; i < ny; i++) {
int k = i * nx;
for (int j = 0; j < nx; j++, k++, cmap++, cm++) {
*cm = COST_OBS;
if (i < 7 || i > ny - 8 || j < 7 || j > nx - 8) {
continue; // don't do borders
}
int v = *cmap;
if (v < COST_OBS_ROS) {
v = COST_NEUTRAL + COST_FACTOR * v;
if (v >= COST_OBS) {
v = COST_OBS - 1;
}
*cm = v;
} else if (v == COST_UNKNOWN_ROS) {
v = COST_OBS - 1;
*cm = v;
}
}
}
}
}
//这个函数内完成了整个路径计算的流程,顺序调用了几个子部分的函数。
bool
NavFn::calcNavFnDijkstra(bool atStart)
{
setupNavFn(true);//重新设置potential数组,计算每个栅格的最优代价
// calculate the nav fn and path
return propNavFnDijkstra(std::max(nx * ny / 20, nx + ny), atStart);
}
//
// calculate navigation function, given a costmap, goal, and start
//
bool
NavFn::calcNavFnAstar()
{
setupNavFn(true);
// calculate the nav fn and path
return propNavFnAstar(std::max(nx * ny / 20, nx + ny));
}
//
// returning values
//
float * NavFn::getPathX() {return pathx;}
float * NavFn::getPathY() {return pathy;}
int NavFn::getPathLen() {return npath;}
// inserting onto the priority blocks
#define push_cur(n) {if (n >= 0 && n < ns && !pending[n] && \
costarr[n] < COST_OBS && curPe < PRIORITYBUFSIZE) \
{curP[curPe++] = n; pending[n] = true;}}
#define push_next(n) {if (n >= 0 && n < ns && !pending[n] && \
costarr[n] < COST_OBS && nextPe < PRIORITYBUFSIZE) \
{nextP[nextPe++] = n; pending[n] = true;}}
#define push_over(n) {if (n >= 0 && n < ns && !pending[n] && \
costarr[n] < COST_OBS && overPe < PRIORITYBUFSIZE) \
{overP[overPe++] = n; pending[n] = true;}}
// Set up navigation potential arrays for new propagation
//该函数对“翻译”生成的costarr数组进行了边际设置等处理,并初始化了potarr数组和梯度数组gradx、grady。
//下面先循环初始化potarr矩阵元素全部为最大值POT_HIGH,并初始化梯度表初始值全部为0.0。
void
NavFn::setupNavFn(bool keepit)
{
// reset values in propagation arrays
//重新设置势场矩阵potarr的值
for (int i = 0; i < ns; i++) {
//将代价值表初始化为最大值,默认起点到所有点的行走代价值都为最大
potarr[i] = POT_HIGH;
if (!keepit) {
costarr[i] = COST_NEUTRAL;
}
gradx[i] = grady[i] = 0.0;
}
//设置costarr的四条边的cell的值为COST_OBS(致命层254),封闭地图四周,以防产生边界以外的轨迹。
// outer bounds of cost array
COSTTYPE * pc;
pc = costarr;
//costarr第一行全部设置为COST_OBS(致命层254)
for (int i = 0; i < nx; i++) {
*pc++ = COST_OBS;
}
//costarr最后一行全部设置为COST_OBS(致命层254)
pc = costarr + (ny - 1) * nx;
for (int i = 0; i < nx; i++) {
*pc++ = COST_OBS;
}
//costarr第一列全部设置为COST_OBS(致命层254)
pc = costarr;
for (int i = 0; i < ny; i++, pc += nx) {
*pc = COST_OBS;
}
//costarr最后一列全部设置为COST_OBS(致命层254)
pc = costarr + nx - 1;
for (int i = 0; i < ny; i++, pc += nx) {
*pc = COST_OBS;
}
// priority buffers
curT = COST_OBS;//当前传播界限
curP = pb1;//当前用于传播的节点索引数组
curPe = 0;//当前用于传播的节点的数量
nextP = pb2;//用于下个传播过程的节点索引数组
nextPe = 0;//用于下个传播过程的节点的数量
overP = pb3;//传播界限外的节点索引数组
overPe = 0;//传播界限外的节点的数量
memset(pending, 0, ns * sizeof(bool));//设置所有的节点状态都为非"等待状态"
// set goal
int k = goal[0] + goal[1] * nx;
//初始化代价:将起始节点所在的potential值(行走代价值)置为0,并且将上下左右四个栅格节点放入curP,
//将状态置为"等待状态"
initCost(k, 0);
// find # of obstacle cells
pc = costarr;
int ntot = 0;
for (int i = 0; i < ns; i++, pc++) {
if (*pc >= COST_OBS) {
ntot++; // number of cells that are obstacles
}
}
nobs = ntot;
}
// initialize a goal-type cost for starting propagation
void
NavFn::initCost(int k, float v)
{
potarr[k] = v;
push_cur(k + 1);
push_cur(k - 1);
push_cur(k - nx);
push_cur(k + nx);
}
//
// Critical function: calculate updated potential value of a cell,
// given its neighbors' values
// Planar-wave update calculation from two lowest neighbors in a 4-grid
// Quadratic approximation to the interpolated value
// No checking of bounds here, this function should be fast
//
#define INVSQRT2 0.707106781
inline void
NavFn::updateCell(int n)
{
// get neighbors
float u, d, l, r;
l = potarr[n - 1];
r = potarr[n + 1];
u = potarr[n - nx];
d = potarr[n + nx];
// ROS_INFO("[Update] c: %0.1f l: %0.1f r: %0.1f u: %0.1f d: %0.1f\n",
// potarr[n], l, r, u, d);
// ROS_INFO("[Update] cost: %d\n", costarr[n]);
// find lowest, and its lowest neighbor
// 寻找上下和左右两组行走代价值的最低值
float ta, tc;
if (l < r) {tc = l;} else {tc = r;}
if (u < d) {ta = u;} else {ta = d;}
// 当权重图显示当前节点为障碍物时,不继续向此节点传播,意味着此节点的最优行走代价值为最大默认值,
// 即保证了轨迹规划不穿过障碍物
// do planar wave update
if (costarr[n] < COST_OBS) { // don't propagate into obstacles
float hf = static_cast<float>(costarr[n]); // traversability factor
float dc = tc - ta; // relative cost between ta,tc
if (dc < 0) { // ta is lowest
dc = -dc;
ta = tc;
}
// calculate new potential
float pot;
if (dc >= hf) { // if too large, use ta-only update
pot = ta + hf;
} else { // two-neighbor interpolation update
// use quadratic approximation
// might speed this up through table lookup, but still have to
// do the divide
float d = dc / hf;
float v = -0.2301 * d * d + 0.5307 * d + 0.7040;
pot = ta + hf * v;
}
// ROS_INFO("[Update] new pot: %d\n", costarr[n]);
// now add affected neighbors to priority blocks
//判断当前节点计算的potential值是否小于本身的potential值,只有当小于时,才更新当前栅格节点的potential值
if (pot < potarr[n]) {
// 计算当前节点上下左右四个节点的权重值
float le = INVSQRT2 * static_cast<float>(costarr[n - 1]);
float re = INVSQRT2 * static_cast<float>(costarr[n + 1]);
float ue = INVSQRT2 * static_cast<float>(costarr[n - nx]);
float de = INVSQRT2 * static_cast<float>(costarr[n + nx]);
potarr[n] = pot;
// 如果上下左右四个节点的权重值加上当前节点的potential值小于这四个节点的potential值时,才加入nexP数组进
//行这四个节点potential值的更新,否则没有必要更新
if (pot < curT) { // low-cost buffer block
if (l > pot + le) {push_next(n - 1);}
if (r > pot + re) {push_next(n + 1);}
if (u > pot + ue) {push_next(n - nx);}
if (d > pot + de) {push_next(n + nx);}
} else { // overflow block否则传递给overP
if (l > pot + le) {push_over(n - 1);}
if (r > pot + re) {push_over(n + 1);}
if (u > pot + ue) {push_over(n - nx);}
if (d > pot + de) {push_over(n + nx);}
}
}
}
}
//
// Use A* method for setting priorities
// Critical function: calculate updated potential value of a cell,
// given its neighbors' values
// Planar-wave update calculation from two lowest neighbors in a 4-grid
// Quadratic approximation to the interpolated value
// No checking of bounds here, this function should be fast
//
#define INVSQRT2 0.707106781
inline void
NavFn::updateCellAstar(int n)
{
// get neighbors
float u, d, l, r;
l = potarr[n - 1];
r = potarr[n + 1];
u = potarr[n - nx];
d = potarr[n + nx];
// ROS_INFO("[Update] c: %0.1f l: %0.1f r: %0.1f u: %0.1f d: %0.1f\n",
// potarr[n], l, r, u, d);
// ROS_INFO("[Update] cost of %d: %d\n", n, costarr[n]);
// find lowest, and its lowest neighbor
float ta, tc;
if (l < r) {tc = l;} else {tc = r;}
if (u < d) {ta = u;} else {ta = d;}
// do planar wave update
if (costarr[n] < COST_OBS) { // don't propagate into obstacles
float hf = static_cast<float>(costarr[n]); // traversability factor
float dc = tc - ta; // relative cost between ta,tc
if (dc < 0) { // ta is lowest
dc = -dc;
ta = tc;
}
// calculate new potential
float pot;
if (dc >= hf) { // if too large, use ta-only update
pot = ta + hf;
} else { // two-neighbor interpolation update
// use quadratic approximation
// might speed this up through table lookup, but still have to
// do the divide
float d = dc / hf;
float v = -0.2301 * d * d + 0.5307 * d + 0.7040;
pot = ta + hf * v;
}
// ROS_INFO("[Update] new pot: %d\n", costarr[n]);
// now add affected neighbors to priority blocks
if (pot < potarr[n]) {
float le = INVSQRT2 * static_cast<float>(costarr[n - 1]);
float re = INVSQRT2 * static_cast<float>(costarr[n + 1]);
float ue = INVSQRT2 * static_cast<float>(costarr[n - nx]);
float de = INVSQRT2 * static_cast<float>(costarr[n + nx]);
// calculate distance
int x = n % nx;
int y = n / nx;
float dist = hypot(x - start[0], y - start[1]) * static_cast<float>(COST_NEUTRAL);
potarr[n] = pot;
pot += dist;
if (pot < curT) { // low-cost buffer block
if (l > pot + le) {push_next(n - 1);}
if (r > pot + re) {push_next(n + 1);}
if (u > pot + ue) {push_next(n - nx);}
if (d > pot + de) {push_next(n + nx);}
} else {
if (l > pot + le) {push_over(n - 1);}
if (r > pot + re) {push_over(n + 1);}
if (u > pot + ue) {push_over(n - nx);}
if (d > pot + de) {push_over(n + nx);}
}
}
}
}
//
// main propagation function
// Dijkstra method, breadth-first
// runs for a specified number of cycles,
// or until it runs out of cells to update,
// or until the Start cell is found (atStart = true)
//
//这个函数以目标点(Potential值已初始化为0)为起点,向整张地图的cell传播,填充potarr数组,直到找到起始点为止,
//potarr数组的数据能够反映“走了多远”和“附近的障碍情况”,为最后的路径计算提供了依据。
bool
NavFn::propNavFnDijkstra(int cycles, bool atStart)
{
int nwv = 0; // max priority block size
int nc = 0; // number of cells put into priority blocks
int cycle = 0; // which cycle we're on
// set up start cell
int startCell = start[1] * nx + start[0];
//此处cycles大小不用太纠结,设置足够大就行,能保证传播到目标点,到了目标点之后,会自动break掉
for (; cycle < cycles; cycle++) { // go for this many cycles, unless interrupted
if (curPe == 0 && nextPe == 0) { // priority blocks empty
break;
//循环迭代更新potarr,判断条件:如果当前正在传播和下一步传播的集都为空,那么说明已经无法继续传播,可能有无法越过的障碍或其他情况,退出。
}
// stats
nc += curPe;
if (curPe > nwv) {
nwv = curPe;
}
// reset pending flags on current priority buffer
// 传播当前节点,并将当前节点上下左右四个节点保存到nextP数组当中,当当前节点传播完之后,将nextP数组中的
// 节点数组传递给当前节点数组curP,又继续开始传播当前节点,循环往复
int * pb = curP;
int i = curPe;
while (i-- > 0) {
pending[*(pb++)] = false;
}
// process current priority buffer
pb = curP;
i = curPe;
while (i-- > 0) {
updateCell(*pb++);
}
// if (displayInt > 0 && (cycle % displayInt) == 0) {
// displayFn(this);
// }
// swap priority blocks curP <=> nextP
curPe = nextPe;
nextPe = 0;
pb = curP; // swap buffers
curP = nextP;
nextP = pb;
// see if we're done with this priority level
if (curPe == 0) {
curT += priInc; // increment priority threshold
curPe = overPe; // set current to overflow block
overPe = 0;
pb = curP; // swap buffers
curP = overP;
overP = pb;
}
//在从目标点向全地图传播的过程中检查,当起点的Potential值不再是被初始化的无穷大,而是有一个实际的值时,说明到达了起点,传播停止。
// check if we've hit the Start cell
if (atStart) {
if (potarr[startCell] < POT_HIGH) {
break;
}
}
}
RCLCPP_DEBUG(
rclcpp::get_logger("rclcpp"),
"[NavFn] Used %d cycles, %d cells visited (%d%%), priority buf max %d\n",
cycle, nc, (int)((nc * 100.0) / (ns - nobs)), nwv);
return (cycle < cycles) ? true : false;
}
//
// main propagation function
// A* method, best-first
// uses Euclidean distance heuristic
// runs for a specified number of cycles,
// or until it runs out of cells to update,
// or until the Start cell is found (atStart = true)
//
bool
NavFn::propNavFnAstar(int cycles)
{
int nwv = 0; // max priority block size
int nc = 0; // number of cells put into priority blocks
int cycle = 0; // which cycle we're on
// set initial threshold, based on distance
float dist = hypot(goal[0] - start[0], goal[1] - start[1]) * static_cast<float>(COST_NEUTRAL);//使用的欧拉距离来作为A*算法的启发函数,计算斜边的距离
curT = dist + curT;//将距离因素考虑进去
// set up start cell
int startCell = start[1] * nx + start[0];
// do main cycle
for (; cycle < cycles; cycle++) { // go for this many cycles, unless interrupted
if (curPe == 0 && nextPe == 0) { // priority blocks empty
break;
}
// stats
nc += curPe;
if (curPe > nwv) {
nwv = curPe;
}
// reset pending flags on current priority buffer
int * pb = curP;
int i = curPe;
while (i-- > 0) {
pending[*(pb++)] = false;
}
// process current priority buffer
pb = curP;
i = curPe;
while (i-- > 0) {
updateCellAstar(*pb++);
}
// if (displayInt > 0 && (cycle % displayInt) == 0) {
// displayFn(this);
// }
// swap priority blocks curP <=> nextP
curPe = nextPe;
nextPe = 0;
pb = curP; // swap buffers
curP = nextP;
nextP = pb;
// see if we're done with this priority level
if (curPe == 0) {
curT += priInc; // increment priority threshold
curPe = overPe; // set current to overflow block
overPe = 0;
pb = curP; // swap buffers
curP = overP;
overP = pb;
}
// check if we've hit the Start cell
if (potarr[startCell] < POT_HIGH) {
break;
}
}
last_path_cost_ = potarr[startCell];
RCLCPP_DEBUG(
rclcpp::get_logger("rclcpp"),
"[NavFn] Used %d cycles, %d cells visited (%d%%), priority buf max %d\n",
cycle, nc, (int)((nc * 100.0) / (ns - nobs)), nwv);
if (potarr[startCell] < POT_HIGH) {
return true; // finished up here}
} else {
return false;
}
}
float NavFn::getLastPathCost()
{
return last_path_cost_;
}
//
// Path construction
// Find gradient at array points, interpolate path
// Use step size of pathStep, usually 0.5 pixel
//
// Some sanity checks:
// 1. Stuck at same index position
// 2. Doesn't get near goal
// 3. Surrounded by high potentials
//
// 通过potential的分布,从目标点开始沿着梯度下降的方向搜索到起点
int
NavFn::calcPath(int n, int * st)
{
// test write
// savemap("test");
// 初始化路径数组
// check path arrays
if (npathbuf < n) {
if (pathx) {delete[] pathx;}
if (pathy) {delete[] pathy;}
pathx = new float[n];
pathy = new float[n];
npathbuf = n;
}
// set up start position at cell
// st is always upper left corner for 4-point bilinear interpolation
if (st == NULL) {st = start;}
int stc = st[1] * nx + st[0];
// set up offset
float dx = 0;
float dy = 0;
npath = 0;
// go for <n> cycles at most
for (int i = 0; i < n; i++) {
// check if near goal
int nearest_point = std::max(
0,
std::min(
nx * ny - 1, stc + static_cast<int>(round(dx)) +
static_cast<int>(nx * round(dy))));
if (potarr[nearest_point] < COST_NEUTRAL) {
pathx[npath] = static_cast<float>(goal[0]);
pathy[npath] = static_cast<float>(goal[1]);
return ++npath; // done!
}
if (stc < nx || stc > ns - nx) { // would be out of bounds
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[PathCalc] Out of bounds");
return 0;
}
// add to path
pathx[npath] = stc % nx + dx;
pathy[npath] = stc / nx + dy;
npath++;
bool oscillation_detected = false;
if (npath > 2 &&
pathx[npath - 1] == pathx[npath - 3] &&
pathy[npath - 1] == pathy[npath - 3])
{
RCLCPP_DEBUG(
rclcpp::get_logger("rclcpp"),
"[PathCalc] oscillation detected, attempting fix.");
oscillation_detected = true;
}
int stcnx = stc + nx;
int stcpx = stc - nx;
// 检查当前到达节点的周边的8个节点是否有障碍物代价值,如果有的话,则直接将stc指向这8个节点中potential值最
// 低的节点
// check for potentials at eight positions near cell
if (potarr[stc] >= POT_HIGH ||
potarr[stc + 1] >= POT_HIGH ||
potarr[stc - 1] >= POT_HIGH ||
potarr[stcnx] >= POT_HIGH ||
potarr[stcnx + 1] >= POT_HIGH ||
potarr[stcnx - 1] >= POT_HIGH ||
potarr[stcpx] >= POT_HIGH ||
potarr[stcpx + 1] >= POT_HIGH ||
potarr[stcpx - 1] >= POT_HIGH ||
oscillation_detected)
{
RCLCPP_DEBUG(
rclcpp::get_logger("rclcpp"),
"[Path] Pot fn boundary, following grid (%0.1f/%d)", potarr[stc], npath);
// check eight neighbors to find the lowest
int minc = stc;
int minp = potarr[stc];
int st = stcpx - 1;
if (potarr[st] < minp) {minp = potarr[st]; minc = st;}
st++;
if (potarr[st] < minp) {minp = potarr[st]; minc = st;}
st++;
if (potarr[st] < minp) {minp = potarr[st]; minc = st;}
st = stc - 1;
if (potarr[st] < minp) {minp = potarr[st]; minc = st;}
st = stc + 1;
if (potarr[st] < minp) {minp = potarr[st]; minc = st;}
st = stcnx - 1;
if (potarr[st] < minp) {minp = potarr[st]; minc = st;}
st++;
if (potarr[st] < minp) {minp = potarr[st]; minc = st;}
st++;
if (potarr[st] < minp) {minp = potarr[st]; minc = st;}
stc = minc;
dx = 0;
dy = 0;
RCLCPP_DEBUG(
rclcpp::get_logger("rclcpp"), "[Path] Pot: %0.1f pos: %0.1f,%0.1f",
potarr[stc], pathx[npath - 1], pathy[npath - 1]);
if (potarr[stc] >= POT_HIGH) {
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[PathCalc] No path found, high potential");
// savemap("navfn_highpot");
return 0;
}
} else { // have a good gradient here
//如果有好的梯度,则直接计算梯度,并沿着梯度方向查找下一个节点
// get grad at four positions near cell
gradCell(stc);//该点
gradCell(stc + 1);//该点右侧点
gradCell(stcnx);//该点下方点
gradCell(stcnx + 1);//该点右下方点
// get interpolated gradient
float x1 = (1.0 - dx) * gradx[stc] + dx * gradx[stc + 1];
float x2 = (1.0 - dx) * gradx[stcnx] + dx * gradx[stcnx + 1];
float x = (1.0 - dy) * x1 + dy * x2; // interpolated x
float y1 = (1.0 - dx) * grady[stc] + dx * grady[stc + 1];
float y2 = (1.0 - dx) * grady[stcnx] + dx * grady[stcnx + 1];
float y = (1.0 - dy) * y1 + dy * y2; // interpolated y
#if 0
// show gradients
RCLCPP_DEBUG(
rclcpp::get_logger("rclcpp"),
"[Path] %0.2f,%0.2f %0.2f,%0.2f %0.2f,%0.2f %0.2f,%0.2f; final x=%.3f, y=%.3f\n",
gradx[stc], grady[stc], gradx[stc + 1], grady[stc + 1],
gradx[stcnx], grady[stcnx], gradx[stcnx + 1], grady[stcnx + 1],
x, y);
#endif
// check for zero gradient, failed
if (x == 0.0 && y == 0.0) {
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[PathCalc] Zero gradient");
return 0;
}
// move in the right direction
float ss = pathStep / hypot(x, y);
dx += x * ss;
dy += y * ss;
// check for overflow
if (dx > 1.0) {stc++; dx -= 1.0;}
if (dx < -1.0) {stc--; dx += 1.0;}
if (dy > 1.0) {stc += nx; dy -= 1.0;}
if (dy < -1.0) {stc -= nx; dy += 1.0;}
}
// ROS_INFO("[Path] Pot: %0.1f grad: %0.1f,%0.1f pos: %0.1f,%0.1f\n",
// potarr[stc], x, y, pathx[npath-1], pathy[npath-1]);
}
// return npath; // out of cycles, return failure
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[PathCalc] No path found, path too long");
// savemap("navfn_pathlong");
return 0; // out of cycles, return failure
}
//
// gradient calculations
//
// calculate gradient at a cell
// positive value are to the right and down
float
NavFn::gradCell(int n)
{
if (gradx[n] + grady[n] > 0.0) { // check this cell
return 1.0;
}
if (n < nx || n > ns - nx) { // would be out of bounds
return 0.0;
}
float cv = potarr[n];
float dx = 0.0;
float dy = 0.0;
// check for in an obstacle
if (cv >= POT_HIGH) {
if (potarr[n - 1] < POT_HIGH) {
dx = -COST_OBS;
} else if (potarr[n + 1] < POT_HIGH) {
dx = COST_OBS;
}
if (potarr[n - nx] < POT_HIGH) {
dy = -COST_OBS;
} else if (potarr[n + nx] < POT_HIGH) {
dy = COST_OBS;
}
} else { // not in an obstacle
// dx calc, average to sides
if (potarr[n - 1] < POT_HIGH) {
dx += potarr[n - 1] - cv;
}
if (potarr[n + 1] < POT_HIGH) {
dx += cv - potarr[n + 1];
}
// dy calc, average to sides
if (potarr[n - nx] < POT_HIGH) {
dy += potarr[n - nx] - cv;
}
if (potarr[n + nx] < POT_HIGH) {
dy += cv - potarr[n + nx];
}
}
// normalize
float norm = hypot(dx, dy);
if (norm > 0) {
norm = 1.0 / norm;
gradx[n] = norm * dx;
grady[n] = norm * dy;
}
return norm;
}
//
// display function setup
// <n> is the number of cycles to wait before displaying,
// use 0 to turn it off
// void
// NavFn::display(void fn(NavFn * nav), int n)
// {
// displayFn = fn;
// displayInt = n;
// }
//
// debug writes
// saves costmap and start/goal
//
// void
// NavFn::savemap(const char * fname)
// {
// char fn[4096];
// RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "[NavFn] Saving costmap and start/goal points");
// // write start and goal points
// snprintf(fn, sizeof(fn), "%s.txt", fname);
// FILE * fp = fopen(fn, "w");
// if (!fp) {
// RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Can't open file %s", fn);
// return;
// }
// fprintf(fp, "Goal: %d %d\nStart: %d %d\n", goal[0], goal[1], start[0], start[1]);
// fclose(fp);
// // write cost array
// if (!costarr) {
// return;
// }
// snprintf(fn, sizeof(fn), "%s.pgm", fname);
// fp = fopen(fn, "wb");
// if (!fp) {
// RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Can't open file %s", fn);
// return;
// }
// fprintf(fp, "P5\n%d\n%d\n%d\n", nx, ny, 0xff);
// fwrite(costarr, 1, nx * ny, fp);
// fclose(fp);
// }
} // namespace nav2_navfn_planner