参考1
参考2
Python 代码
copy 参考1
# -*- coding: utf-8 -*-
import numpy as np
from heapq import heappush, heappop
def heuristic_cost_estimate(neighbor, goal):
x = neighbor[0] - goal[0]
y = neighbor[1] - goal[1]
return abs(x) + abs(y)
def dist_between(a, b):
return (b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2
def reconstruct_path(came_from, current):
path = [current]
while current in came_from:
current = came_from[current]
path.append(current)
return path
# astar function returns a list of points (shortest path)
def astar(array, start, goal):
directions = [(0, 1), (0, -1), (1, 0), (-1, 0), (1, 1), (1, -1), (-1, 1), (-1, -1)] # 8个方向
close_set = set()
came_from = {}
gscore = {start: 0}
fscore = {start: heuristic_cost_estimate(start, goal)}
openSet = []
heappush(openSet, (fscore[start], start)) # 往堆中插入一条新的值
# while openSet is not empty
while openSet:
# current := the node in openSet having the lowest fScore value
current = heappop(openSet)[1] # 从堆中弹出fscore最小的节点
if current == goal:
return reconstruct_path(came_from, current)
close_set.add(current)
for i, j in directions: # 对当前节点的 8 个相邻节点一一进行检查
neighbor = current[0] + i, current[1] + j
## 判断节点是否在地图范围内,并判断是否为障碍物
if 0 <= neighbor[0] < array.shape[0]:
if 0 <= neighbor[1] < array.shape[1]:
if array[neighbor[0]][neighbor[1]] == 0: # 0为障碍物
continue
else:
# array bound y walls
continue
else:
# array bound x walls
continue
# Ignore the neighbor which is already evaluated.
if neighbor in close_set:
continue
# The distance from start to a neighbor via current
tentative_gScore = gscore[current] + dist_between(current, neighbor)
if neighbor not in [i[1] for i in openSet]: # Discover a new node
heappush(openSet, (fscore.get(neighbor, np.inf), neighbor))
elif tentative_gScore >= gscore.get(neighbor, np.inf): # This is not a better path.
continue
# This path is the best until now. Record it!
came_from[neighbor] = current
gscore[neighbor] = tentative_gScore
fscore[neighbor] = tentative_gScore + heuristic_cost_estimate(neighbor, goal)
return False
if __name__ == "__main__":
from PIL import Image
import matplotlib.pyplot as plt
img = Image.open('map.bmp')
map = np.array(img) # 图像转化为二维数组
path = astar(map, (0, 0), (260, 260))
# 绘制路径
img = np.array(img.convert('RGB'))
for i in range(len(path)):
img[path[i]] = [0, 0, 255]
plt.imshow(img)
plt.axis('off')
plt.show()
C++ 代码
给出 github 链接
加了一部分注解
AStar.cpp
#include "AStar.hpp"
#include <algorithm>
#include <cmath>
using namespace std::placeholders;
bool AStar::Vec2i::operator == (const Vec2i& coordinates_)
{
return (x == coordinates_.x && y == coordinates_.y);
}
AStar::Vec2i operator + (const AStar::Vec2i& left_, const AStar::Vec2i& right_)
{
return{ left_.x + right_.x, left_.y + right_.y };
}
AStar::Node::Node(Vec2i coordinates_, Node *parent_)
{
parent = parent_;
coordinates = coordinates_;
G = H = 0;
}
AStar::uint AStar::Node::getScore()
{
return G + H;
}
AStar::Generator::Generator()
{
setDiagonalMovement(false);
setHeuristic(&Heuristic::manhattan);
direction = {
{ 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 },
{ -1, -1 }, { 1, 1 }, { -1, 1 }, { 1, -1 }
};
}
void AStar::Generator::setWorldSize(Vec2i worldSize_)
{
worldSize = worldSize_;
}
void AStar::Generator::setDiagonalMovement(bool enable_)
{
directions = (enable_ ? 8 : 4);
}
void AStar::Generator::setHeuristic(HeuristicFunction heuristic_)
{
heuristic = std::bind(heuristic_, _1, _2);
}
void AStar::Generator::addCollision(Vec2i coordinates_)
{
walls.push_back(coordinates_);
}
void AStar::Generator::removeCollision(Vec2i coordinates_)
{
auto it = std::find(walls.begin(), walls.end(), coordinates_);
if (it != walls.end()) {
walls.erase(it);
}
}
void AStar::Generator::clearCollisions()
{
walls.clear();
}
AStar::CoordinateList AStar::Generator::findPath(Vec2i source_, Vec2i target_)
{
Node *current = nullptr;
NodeSet openSet, closedSet;
openSet.reserve(100);
closedSet.reserve(100);
openSet.push_back(new Node(source_));
while (!openSet.empty()) {
auto current_it = openSet.begin();
current = *current_it;
// 遍历openset点 f值最小的点
for (auto it = openSet.begin(); it != openSet.end(); it++) {
auto node = *it;
// Node 中已经存了 G+H 的值直接读取就好
if (node->getScore() <= current->getScore()) {
current = node;
current_it = it;
}
}
if (current->coordinates == target_) {
break;
}
closedSet.push_back(current);
openSet.erase(current_it);
// 8 个方向探索 找点加入openList中
for (uint i = 0; i < directions; ++i) {
// 生成新的点 构造且初始化
Vec2i newCoordinates(current->coordinates + direction[i]);
// 碰撞检测和closeset检测 若已经在closeset中 则继续
if (detectCollision(newCoordinates) ||
findNodeOnList(closedSet, newCoordinates)) {
continue;
}
// 判断是横向 还是斜方向 分别给与不同的代价值
uint totalCost = current->G + ((i < 4) ? 10 : 14);
// 查找openset 是否有 新探索出来的点 如果没有则添加进去
// 如果有则更新G值 且更新父节点
Node *successor = findNodeOnList(openSet, newCoordinates);
if (successor == nullptr) {
successor = new Node(newCoordinates, current);
successor->G = totalCost;
successor->H = heuristic(successor->coordinates, target_);
openSet.push_back(successor);
}
else if (totalCost < successor->G) {
successor->parent = current;
successor->G = totalCost;
}
}
}
CoordinateList path;
while (current != nullptr) {
path.push_back(current->coordinates);
current = current->parent;
}
releaseNodes(openSet);
releaseNodes(closedSet);
return path;
}
AStar::Node* AStar::Generator::findNodeOnList(NodeSet& nodes_, Vec2i coordinates_)
{
for (auto node : nodes_) {
if (node->coordinates == coordinates_) {
return node;
}
}
return nullptr;
}
void AStar::Generator::releaseNodes(NodeSet& nodes_)
{
for (auto it = nodes_.begin(); it != nodes_.end();) {
delete *it;
it = nodes_.erase(it);
}
}
bool AStar::Generator::detectCollision(Vec2i coordinates_)
{
if (coordinates_.x < 0 || coordinates_.x >= worldSize.x ||
coordinates_.y < 0 || coordinates_.y >= worldSize.y ||
std::find(walls.begin(), walls.end(), coordinates_) != walls.end()) {
return true;
}
return false;
}
AStar::Vec2i AStar::Heuristic::getDelta(Vec2i source_, Vec2i target_)
{
return{ abs(source_.x - target_.x), abs(source_.y - target_.y) };
}
AStar::uint AStar::Heuristic::manhattan(Vec2i source_, Vec2i target_)
{
auto delta = std::move(getDelta(source_, target_));
return static_cast<uint>(10 * (delta.x + delta.y));
}
AStar::uint AStar::Heuristic::euclidean(Vec2i source_, Vec2i target_)
{
auto delta = std::move(getDelta(source_, target_));
return static_cast<uint>(10 * sqrt(pow(delta.x, 2) + pow(delta.y, 2)));
}
AStar::uint AStar::Heuristic::octagonal(Vec2i source_, Vec2i target_)
{
auto delta = std::move(getDelta(source_, target_));
return 10 * (delta.x + delta.y) + (-6) * std::min(delta.x, delta.y);
}
AStar.hpp 头文件
/*
Copyright (c) 2015, Damian Barczynski <daan.net@wp.eu>
Following tool is licensed under the terms and conditions of the ISC license.
For more information visit https://opensource.org/licenses/ISC.
*/
#ifndef __ASTAR_HPP_8F637DB91972F6C878D41D63F7E7214F__
#define __ASTAR_HPP_8F637DB91972F6C878D41D63F7E7214F__
#include <vector>
#include <functional>
#include <set>
namespace AStar
{
struct Vec2i
{
int x, y;
bool operator == (const Vec2i& coordinates_);
};
using uint = unsigned int;
using HeuristicFunction = std::function<uint(Vec2i, Vec2i)>;
using CoordinateList = std::vector<Vec2i>;
struct Node
{
uint G, H;
Vec2i coordinates;
Node *parent;
Node(Vec2i coord_, Node *parent_ = nullptr);
uint getScore();
};
using NodeSet = std::vector<Node*>; // 数组重新定义
class Generator
{
bool detectCollision(Vec2i coordinates_);
Node* findNodeOnList(NodeSet& nodes_, Vec2i coordinates_); // 传入数组类型 类似 vector<int> &a
void releaseNodes(NodeSet& nodes_);
public:
Generator(); // 构造函数
void setWorldSize(Vec2i worldSize_); // 地图大小
void setDiagonalMovement(bool enable_); // 设置对角元素
void setHeuristic(HeuristicFunction heuristic_); // 启发函数
CoordinateList findPath(Vec2i source_, Vec2i target_); //
void addCollision(Vec2i coordinates_);
void removeCollision(Vec2i coordinates_);
void clearCollisions();
private:
HeuristicFunction heuristic;
CoordinateList direction, walls;
Vec2i worldSize;
uint directions;
};
class Heuristic
{
static Vec2i getDelta(Vec2i source_, Vec2i target_);
public:
static uint manhattan(Vec2i source_, Vec2i target_);
static uint euclidean(Vec2i source_, Vec2i target_);
static uint octagonal(Vec2i source_, Vec2i target_);
};
}
#endif // __ASTAR_HPP_8F637DB91972F6C878D41D63F7E7214F__
main 文件
注意文件的目录 我的 源文件在source 目录下
#include <iostream>
#include "source/AStar.hpp"
int main()
{
AStar::Generator generator;
// 设置地图尺寸
generator.setWorldSize({25, 25});
// 添加障碍物 以坐标形式添加
generator.addCollision({17,17});
// 选择 曼哈顿距离 欧式距离 或者欧式距离平方
generator.setHeuristic(AStar::Heuristic::euclidean);
generator.setDiagonalMovement(true);
std::cout << "Generate path ... \n";
auto path = generator.findPath({3, 3}, {20, 20});
for(auto& coordinate : path) {
std::cout << coordinate.x << " " << coordinate.y << "\n";
}
}
cmakelist 文件
cmake_minimum_required (VERSION 2.8.11)
project (a-star)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
include_directories(source)
add_library(a-star source/AStar.cpp)
add_executable(main main.cpp)
target_link_libraries(main a-star)