Hybrid Astar 算法剖析和实现(六)

在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习。我的使命就是过滤掉大量的无效信息,将知识体系化,以短平快的方式直达问题本质,把大家从大海捞针的痛苦中解脱出来。

0 前言

本篇继续介绍节点拓展的内容,以及示例代码实现。

1 碰撞检测和边界检测

上一篇(Hybrid Astar 算法剖析和实现(五)_穿越临界点的博客-CSDN博客)讲到中间节点的存在就是为了进行相对可靠的碰撞检测。

节点每次拓展都要进行碰撞检测和边界检测(边界检测可以理解为碰撞检测的一种)。

节点每次拓展需要检测几次边界内的碰撞呢?有几个中间节点(包括最后一个节点)就要检测几次。

节点每次拓展需要做几次边界碰撞检测呢?只需要做一次。

2 节点状态离散化

按照上篇中的公式进行节点拓展后,得到的是节点连续状态空间 ( x , y , θ ) (x,y,\theta) (x,y,θ),需要将其离散化后映射到状态栅格中,这样做是为了方便识别两个节点状态是否是同一个节点状态。

离散化请参考 Hybrid Astar 算法剖析和实现(二)_穿越临界点的博客-CSDN博客

3 代码实现

为了将抽象概念具体化,下面给出节点拓展流程的示例代码。

