global_planner
nav_core의 base_global_planner의 plugin이다. class 정의를 살펴보면 다음과 같다.
class GlobalPlanner : public nav_core::BaseGlobalPlanner {
public:
GlobalPlanner();
GlobalPlanner(std::string name, costmap_2d::Costmap2D* costmap, std::string frame_id);
~GlobalPlanner();
void initialize(std::string name, costmap_2d::Costmap2DROS* costmap_ros);
void initialize(std::string name, costmap_2d::Costmap2D* costmap, std::string frame_id);
bool makePlan(const geometry_msgs::PoseStamped& start, const geometry_msgs::PoseStamped& goal,
std::vector<geometry_msgs::PoseStamped>& plan);
bool makePlan(const geometry_msgs::PoseStamped& start, const geometry_msgs::PoseStamped& goal, double tolerance,
std::vector<geometry_msgs::PoseStamped>& plan);
bool computePotential(const geometry_msgs::Point& world_point);
bool getPlanFromPotential(double start_x, double start_y, double end_x, double end_y,
const geometry_msgs::PoseStamped& goal,
std::vector<geometry_msgs::PoseStamped>& plan);
double getPointPotential(const geometry_msgs::Point& world_point);
bool validPointPotential(const geometry_msgs::Point& world_point);
bool validPointPotential(const geometry_msgs::Point& world_point, double tolerance);
void publishPlan(const std::vector<geometry_msgs::PoseStamped>& path);
bool makePlanService(nav_msgs::GetPlan::Request& req, nav_msgs::GetPlan::Response& resp);
protected:
costmap_2d::Costmap2D* costmap_;
std::string frame_id_;
ros::Publisher plan_pub_;
bool initialized_, allow_unknown_;
private:
void mapToWorld(double mx, double my, double& wx, double& wy);
bool worldToMap(double wx, double wy, double& mx, double& my);
void clearRobotCell(const geometry_msgs::PoseStamped& global_pose, unsigned int mx, unsigned int my);
void publishPotential(float* potential);
double planner_window_x_, planner_window_y_, default_tolerance_;
boost::mutex mutex_;
ros::ServiceServer make_plan_srv_;
PotentialCalculator* p_calc_;
Expander* planner_;
Traceback* path_maker_;
OrientationFilter* orientation_filter_;
bool publish_potential_;
ros::Publisher potential_pub_;
int publish_scale_;
void outlineMap(unsigned char* costarr, int nx, int ny, unsigned char value);
float* potential_array_;
unsigned int start_x_, start_y_, end_x_, end_y_;
bool old_navfn_behavior_;
float convert_offset_;
bool outline_map_;
dynamic_reconfigure::Server<global_planner::GlobalPlannerConfig> *dsrv_;
void reconfigureCB(global_planner::GlobalPlannerConfig &config, uint32_t level);
};
여기서 봐야 중점으로 봐야하는건 makePlan을 할 때와, initialize에서 어떤 걸 하는지 중점으로 확인한다.
GlobalPlanner(std::string name, costmap_2d::Costmap2D* costmap, std::string frame_id);
initialize에 필요한 인자들을 받는 constructor이다. 받은뒤 initialize를 호출한다.
void GlobalPlanner::initialize(std::string name, costmap_2d::Costmap2DROS *costmap_ros)
frameId를 costmap의 frameId로 사용하는 initialize방법이다. costmap에서 frameId를 가져온다.
void GlobalPlanner::initialize(std::string name, costmap_2d::Costmap2D *costmap, std::string frame_id)
initialize를 하는 함수이다.
- initialize flow
- if (!initialized_)
- initialize는 한번만 필요하다.
- unsigned int cx = costmap->getSizeInCellsX(), cy = costmap->getSizeInCellsY()
- costmap의 Cellsize를 가져온다.
- if (!old_navfn_behavior_)
- old_navfn_behavior_ parameter에 따라 convert_offset_값을 설정한다.
- if (use_quadratic)
- use_quadratic 에 따라 Calculator 종류를 설정한다.
- if (use_dijkstra)
- use_dijkstra가 true이면 dijkstar expansion(planner)를 생성한다.
- false이면 A* expansion(planner)를 생성한다.
- if (use_grid_path)
- use_grid_path가 true면 GridPath로 path_maker를 설정한다.
- false이면 GradientPath로 path_maker를 설정한다.
- orientation_filter_ = new OrientationFilter()
- orientation filter 생성
- 나머지는 자잘한 설정들이다. (보면 안다.)
- if (!initialized_)
bool GlobalPlanner::makePlan(const geometry_msgs::PoseStamped &start, const geometry_msgs::PoseStamped &goal,
std::vector<geometry_msgs::PoseStamped> &plan)
기존 base_global_planner에서 정의된 인터페이스로, tolerance가 정의 되어 있지 않아 Default tolerance로 makePlane을 하게 된다.
bool GlobalPlanner::makePlan(const geometry_msgs::PoseStamped &start, const geometry_msgs::PoseStamped &goal,
double tolerance, std::vector<geometry_msgs::PoseStamped> &plan)
plan을 하기 위한 준비를 하고, getPlanFromPotential을 호출하는 함수이다.
- make plan flow
- boost::mutex::scoped_lock lock(mutex_)
- plan을 하는 동안 변수들을 건들게 되니까 mutex를 잠그고 시작한다.
- if (!initialized_)
- planner가 initialized됐는지 확인한다.
- plan.clear()
- plan을 초기화 한다.(vector 초기화)
- if (goal.header.frame_id != global_frame)
- goal의 좌표가 global_frame에 기준한 좌표로 들어왔는지 확인한다.
이건 여기서 알아서 처리해도 좋을거 같은데...
- goal의 좌표가 global_frame에 기준한 좌표로 들어왔는지 확인한다.
- if (start.header.frame_id != global_frame)
- start의 좌표가 global_frame에 기준한 좌표로 들어왔는지 확인한다.
이것도 마찬가지...
- start의 좌표가 global_frame에 기준한 좌표로 들어왔는지 확인한다.
- if (!costmap_->worldToMap(wx, wy, start_x_i, start_y_i))
- meter 단위로 들어온 x, y 좌표를 costmap의 pixel좌표로 transform한다.
- if (!costmap_->worldToMap(wx, wy, goal_x_i, goal_y_i))
- goal에대해서도 똑같이 해준다.
- clearRobotCell(start, start_x_i, start_y_i)
- 로봇이 있는곳은 obstacle이 아니니까 cell을 초기화 해준다.
- int nx = costmap_->getSizeInCellsX(), ny = costmap_->getSizeInCellsY()
- costmap 크기를 가져온다.
- p_calc_->setSize(nx, ny); planner_->setSize(nx, ny); path_maker_->setSize(nx, ny); potential_array_ = new float[nx * ny]
- plan에 필요한 사이즈를 초기화 한다.
- if (outline_map_)
- 맵에 테두리를 그리는 옵션이 들어가면 테두리를 그린다.
- bool found_legal = planner_->calculatePotentials(costmap_->getCharMap(), start_x, start_y, goal_x, goal_y, nx * ny * 2, potential_array_)
- A* 또는 Dijkstar로 goal point까지의 potential을 계산한다.
- if (!old_navfn_behavior_)
- goal point의 Cell을 초기화 한다.
- if (publish_potential_)
- potential map을 publish한다.
- if (found_legal)
- 위에 조건들이 부합하면 planner에 plan을 요청하는 함수로 책임을 위임한다.
- orientation_filter_->processPath(start, plan)
- orientation filter로 로봇이 plan을 따라갈 때의 orientation을 수정한다.
- publishPlan(plan)
- visualization을 위해 plan을 publish한다.
- boost::mutex::scoped_lock lock(mutex_)
bool GlobalPlanner::getPlanFromPotential(double start_x, double start_y, double goal_x, double goal_y,
const geometry_msgs::PoseStamped &goal, std::vector<geometry_msgs::PoseStamped> &plan)
expander(planner)를 호출하는 함수이다.
- get plan from potential flow
- if (!initialized_)
- planner가 initialized됐는지 확인한다.
- plan.clear()
- plan을 초기화 한다.
- 앞에서 많이 초기화 하고 주는데 혹시 몰라서 또 해주는 거 같다.
- path_maker_->getPath(potential_array_, start_x, start_y, goal_x, goal_y, path)
- path를 생성한다.
- for (int i = path.size() - 1; i >= 0; i--)
- 생성된 path들을 pixel단위에서 meter단위로 변경한다.
- if (!initialized_)
Expander
global planner에선 실질적으로 meter단위에서 pixel단위로 변환한뒤 Expender가 start point부터 end point까지의 potential을 생성하고, traceback이 potential을 기반으로 path를 만들어내게 된다.
expander의 class interface는 다음과 같다.
class Expander {
public:
Expander(PotentialCalculator* p_calc, int nx, int ny) :
unknown_(true), lethal_cost_(253), neutral_cost_(50), factor_(3.0), p_calc_(p_calc) {
setSize(nx, ny);
}
virtual ~Expander() {}
virtual bool calculatePotentials(unsigned char* costs, double start_x, double start_y, double end_x, double end_y,
int cycles, float* potential) = 0;
virtual void setSize(int nx, int ny) {
nx_ = nx;
ny_ = ny;
ns_ = nx * ny;
}
void setLethalCost(unsigned char lethal_cost) {
lethal_cost_ = lethal_cost;
}
void setNeutralCost(unsigned char neutral_cost) {
neutral_cost_ = neutral_cost;
}
void setFactor(float factor) {
factor_ = factor;
}
void setHasUnknown(bool unknown) {
unknown_ = unknown;
}
void clearEndpoint(unsigned char* costs, float* potential, int gx, int gy, int s){
int startCell = toIndex(gx, gy);
for(int i=-s;i<=s;i++){
for(int j=-s;j<=s;j++){
int n = startCell+i+nx_*j;
if(potential[n]<POT_HIGH)
continue;
float c = costs[n]+neutral_cost_;
float pot = p_calc_->calculatePotential(potential, c, n);
potential[n] = pot;
}
}
}
protected:
inline int toIndex(int x, int y) {
return x + nx_ * y;
}
int nx_, ny_, ns_; /**< size of grid, in pixels */
bool unknown_;
unsigned char lethal_cost_, neutral_cost_;
int cells_visited_;
float factor_;
PotentialCalculator* p_calc_;
};
- virtual bool calculatePotentials(unsigned char* costs, double start_x, double start_y, double end_x, double end_y, int cycles, float* potential)
- start부터 goal까지의 potential을 계산한다.
- virtual void setSize(int nx, int ny)
- map 크기를 설정한다.
- void clearEndpoint(unsigned char* costs, float* potential, int gx, int gy, int s)
- goal point를 clear한다.
- inline int toIndex(int x, int y)
- potential map에서 x,y의 index를 얻는 함수이다.(2차원 map → 1차원)
Expander는 2개의 class로 정의 되어있다.(Dijkstar , A*) 다음과 같다.
Dijkstar(Expander)
- start에서 goal을 찾아갈 때 Dijkstar algorithm을 사용한다.
- 내부 구현은 Dijkstar 그대로이다.
A* (Expander)
- start에서 goal을 찾아갈 때 A* algorithm을 사용한다.
- 내부 구현은 A* 그대로다.
Traceback
global planner에선 실질적으로 meter단위에서 pixel단위로 변환, Expander의 potential 생성을 하고 실질적인 path를 생성하는건 Traceback이 담당하게 된다. traceback의 class interface는 다음과 같다.
class Traceback {
public:
Traceback(PotentialCalculator* p_calc) : p_calc_(p_calc) {}
virtual ~Traceback() {}
virtual bool getPath(float* potential, double start_x, double start_y, double end_x, double end_y, std::vector<std::pair<float, float> >& path) = 0;
virtual void setSize(int xs, int ys) {
xs_ = xs;
ys_ = ys;
}
inline int getIndex(int x, int y) {
return x + y * xs_;
}
void setLethalCost(unsigned char lethal_cost) {
lethal_cost_ = lethal_cost;
}
protected:
int xs_, ys_;
unsigned char lethal_cost_;
PotentialCalculator* p_calc_;
}
- p_calc
- Potential을 계산할 때 사용하는 potential calcuator이다.
- getPath(float* potential, double start_x, double start_y, double end_x, double end_y, std::vector<std::pair<float, float> >& path)
- potential map이 주어졌을 때, path를 생성하는 함수이다.
- setSize(int xs, int ys)
- path를 찾기 위한 map의 크기를 세팅한다.
- getIndex(int x, int y)
- potential map에서 x,y의 index를 얻는 함수이다.(2차원 map → 1차원)
- setLethalCost(unsigned char lethal_cost)
- lethalcost를 설정한다.
GridPath(Traceback)
cell 단위로 grid한 path가 나오는 traceback이다. goal point에서 start로 역으로 오면서 path를 그리게 된다.
bool GridPath::getPath(float* potential, double start_x, double start_y, double end_x, double end_y, std::vector<std::pair<float, float> >& path)
- 최소 값을 찾으며 역행해서 해당하는 cell index를 path에 넣어준다.
GradientPath(Traceback)
path가 연속적으로 (스무스하게)나오는 trace back이다. GridPath와 마찬가지로 goal point에서 start로 ㅇ역으로 오면서 path를 그린다.
bool GradientPath::getPath(float* potential, double start_x, double start_y, double goal_x, double goal_y, std::vector<std::pair<float, float> >& path)
- 최소 값을 찾으며 역행하여 path를 생성한다.
'ROS' 카테고리의 다른 글
[ROS2] Install (Ubuntu 20.04, foxy) (0) | 2022.03.30 |
---|---|
[ROS] Move Base (6) Local Planner 개념 (0) | 2022.03.16 |
[ROS] Move Base (4) Global Planner 개념 (0) | 2022.02.21 |
[ROS] Move Base (3) Move Base Code 분석 (0) | 2022.02.21 |
[ROS] Move Base (2) Velocity & Acceleration (0) | 2022.02.18 |