赞
踩
// // Created by lihao on 19-7-9. // #ifndef OCCMAPTRANSFORM_H #define OCCMAPTRANSFORM_H #include <iostream> #include <cmath> #include <nav_msgs/OccupancyGrid.h> #include <tf/tf.h> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; //-------------------------------- Class---------------------------------// class OccupancyGridParam{ public: // Interface void GetOccupancyGridParam(nav_msgs::OccupancyGrid OccGrid); void Image2MapTransform(Point& src_point, Point2d& dst_point); void Map2ImageTransform(Point2d& src_point, Point& dst_point); private: // Private function public: // Public variable double resolution; int height; int width; // Origin pose double x; double y; double theta; private: // Private variable // Transform Mat R; Mat t; }; #endif //OCCMAPTRANSFORM_H
// // Created by lihao on 19-7-10. // #include "OccMapTransform.h" void OccupancyGridParam::GetOccupancyGridParam(nav_msgs::OccupancyGrid OccGrid) { // Get parameter resolution = OccGrid.info.resolution; height = OccGrid.info.height; width = OccGrid.info.width; x = OccGrid.info.origin.position.x; y = OccGrid.info.origin.position.y; double roll, pitch, yaw; geometry_msgs::Quaternion q = OccGrid.info.origin.orientation; tf::Quaternion quat(q.x, q.y, q.z, q.w); // x, y, z, w tf::Matrix3x3(quat).getRPY(roll, pitch, yaw); theta = yaw; // Calculate R, t R = Mat::zeros(2,2, CV_64FC1); R.at<double>(0, 0) = resolution * cos(theta); R.at<double>(0, 1) = resolution * sin(-theta); R.at<double>(1, 0) = resolution * sin(theta); R.at<double>(1, 1) = resolution * cos(theta); t = Mat(Vec2d(x, y), CV_64FC1); } void OccupancyGridParam::Image2MapTransform(Point& src_point, Point2d& dst_point) { // Upside down Mat P_src = Mat(Vec2d(src_point.x, height - 1 - src_point.y), CV_64FC1); // Rotate and translate Mat P_dst = R * P_src + t; dst_point.x = P_dst.at<double>(0, 0); dst_point.y = P_dst.at<double>(1, 0); } void OccupancyGridParam::Map2ImageTransform(Point2d& src_point, Point& dst_point) { Mat P_src = Mat(Vec2d(src_point.x, src_point.y), CV_64FC1); // Rotate and translate Mat P_dst = R.inv() * (P_src - t); // Upside down dst_point.x = round(P_dst.at<double>(0, 0)); dst_point.y = height - 1 - round(P_dst.at<double>(1, 0)); }
// // Created by lihao on 19-7-9. // #ifndef ASTAR_H #define ASTAR_H #include <iostream> #include <queue> #include <unordered_map> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; namespace pathplanning{ enum NodeType{ obstacle = 0, free, inOpenList, inCloseList }; struct Node{ Point point; // node coordinate int F, G, H; // cost Node* parent; // parent node Node(Point _point = Point(0, 0)):point(_point), F(0), G(0), H(0), parent(NULL) { } }; struct cmp { bool operator() (pair<int, Point> a, pair<int, Point> b) // Comparison function for priority queue { return a.first > b.first; // min heap } }; struct AstarConfig{ bool Euclidean; // true/false int OccupyThresh; // 0~255 int InflateRadius; // integer AstarConfig(bool _Euclidean = true, int _OccupyThresh = -1, int _InflateRadius = -1): Euclidean(_Euclidean), OccupyThresh(_OccupyThresh), InflateRadius(_InflateRadius) { } }; class Astar{ public: // Interface function void InitAstar(Mat& _Map, AstarConfig _config = AstarConfig()); void InitAstar(Mat& _Map, Mat& Mask, AstarConfig _config = AstarConfig()); void PathPlanning(Point _startPoint, Point _targetPoint, vector<Point>& path); void DrawPath(Mat& _Map, vector<Point>& path, InputArray Mask = noArray(), Scalar color = Scalar(0, 0, 255), int thickness = 1, Scalar maskcolor = Scalar(255, 255, 255)); inline int point2index(Point point) { return point.y * Map.cols + point.x; } inline Point index2point(int index) { return Point(int(index / Map.cols), index % Map.cols); } private: void MapProcess(Mat& Mask); Node* FindPath(); void GetPath(Node* TailNode, vector<Point>& path); private: //Object Mat Map; Point startPoint, targetPoint; Mat neighbor; Mat LabelMap; AstarConfig config; priority_queue<pair<int, Point>, vector<pair<int, Point>>, cmp> OpenList; // open list unordered_map<int, Node*> OpenDict; // open dict vector<Node*> PathList; // path list }; } #endif //ASTAR_H
// // Created by lihao on 19-7-9. // #include "Astar.h" namespace pathplanning{ void Astar::InitAstar(Mat& _Map, AstarConfig _config) { Mat Mask; InitAstar(_Map, Mask, _config); } void Astar::InitAstar(Mat& _Map, Mat& Mask, AstarConfig _config) { char neighbor8[8][2] = { {-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1} }; Map = _Map; config = _config; neighbor = Mat(8, 2, CV_8S, neighbor8).clone(); MapProcess(Mask); } void Astar::PathPlanning(Point _startPoint, Point _targetPoint, vector<Point>& path) { // Get variables startPoint = _startPoint; targetPoint = _targetPoint; // Path Planning Node* TailNode = FindPath(); GetPath(TailNode, path); } void Astar::DrawPath(Mat& _Map, vector<Point>& path, InputArray Mask, Scalar color, int thickness, Scalar maskcolor) { if(path.empty()) { cout << "Path is empty!" << endl; return; } _Map.setTo(maskcolor, Mask); for(auto it:path) { rectangle(_Map, it, it, color, thickness); } } void Astar::MapProcess(Mat& Mask) { int width = Map.cols; int height = Map.rows; Mat _Map = Map.clone(); // Transform RGB to gray image if(_Map.channels() == 3) { cvtColor(_Map.clone(), _Map, cv::COLOR_BGR2GRAY); } // Binarize if(config.OccupyThresh < 0) { threshold(_Map.clone(), _Map, 0, 255, cv::THRESH_OTSU); } else { threshold(_Map.clone(), _Map, config.OccupyThresh, 255, cv::THRESH_BINARY); } // Inflate Mat src = _Map.clone(); if(config.InflateRadius > 0) { Mat se = getStructuringElement(MORPH_ELLIPSE, Size(2 * config.InflateRadius, 2 * config.InflateRadius)); erode(src, _Map, se); } // Get mask bitwise_xor(src, _Map, Mask); // Initial LabelMap LabelMap = Mat::zeros(height, width, CV_8UC1); for(int y=0;y<height;y++) { for(int x=0;x<width;x++) { if(_Map.at<uchar>(y, x) == 0) { LabelMap.at<uchar>(y, x) = obstacle; } else { LabelMap.at<uchar>(y, x) = free; } } } } Node* Astar::FindPath() { int width = Map.cols; int height = Map.rows; Mat _LabelMap = LabelMap.clone(); // Add startPoint to OpenList Node* startPointNode = new Node(startPoint); OpenList.push(pair<int, Point>(startPointNode->F, startPointNode->point)); int index = point2index(startPointNode->point); OpenDict[index] = startPointNode; _LabelMap.at<uchar>(startPoint.y, startPoint.x) = inOpenList; while(!OpenList.empty()) { // Find the node with least F value Point CurPoint = OpenList.top().second; OpenList.pop(); int index = point2index(CurPoint); Node* CurNode = OpenDict[index]; OpenDict.erase(index); int curX = CurPoint.x; int curY = CurPoint.y; _LabelMap.at<uchar>(curY, curX) = inCloseList; // Determine whether arrive the target point if(curX == targetPoint.x && curY == targetPoint.y) { return CurNode; // Find a valid path } // Traversal the neighborhood for(int k = 0;k < neighbor.rows;k++) { int y = curY + neighbor.at<char>(k, 0); int x = curX + neighbor.at<char>(k, 1); if(x < 0 || x >= width || y < 0 || y >= height) { continue; } if(_LabelMap.at<uchar>(y, x) == free || _LabelMap.at<uchar>(y, x) == inOpenList) { // Determine whether a diagonal line can pass int dist1 = abs(neighbor.at<char>(k, 0)) + abs(neighbor.at<char>(k, 1)); if(dist1 == 2 && _LabelMap.at<uchar>(y, curX) == obstacle && _LabelMap.at<uchar>(curY, x) == obstacle) continue; // Calculate G, H, F value int addG, G, H, F; if(dist1 == 2) { addG = 14; } else { addG = 10; } G = CurNode->G + addG; if(config.Euclidean) { int dist2 = (x - targetPoint.x) * (x - targetPoint.x) + (y - targetPoint.y) * (y - targetPoint.y); H = round(10 * sqrt(dist2)); } else { H = 10 * (abs(x - targetPoint.x) + abs(y - targetPoint.y)); } F = G + H; // Update the G, H, F value of node if(_LabelMap.at<uchar>(y, x) == free) { Node* node = new Node(); node->point = Point(x, y); node->parent = CurNode; node->G = G; node->H = H; node->F = F; OpenList.push(pair<int, Point>(node->F, node->point)); int index = point2index(node->point); OpenDict[index] = node; _LabelMap.at<uchar>(y, x) = inOpenList; } else // _LabelMap.at<uchar>(y, x) == inOpenList { // Find the node int index = point2index(Point(x, y)); Node* node = OpenDict[index]; if(G < node->G) { node->G = G; node->F = F; node->parent = CurNode; } } } } } return NULL; // Can not find a valid path } void Astar::GetPath(Node* TailNode, vector<Point>& path) { PathList.clear(); path.clear(); // Save path to PathList Node* CurNode = TailNode; while(CurNode != NULL) { PathList.push_back(CurNode); CurNode = CurNode->parent; } // Save path to vector<Point> int length = PathList.size(); for(int i = 0;i < length;i++) { path.push_back(PathList.back()->point); PathList.pop_back(); } // Release memory while(OpenList.size()) { Point CurPoint = OpenList.top().second; OpenList.pop(); int index = point2index(CurPoint); Node* CurNode = OpenDict[index]; delete CurNode; } OpenDict.clear(); } }
#include <iostream> #include <ros/ros.h> #include <nav_msgs/OccupancyGrid.h> #include <nav_msgs/Path.h> #include <geometry_msgs/PoseWithCovarianceStamped.h> #include <geometry_msgs/PoseStamped.h> #include <geometry_msgs/Quaternion.h> #include <opencv2/opencv.hpp> #include "Astar.h" #include "OccMapTransform.h" using namespace cv; using namespace std; //-------------------------------- Global variables ---------------------------------// // Subscriber ros::Subscriber map_sub; ros::Subscriber startPoint_sub; ros::Subscriber targetPoint_sub; // Publisher ros::Publisher mask_pub; ros::Publisher path_pub; // Object nav_msgs::OccupancyGrid OccGridMask; nav_msgs::Path path; pathplanning::AstarConfig config; pathplanning::Astar astar; OccupancyGridParam OccGridParam; Point startPoint, targetPoint; // Parameter double InflateRadius; bool map_flag; bool startpoint_flag; bool targetpoint_flag; bool start_flag; int rate; //-------------------------------- Callback function ---------------------------------// void MapCallback(const nav_msgs::OccupancyGrid& msg) { // Get parameter OccGridParam.GetOccupancyGridParam(msg); // Get map int height = OccGridParam.height; int width = OccGridParam.width; int OccProb; Mat Map(height, width, CV_8UC1); for(int i=0;i<height;i++) { for(int j=0;j<width;j++) { OccProb = msg.data[i * width + j]; OccProb = (OccProb < 0) ? 100 : OccProb; // set Unknown to 0 // The origin of the OccGrid is on the bottom left corner of the map Map.at<uchar>(height-i-1, j) = 255 - round(OccProb * 255.0 / 100.0); } } // Initial Astar Mat Mask; config.InflateRadius = round(InflateRadius / OccGridParam.resolution); astar.InitAstar(Map, Mask, config); // Publish Mask OccGridMask.header.stamp = ros::Time::now(); OccGridMask.header.frame_id = "map"; OccGridMask.info = msg.info; OccGridMask.data.clear(); for(int i=0;i<height;i++) { for(int j=0;j<width;j++) { OccProb = Mask.at<uchar>(height-i-1, j) * 255; OccGridMask.data.push_back(OccProb); } } // Set flag map_flag = true; startpoint_flag = false; targetpoint_flag = false; } void StartPointCallback(const geometry_msgs::PoseWithCovarianceStamped& msg) { Point2d src_point = Point2d(msg.pose.pose.position.x, msg.pose.pose.position.y); OccGridParam.Map2ImageTransform(src_point, startPoint); // Set flag startpoint_flag = true; if(map_flag && startpoint_flag && targetpoint_flag) { start_flag = true; } // ROS_INFO("startPoint: %f %f %d %d", msg.pose.pose.position.x, msg.pose.pose.position.y, // startPoint.x, startPoint.y); } void TargetPointtCallback(const geometry_msgs::PoseStamped& msg) { Point2d src_point = Point2d(msg.pose.position.x, msg.pose.position.y); OccGridParam.Map2ImageTransform(src_point, targetPoint); // Set flag targetpoint_flag = true; if(map_flag && startpoint_flag && targetpoint_flag) { start_flag = true; } // ROS_INFO("targetPoint: %f %f %d %d", msg.pose.position.x, msg.pose.position.y, // targetPoint.x, targetPoint.y); } //-------------------------------- Main function ---------------------------------// int main(int argc, char * argv[]) { // Initial node ros::init(argc, argv, "astar"); ros::NodeHandle nh; ros::NodeHandle nh_priv("~"); ROS_INFO("Start astar node!\n"); // Initial variables map_flag = false; startpoint_flag = false; targetpoint_flag = false; start_flag = false; // Parameter nh_priv.param<bool>("Euclidean", config.Euclidean, true); nh_priv.param<int>("OccupyThresh", config.OccupyThresh, -1); nh_priv.param<double>("InflateRadius", InflateRadius, -1); nh_priv.param<int>("rate", rate, 10); // Subscribe topics map_sub = nh.subscribe("map", 10, MapCallback); startPoint_sub = nh.subscribe("initialpose", 10, StartPointCallback); targetPoint_sub = nh.subscribe("move_base_simple/goal", 10, TargetPointtCallback); // Advertise topics mask_pub = nh.advertise<nav_msgs::OccupancyGrid>("mask", 1); path_pub = nh.advertise<nav_msgs::Path>("nav_path", 10); // Loop and wait for callback ros::Rate loop_rate(rate); while(ros::ok()) { if(start_flag) { double start_time = ros::Time::now().toSec(); // Start planning path vector<Point> PathList; astar.PathPlanning(startPoint, targetPoint, PathList); if(!PathList.empty()) { path.header.stamp = ros::Time::now(); path.header.frame_id = "map"; path.poses.clear(); for(int i=0;i<PathList.size();i++) { Point2d dst_point; OccGridParam.Image2MapTransform(PathList[i], dst_point); geometry_msgs::PoseStamped pose_stamped; pose_stamped.header.stamp = ros::Time::now(); pose_stamped.header.frame_id = "map"; pose_stamped.pose.position.x = dst_point.x; pose_stamped.pose.position.y = dst_point.y; pose_stamped.pose.position.z = 0; path.poses.push_back(pose_stamped); } path_pub.publish(path); double end_time = ros::Time::now().toSec(); ROS_INFO("Find a valid path successfully! Use %f s", end_time - start_time); } else { ROS_ERROR("Can not find a valid path"); } // Set flag start_flag = false; } if(map_flag) { mask_pub.publish(OccGridMask); } loop_rate.sleep(); ros::spinOnce(); } return 0; }
<!-- -*- mode: XML -*- --> <launch> ################ map server ################ <node pkg="map_server" name="map_server" type="map_server" args="$(find astar_search)/maps/gmapping-005.yaml"/> ################ start astar node ################ <node pkg="astar_search" type="astar_search" name="astar" output="screen"> <param name="Euclidean" value="true"/> <param name="OccupyThresh" value="-1"/> <param name="InflateRadius" value="0.25"/> <param name="rate" value="10"/> </node> [map_server-2] killing on exit ################ start rviz ################ <node pkg="rviz" type="rviz" name="rviz" args="-d $(find astar_search)/rviz/astar.rviz"/> </launch>
在ROS中,用nav_msgs/OccupancyGrid消息类型来表示二维栅格地图,该数据类型包含了地图的元信息(如分辨率、地图大小等等)以及每个栅格的占用状态信息。
std_msgs/Header header
nav_msgs/MapMetaData info
int8[] data
各个字段的意义如下:
header:用于描述这个消息的meta信息,包括时间戳和坐标系。
info:地图的元数据,这是一个nav_msgs/MapMetaData类型,包含地图的分辨率、尺寸以及提供地图的坐标系的原点等信息。
data:实际的地图数据,这是一个一维数组。数组中的每个元素表示对应栅格的占用信息。值为0表示未占用(通常用于表示可行走的区域),值为100表示被占用(通常用于表示障碍物或者禁行的区域),值为-1表示未知。
总的来说,nav_msgs/OccupancyGrid是一种在ROS中表示二维栅格地图的标准类型,广泛应用于地图构建、路径规划和导航等任务。
initialpose: geometry_msgs/PoseWithCovarianceStamped
movebasesimple/goal: geometry_msgs/PoseStamped
mask:nav_msgs/OccupancyGrid
navpath:navmsgs/Path
Euclidean(bool; 默认值: “true”):在计算H值时,选择使用欧几里得距离还是曼哈顿距离。
OccupyThresh(int; 默认值: -1):图像二值化的阈值。当OccupyThresh小于零时,利用Otsu方法生成阈值。
InflateRadius(double; 默认值: -1):InflateRadius是膨胀半径(单位:米)。当InflateRadius小于或等于零时,不进行膨胀操作。
rate(int; 默认值: 10):发布mask主题的频率。
catkin_make
source ./devel/setup.bash
roslaunch astar_search astar.launch
1、初始化openlist(待检查的列表)和closelist(已检查的列表),将起点(start)加入openlist中,并找出start周围可移动的栅格(八叉树),计算start到这些周围点的欧式距离g,并将start设置为父节点。
2、在完成了对起点start的检查之后,把start的周围点加入到openlist中,同时把start从openlist中删除,并加入到closelist中。
3、这时openlist中存放的是start的周围点,计算每个周围点的f=g+h,其中g是起点start到当前节点的距离,h是当前节点到终点的距离(曼哈顿距离),找出周围点中f最小的节点n,并对他执行前面同样的检查。
在搜索n的周围点时注意:
①如果周围点在closedlist中,忽略此周围点;
②如果周围点既不在closedlist中,也不在openlist中,计算g、h和f,设置当前节点n为父节点,并将新的周围点加入到openlist中;
③如果周围点在openlist中(表面该邻居已有父节点),计算该父节点经过当前点n再到周围点是否使其能够得到更小的g,如果可以,将该周围点的父节点设置为当前点n,并更新其g和h值。
完成对当前点n的检查之后,将其从openlist中剔除,并加入到closed中。
4、当openlist中出现目标点goal时,找到路径;当openlist中为空时,则无法找到。
主要对核心代码Astar.cpp文件进行算法的解释。
void InitAstar(Mat& _Map, AstarConfig _config = AstarConfig());
void InitAstar(Mat& _Map, Mat& Mask, AstarConfig _config = AstarConfig());
void PathPlanning(Point _startPoint, Point _targetPoint, vector<Point>& path);
void Astar::DrawPath(Mat& _Map, vector<Point>& path, InputArray Mask, Scalar color,
int thickness, Scalar maskcolor)
void MapProcess(Mat& Mask);
Node* FindPath();
总体来说,这段代码实现了A算法的路径规划过程,并提供了一些辅助函数用于地图处理和路径给制,它将地图处理为二值图像,小搜索节点的方式找到最短路径,并将路径绘制在地图上,这段代码的关键在于A算法的实现,它通过优先队列和哈希表来高效地管理节点,并利用启发函数来指导搜索过程。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。