/*依据当前节点获得邻居节点(节点拓展)*/
void HybridAStar::GetNeighborNodes(const StateNode& curr_node,
                                   std::vector<StateNode> &neighbor_nodes) {
    neighbor_nodes.clear();

    /*第一次循环是对前轮转角的离散*/
    for (int i = -steering_discrete_num_; i <= steering_discrete_num_; ++i) {
        VectorVec3d intermediate_state;
        bool has_obstacle = false;

        double x = curr_node.state_.x();
        double y = curr_node.state_.y();
        double theta = curr_node.state_.z();

        const double phi = i * steering_radian_step_size_;

        // forward:v取正向最大值
        /*该层循环实质上是对时间t进行离散,这里用前进距离等效*/
        for (int j = 1; j <= segment_length_discrete_num_; j++) {
            /*基于运动学模型进行中间节点拓展
            (最后一个中间节点是最终的状态节点)*/
            DynamicModel(move_step_size_, phi, x, y, theta);
            intermediate_state.emplace_back(Vec3d(x, y, theta));
		   /*对每个中间节点进行碰撞检测*/
            if (!CheckCollision(x, y, theta)) {
                has_obstacle = true;
                break;
            }
        }

        /*状态节点离散化,映射到状态栅格中*/
        Vec3i ids = State2Index(intermediate_state.back());
        /*判断节点所处集合*/
        StateNode::NODE_STATUS node_state = StateNode::NOT_VISITED;
        auto pnode = visited_.find(ids);
        if (pnode != visited_.end()) {
            node_state = pnode->second.node_status_;
        } else {
            node_state = StateNode::NOT_VISITED;
        }

        /*进行边界检测并提前过滤掉已经存在于close set中的节点(提升效率)*/
        if (node_state != StateNode::IN_CLOSESET &&
            !BeyondBoundary(intermediate_state.back().head(2)) &&
            !has_obstacle) {
            StateNode neighor_node(ids);
            neighor_node.node_status_ = node_state;
            neighor_node.intermediate_states_ = intermediate_state;
            neighor_node.state_ = intermediate_state.back();
            neighor_node.steering_grade_ = i;
            neighor_node.direction_ = StateNode::FORWARD;
            neighbor_nodes.push_back(neighor_node);
        }

        // backward: v取负向最大值情况省略
        ...
}

单个节点拓展的示例代码如下:

其中的NormalizeAngle函数实现可以参照 角度归一化实现_穿越临界点的博客-CSDN博客_角度归一化

void HybridAStar::DynamicModel(const double &step_size, const double &phi,
                               double &x, double &y, double &theta) const {
    x = x + step_size * std::cos(theta);
    y = y + step_size * std::sin(theta);
    theta = NormalizeAngle(theta + step_size / wheel_base_ * std::tan(phi));
}

4 总结

本篇主要介绍了Hybrid Astar算法节点拓展的代码实现。下一篇重点介绍碰撞检测算法。

恭喜你又坚持看完了一篇博客,又进步了一点点!如果感觉还不错就点个赞再走吧,你的点赞和关注将是我持续输出的哒哒哒动力~~

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
AStar算法是一种常用的路径规划算法,其主要思路是在图或网格中找到一条最短路径。 具体实现思路如下: 1. 初始化起点和终点,并将起点加入open列表中。 2. 从open列表中选取f值最小的节点作为当前节点,并将其加入close列表中。 3. 根据当前节点,找到其相邻的节点,并计算它们的f值(f = g + h),其中g表示从起点到当前节点的距离,h表示从当前节点到终点的估算距离。 4. 对于每个相邻节点,如果它已经在close列表中,或者障碍物阻挡了它,或者它已经在open列表中但是新的路径比原路径更长,则忽略它。 5. 否则,将当前节点作为该相邻节点的父节点,更新该相邻节点的g值和f值,并将其加入open列表中。 6. 重复执行步骤2-5,直到终点被加入close列表中或者open列表为空。 7. 如果终点被加入close列表中,则说明找到了一条最短路径,根据每个节点的父节点可以回溯得到该路径。 以下是Python的示例代码: ```python import heapq def astar(start, end, obstacles, grid_size): """ A*算法实现 :param start: 起点坐标 (x, y) :param end: 终点坐标 (x, y) :param obstacles: 障碍物坐标列表 [(x1, y1), (x2, y2), ...] :param grid_size: 网格大小 (width, height) :return: 最短路径坐标列表 [(x1, y1), (x2, y2), ...] """ # 计算启发式距离 def heuristic(a, b): return abs(b[0] - a[0]) + abs(b[1] - a[1]) # 判断坐标是否在网格内 def in_grid(x, y): return 0 <= x < grid_size[0] and 0 <= y < grid_size[1] # 判断坐标是否为障碍物 def is_obstacle(x, y): return (x, y) in obstacles # 获取周围相邻的坐标 def get_neighbors(x, y): neighbors = [(x+1, y), (x, y+1), (x-1, y), (x, y-1)] return filter(lambda p: in_grid(*p) and not is_obstacle(*p), neighbors) # 初始化起点和终点 start_node = (0, start, None) end_node = (heuristic(start, end), end, None) # 初始化open列表和close列表 open_list = [start_node] close_list = set() # 循环直到找到终点或open列表为空 while open_list: # 选取f值最小的节点作为当前节点 current_node = heapq.heappop(open_list) if current_node[1] == end: # 找到终点,回溯得到路径 path = [] while current_node: path.append(current_node[1]) current_node = current_node[2] return path[::-1] # 将当前节点加入close列表中 close_list.add(current_node[1]) # 处理相邻节点 for neighbor in get_neighbors(*current_node[1]): # 如果相邻节点已经在close列表中,忽略它 if neighbor in close_list: continue # 计算相邻节点的f值 g = current_node[0] + 1 h = heuristic(neighbor, end) f = g + h # 如果相邻节点已经在open列表中,更新它的f值和父节点 for node in open_list: if node[1] == neighbor: if f < node[0]: open_list.remove(node) node = (f, neighbor, current_node) heapq.heappush(open_list, node) break else: # 否则,将相邻节点加入open列表中 node = (f, neighbor, current_node) heapq.heappush(open_list, node) # open列表为空,无法找到路径 return [] ``` 通过调用`astar`函数,传入起点、终点、障碍物和网格大小,即可得到最短路径坐标列表。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

穿越临界点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值