7.3 可视化的D* Lite路径规系统
本项目是一个基于D* Lite算法的路径规划器,通过库SFML实现了一个可视化的网格地图界面,用户可以通过左键点击来设置障碍物,右键点击来清除障碍物,并且可以通过键盘上的'R'键来重置地图。DStarLite类实现了D* Lite算法的核心功能,用于在给定的网格地图中计算从起点到目标点的最短路径。CellGrid类用于管理和绘制地图的图形表示。整个项目提供了一个交互式的界面,让用户可以直观地看到路径规划的过程和结果。
实例7-1:可视化的D* Lite路径规系统(codes/7/DStarLite/)
7.3.1 项目介绍
本项目是一个基于D* Lite算法的路径规划器,通过SFML库实现了一个可视化的网格地图界面。用户可以在地图上设置起点、目标点以及障碍物,并观察算法计算出的最短路径。
1. 功能特点
- 可视化地图界面:使用SFML库创建了一个可视化的网格地图界面,用户可以直观地看到地图布局和路径规划结果。
- 交互式操作:用户可以使用鼠标左键点击设置障碍物,右键点击清除障碍物,并且可以通过键盘上的'R'键来重置地图。
- D* Lite算法实现:使用DStarLite类实现了D* Lite算法,用于在给定的网格地图中计算从起点到目标点的最短路径。
- 地图管理:使用CellGrid类管理和绘制地图的图形表示,包括起点、目标点、障碍物和路径等。
2. 项目价值
该项目为用户提供了一个直观、实用的路径规划工具,可用于演示和测试D* Lite算法在不同地图场景下的表现。用户可以通过调整地图布局和参数来观察算法的效果,从而更好地理解和学习路径规划算法。
7.3.2 运算符重载
文件util.hpp定义了一些实用函数和运算符重载,主要用于处理 std::pair 类型和 std::vector 类型的操作。
1. 运算符重载
(1)operator+:重载了 + 运算符,用于对两个 std::pair 进行元素相加操作。
(2)operator-:重载了 - 运算符,分为两种情况:
- 重载了两个 std::pair 相减的操作。
- 重载了单个 std::pair 的取反操作。
这些重载操作使得 std::pair 类型可以直接进行算术运算。
2. 函数arg_min()
函数arg_min()用于返回一个 std::vector 中最小元素的索引。它接受一个 std::vector 类型的参数,并使用函数std::min_element()找到最小元素,然后返回该最小元素的索引。注意,这个函数会复制一份输入的向量,因此如果对输入向量没有修改的需求,可以传递一个常引用来避免不必要的复制。
总的来说,文件util.hpp提供了一些方便的实用函数和运算符重载,以简化对 std::pair 和 std::vector 的操作。文件util.hpp的具体实现代码如下所示。
#ifndef __DSTARLITE_UTIL_HPP__
#define __DSTARLITE_UTIL_HPP__
#include <vector>
#include <utility>
#include <algorithm>
/**
* @brief 加法重载 `std::pair<T1, T2>`
*
* @tparam T1 第一个类型
* @tparam T2 第二个类型
* @param p1 第一个 std::pair
* @param p2 第二个 std::pair
* @return std::pair<T1, T2> 返回类型为 std::pair<T1, T2>
*/
template<typename T1, typename T2>
std::pair<T1, T2> operator+(const std::pair<T1, T2> &p1, const std::pair<T1, T2> &p2)
{
return {p1.first + p2.first, p1.second + p2.second};
}
/**
* @brief 减法重载 `std::pair<T1, T2>`
*
* @tparam T1 第一个类型
* @tparam T2 第二个类型
* @param p1 第一个 std::pair
* @param p2 第二个 std::pair
* @return std::pair<T1, T2> 返回类型为 std::pair<T1, T2>
*/
template<typename T1, typename T2>
std::pair<T1, T2> operator-(const std::pair<T1, T2> &p1, const std::pair<T1, T2> &p2)
{
return {p1.first - p2.first, p1.second - p2.second};
}
/**
* @brief 取反运算符进行重载 `std::pair<T1, T2>`
*
* @tparam T1 第一个类型
* @tparam T2 第二个类型
* @param p std::pair
* @return std::pair<T1, T2> 返回类型为 std::pair<T1, T2>
*/
template<typename T1, typename T2> std::pair<T1, T2> operator-(const std::pair<T1, T2> &p)
{
return {-p.first, -p.second};
}
/**
* @brief 返回向量中最小元素的索引
*
* @tparam T 类型
* @param v 向量
* @return size_t 返回类型为 size_t
*/
template<typename T> size_t arg_min(std::vector<T> v)
{
return std::min_element(v.begin(), v.end()) - v.begin();
}
#endif /* __DSTARLITE_UTIL_HPP__ */
在上述代码中,实现了对 std::pair 类型的运算符重载功能,使得在D* Lite算法中对节点的操作更加方便。D* Lite算法中节点的表示通常使用 std::pair 类型,其中第一个元素是节点的坐标,第二个元素是节点的代价值或者其他属性。文件util.hpp提供了函数arg_min(),用于找到向量中最小元素的索引。在D* Lite算法中,经常需要找到最小值对应的索引,比如找到最小代价路径的下一个节点。
7.3.3 优先队列
文件PriorityQueue.hpp 定义了一个优先队列类 PriorityQueue,它是对标准库中的优先队列进行了扩展,增加了一些额外的功能,比如根据元素的值来移除元素。文件PriorityQueue.hpp实现了一个优先队列,这在D* Lite算法中是必需的。D* Lite算法中需要根据节点的代价值来动态调整搜索顺序,优先队列是一种高效的数据结构,用于按照节点的代价值进行排序和访问。
注意:优先队列的实现支持了一些额外的功能,比如根据节点的值来更新其优先级、根据节点的值来移除节点等等。这些功能在D* Lite算法的实现中可能会派上用场,比如在路径规划过程中需要动态更新节点的代价值。
文件PriorityQueue.hpp的具体实现代码如下所示。
#ifndef __DSTARLITE_PRIORITYQUEUE_HPP__
#define __DSTARLITE_PRIORITYQUEUE_HPP__
#include <vector>
#include <utility>
#include <algorithm>
using std::make_heap;
using std::pair;
using std::pop_heap;
using std::push_heap;
using std::vector;
/**
* @brief 优先队列容器类。在STL实现中添加了“按值删除元素”的功能
*
* @tparam Tp 优先级的类型
* @tparam Te 队列中元素的类型
*/
template<class Tp, class Te> class PriorityQueue
{
public:
/**
* @brief 查看顶部元素的值并返回键值对。
*
* @return pair<Tp, Te> {优先级, 元素}
*/
pair<Tp, Te> top() const
{
return data.front(); // 返回 {优先级, 元素} 键值对
}
/**
* @brief 查看顶部元素的键。
*
* @return Tp 优先级
*/
Tp topKey() const
{
return data.front().first; // 仅返回优先级;
}
/**
* @brief 返回当前容器中的元素数量。
*
* @return int
*/
int size() const
{
return data.size();
}
/**
* @brief 如果容器为空则返回