【深蓝学院】移动机器人运动规划--第3章 基于采样的路径规划--作业

0. Assignment

在这里插入图片描述

T1. MATLAB实现RRT

1.1 GPT-4任务分析

> 。

RRT伪代码:
在这里插入图片描述

任务1即使用matlab实现RRT,结合作业所给框架,简单梳理,可结合1.2代码理解:

  1. 设置start,goal,near to goal threshold Thr,step的步长Delta
  2. Tree初始化,数据结构:2D xy坐标,父节点xy坐标,父节点到该节点距离,父节点的index
  3. 建树部分需要我们完成:
    1. step1完成伪代码的Sample,得到x_rand
    2. step2完成伪代码的Near,具体操作:遍历树中所有Nodes,计算每个Nodes到x_rand的欧氏距离dst,取最小的,记录dst值和index
    3. step3完成伪代码的Steer,结合collisionChecking.m中进行collision checking的方法,计算方向并更新x_new
    4. step 4: 将x_new插入树T,对应伪代码Edge,CollisionFree,collision check直接调用函数,不赘述。
    5. step 5: 检查是否到达目标点附近,计算欧氏距离与Thr比较。
    6. step 6:绘图,不赘述。
  4. 反向查询并绘图:
    找到goal之后,从goal回溯到父节点为start的节点,依次坐标记录到path中,最后调用MATLAB的end关键字插入一个新的node,即start,并记录start坐标,所以path中按照index顺序实际上是存储的inverse path。
    %已经遍历完path中除了起点外的所有node,end+1表示新增一个元素,所以后面再调用end时就是访问添加后的最后一个元素了
    path.pos(end+1).x = x_I; 
    path.pos(end).y = y_I;
    
    找到的path绘图为蓝色,结束。

1.2 代码

RRT.m代码:

% RRT算法
%% 流程初始化
clc
clear all; close all;
x_I=1; y_I=1;           % 设置初始点
x_G=799; y_G=799;       % 设置目标点(可尝试修改终点)
Thr=50;                 % 设置目标点阈值,用法:若当前节点和终点的欧式距离小于Thr,则跳出当前for循环
Delta= 30;              % 设置扩展步长  应该是stepsize
%% 建树初始化
T.v(1).x = x_I;         % T是我们要做的树,v是节点,这里先把起始点加入到T里面来
T.v(1).y = y_I; 
T.v(1).xPrev = x_I;     % 起始节点的父节点仍然是其本身
T.v(1).yPrev = y_I;
T.v(1).dist=0;          % 从父节点到该节点的距离,这里可取欧氏距离
T.v(1).indPrev = 0;     % 这个变量什么意思?根据下面代码来看,indPrev表示indexPrev,父节点的index
%% 开始构建树,作业部分
figure(1);
ImpRgb=imread('generate_RRT.png');
Imp=rgb2gray(ImpRgb);  %地图,255为白色,free部分
imshow(Imp)
xL=size(Imp,2);%地图x轴长度 列数
yL=size(Imp,1);%地图y轴长度 行数
hold on
plot(x_I, y_I, 'ro', 'MarkerSize',10, 'MarkerFaceColor','r');
plot(x_G, y_G, 'go', 'MarkerSize',10, 'MarkerFaceColor','g');% 绘制起点和目标点
count=1;%记录树T中节点的个数
bFind = false;for iter = 1:3000x_rand=[];%Step 1: 在地图中随机采样一个点x_rand%提示:用(x_rand(1),x_rand(2))表示环境中采样点的坐标x_rand(1) = rand * (xL-1) +1;%-1是为了保证随机生成的坐标不会超出地图的边界,+1是因为索引是从1开始的x_rand(2) = rand * (yL-1) +1;x_near=[];%Step 2: 遍历树,从树中找到最近邻近点x_near(计算每个节点到 x_rand 的距离,找出最近的一个节点作为 x_near。)%提示:x_near已经在树T里numNodes = length(T.v);nearest_dst = Inf;nearest_idx = 1;for node_idx = 1:numNodestmp_dist = sqrt(sum(([T.v(node_idx).x T.v(node_idx).y]-[x_rand(1) x_rand(2)]).^2));%T中每个节点到x_rand的欧氏距离if tmp_dist < nearest_dstnearest_dst = tmp_dist;nearest_idx = node_idx;endendx_near(1) = T.v(nearest_idx).x;x_near(2) = T.v(nearest_idx).y;%Step 3: 扩展得到x_new节点%提示:注意使用扩展步长Deltadir=atan2(x_rand(1)-x_near(1),x_rand(2)-x_near(2));x_new = x_near + Delta.*[sin(dir) cos(dir)];%检查节点是否是collision-free(即是否feasiable),如果不collision-free,则继续下一次采样if ~collisionChecking(x_near,x_new,Imp) continue;endcount=count+1;%Step 4: 将x_new插入树T %提示:新节点x_new的父节点是x_nearT.v(count).x = x_new(1);T.v(count).y = x_new(2);T.v(count).xPrev = x_I;T.v(count).yPrev = y_I;T.v(count).dist = nearest_dst;T.v(count).indPrev = nearest_idx;%Step 5:检查是否到达目标点附近 %提示:注意使用目标点阈值Thr,若当前节点和终点的欧式距离小于Thr,则跳出当前for循环(退出前需要绘制出x_near和x_new之间的路径)check_dist = calDist(x_new, [x_G y_G]);if check_dist < Thrplot([x_near(1); x_new(1);], [x_near(2); x_new(2)], 'r', 'Linewidth', 3);hold on;bFind = true;break;end%Step 6:将x_near和x_new之间的路径画出来%提示 1:使用plot绘制,因为要多次在同一张图上绘制线段,所以每次使用plot后需要接上hold on命令%提示 2:在判断终点条件弹出for循环前,记得把x_near和x_new之间的路径画出来plot([x_near(1); x_new(1);], [x_near(2); x_new(2)], 'r', 'Linewidth', 3);pause(0.05); %暂停一会,使得RRT扩展过程容易观察
endif T.v(count).x == T.v(end).x && T.v(count).y == T.v(end).yfprintf('T.v(count) == T.v(end), T.v(count): (%f, %f),  T.v(end): (%f, %f)\n', T.v(count).x, T.v(count).y,T.v(end).x, T.v(end).y);
elsefprintf('T.v(count) != T.v(end)\n');
end%% 路径已经找到,反向查询
if bFindpath.pos(1).x = x_G; path.pos(1).y = y_G;path.pos(2).x = T.v(end).x; path.pos(2).y = T.v(end).y;%关键字end用于表示pathIndex = T.v(end).indPrev; % 终点加入路径j=0;while 1path.pos(j+3).x = T.v(pathIndex).x;path.pos(j+3).y = T.v(pathIndex).y;pathIndex = T.v(pathIndex).indPrev;if pathIndex == 1breakendj=j+1;end  % 沿终点回溯到起点% 起点加入路径path.pos(end+1).x = x_I; %已经遍历完path中除了起点外的所有node,end+1表示新增一个元素,所以后面再调用end时就是访问添加后的最后一个元素了,没毛病path.pos(end).y = y_I;for j = 2:length(path.pos)plot([path.pos(j).x; path.pos(j-1).x;], [path.pos(j).y; path.pos(j-1).y], 'b', 'Linewidth', 3);end
elsedisp('Error, no path found!');
end

calDist.m代码:

function dist=calDist(startPose,goalPose)
dist=sqrt(sum((startPose-goalPose).^2));

1.3实验结果

如下所示:
在这里插入图片描述

1.4 更改Map

根据其中加载的数据,我们可以使用OpenCV生成其他类型的地图,下面是代码,在createRRTMap可以简单更改障碍物形状,上面实验使用的是我自己生成的map。

#include <vector>
#include <stdio.h>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;void createRRTMap(Mat &mat)
{mat = cv::Mat(mat.rows, mat.cols, CV_8UC1, cv::Scalar(255)).clone();for(int i = 100; i < 200; ++i)for(int j = 100; j < 200; ++j)mat.at<uint8_t>(i, j) = 0;for(int i = 400; i < 700; ++i)for(int j = 300; j < 600; ++j)mat.at<uint8_t>(i, j) = 0;for(int i = 100; i < 300; ++i)for(int j = 270; j < 700; ++j)mat.at<uint8_t>(i, j) = 0;
}int main( )
{Mat matRRT(800, 800, CV_8UC1);Mat matRRT_rgb(800, 800, CV_8UC1);createRRTMap(matRRT);cv::cvtColor(matRRT, matRRT_rgb, cv::COLOR_GRAY2BGR);vector<int>compression_params;compression_params.push_back(CV_IMWRITE_JPEG_QUALITY);//对于PNG格式的图片,表示压缩级别,值为[0, 9],值越大,尺寸越小,压缩时间越长compression_params.push_back(95);//显示图片try{imwrite("generate_RRT.png", matRRT_rgb, compression_params);imshow("生成的RRT png图",matRRT_rgb);fprintf(stdout,"PNG图片文件的alpha数据保存完毕~\n可以在工程目录下查看由imwrite函数生成的图片\n");waitKey(0);}catch(runtime_error& ex) {fprintf(stderr,"图像转换成PNG格式发生错误:%s\n", ex.what());return 1;}return 0;
}

T2. C++实现RRT*

2.1 关于K-D树的介绍

K-D树(K-Dimensional Tree的简称)是一种用于组织k维空间中的点的数据结构。这种数据结构方便了对多维空间的快速搜索,经常被用于各种数据库和计算机视觉中的快速检索任务。

K-D树是一棵二叉树,其中每个节点都是k维数值点。构造K-D树时,数据会递归地被划分成两个半空间,这个过程类似于二分查找算法。构建树的一个典型方法是选择一个维度,并在这个维度上找到位于中间的值作为划分点,将数据集分为两个子集,然后在子集上重复这个过程,但每次选择不同的维度,直到满足某个终止条件(如每个节点下的点的数量小于一定阈值)。

在K-D树的每个节点上,选择的维度和该维度上的值定义了一个超平面,这个超平面将空间分成两半,每半空间对应树中的一个分支。

搜索时,如果要找的点在划分平面的一边,就进入对应的子树继续搜索。这样可以忽略掉空间中的一大半区域,从而快速缩小搜索范围。不过需要注意的是,最近邻搜索时可能需要检查两边的子树,因为最近的点可能在划分平面的另一侧。

K-D树的优势是在处理多维数据点时提供了比线性搜索更快的查询时间,尤其是在数据点数量很大时。然而,当维数增加时,K-D树的效率会下降,这种现象被称为“维度的诅咒”。在这种情况下,可能需要考虑其他类型的数据结构,如球树或近似最近邻方法。

总结:K-D树是一颗二叉树,其中每个节点都是k维数值点,便于高维查找,当使用最近邻搜索时可能需要检查两边的子树,因为最近的点可能在划分平面的另一侧。维数增加会导致查找效率下降。

2.2 kd_nearest3 执行K-D树最近邻搜索

  1. kd_nearest_i()具体搜索
    1. 搜索过程,分割不会减小搜索空间维度。
      步骤:整个空间,分割空间,子区域,左/右子树的(超)矩形

    2. 近边界(nearer boundary)和远边界(farther boundary)的更新
      在这里插入图片描述

    3. K-D树搜索结束条件,效率高,大数据量搜索效率提升明显,随着维度升高,效率下降。
      在这里插入图片描述
      在这里插入图片描述

static void kd_nearest_i(struct kdnode *node, const double *pos, struct kdnode **result, double *result_dist_sq, struct kdhyperrect *rect)
{int dir = node->dir;//在第几维上分割搜索空间int i;double dummy, dist_sq;struct kdnode *nearer_subtree, *farther_subtree;double *nearer_hyperrect_coord, *farther_hyperrect_coord;//近边界和远边界/* Decide whether to go left or right in the tree */dummy = pos[dir] - node->pos[dir];if (dummy <= 0)  //pos <= node, pos放node左边{nearer_subtree = node->left;farther_subtree = node->right;//在第dir维上更新划分依据:rect->max离node更近,rect->min离node更远,这是指针操作,不是数值加法,//指针指向了新的划分边界,近边界是rect的第dir维max,远边界是dir维minnearer_hyperrect_coord = rect->max + dir;farther_hyperrect_coord = rect->min + dir;}else{nearer_subtree = node->right;farther_subtree = node->left;nearer_hyperrect_coord = rect->min + dir;farther_hyperrect_coord = rect->max + dir;}if (nearer_subtree){/* Slice the hyperrect to get the hyperrect of the nearer subtree */  //递归时其实已经改变了rect的值dummy = *nearer_hyperrect_coord;//备份*nearer_hyperrect_coord = node->pos[dir];//更新rect的judge的维度的值,下一次递归使用这新维度的值来进行judge,新维度上该值为本次root节点上该维度的值(相当于本次用过了这个维度)/* Recurse down into nearer subtree */kd_nearest_i(nearer_subtree, pos, result, result_dist_sq, rect);/* Undo the slice */*nearer_hyperrect_coord = dummy;}/* Check the distance of the point at the current node, compare it* with our best so far */dist_sq = 0;for (i = 0; i < rect->dim; i++){dist_sq += SQ(node->pos[i] - pos[i]);}if (dist_sq < *result_dist_sq){*result = node;*result_dist_sq = dist_sq;}if (farther_subtree){/* Get the hyperrect of the farther subtree */dummy = *farther_hyperrect_coord;*farther_hyperrect_coord = node->pos[dir];/* Check if we have to recurse down by calculating the closest* point of the hyperrect and see if it's closer than our* minimum distance in result_dist_sq. *///在遍历完near子树,计算完result_dist_sq之后,计算farther子树上是否需要继续遍历(farther的dist是否比现有最小的dist小,如果小,则需要继续搜)if (hyperrect_dist_sq(rect, pos) < *result_dist_sq){/* Recurse down into farther subtree */kd_nearest_i(farther_subtree, pos, result, result_dist_sq, rect);}/* Undo the slice on the hyperrect */*farther_hyperrect_coord = dummy;}
}
  1. 搜索结束后rlist_insert插入result node,插入之后,list->next指向的是itemd所指的kdnode
  2. kd_res_rewind使得rset指向result的knode。
  3. void* 类型安全转换为RRTNode3DPtr类型,即TreeNode*类型。

2.3 chooseParent部分实现

该部分作业中的代码和ZJU-FAST-LAB中的代码有所区别,后者主要考虑其他算法的兼容性,在数据结构上有所不同,而作业主要针对RRT*,所以数据结构更简单。

理解了K-D tree的数据结构以及RRT* 的改进点之后,实现该部分代码比较容易,这里使用了std::multimap,先计算所有range内的edge dist并插入std::multimap中,会自动按key升序排序,然后从前到后进行collision checking,找到第一个collision-free的就找到了Parent。

 // TODO Choose a parent according to potential cost-from-start values// ! Hints:// !  1. Use map_ptr_->isSegmentValid(p1, p2) to check line edge validity;// !  2. Default parent is [nearest_node];// !  3. Store your chosen parent-node-pointer, the according cost-from-parent and cost-from-start// !     in [min_node], [cost_from_p], and [min_dist_from_start], respectively;// !  4. [Optional] You can sort the potential parents first in increasing order by cost-from-start value;// !  5. [Optional] You can store the collison-checking results for later usage in the Rewire procedure.// ! Implement your own code inside the following loopif(use_chooseParent_) {std::multimap<double, std::pair<RRTNode3DPtr, double>> mmp_neighbor_nodes;for (auto &curr_node : neighbour_nodes){double dist2curr_node = calDist(curr_node->x, x_new);double dist_from_start = curr_node->cost_from_start + dist2curr_node;//不同前驱的更新的cost-to-comedouble tmp_cost_from_p = dist2curr_node;//cost-from-parent,即edge_costmmp_neighbor_nodes.insert(std::make_pair(dist_from_start, std::make_pair(curr_node, tmp_cost_from_p)));}int mmp_count = 0;for(auto &each_item : mmp_neighbor_nodes) {++mmp_count;if(map_ptr_->isSegmentValid(x_new, each_item.second.first->x)) {min_dist_from_start = each_item.first;cost_from_p = each_item.second.second;min_node = each_item.second.first;ROS_DEBUG("\nmmp_count: %d, neighbor size: %lu, collision-free, \tcur_dist_from_start: %.10f, cur_cost_from_p: %.10f",mmp_count, neighbour_nodes.size(), each_item.first, each_item.second.second);break;} else {ROS_DEBUG("\nmmp_count: %d, neighbor size: %lu, collisioned, continue, \tcur_dist_from_start: %.10f, cur_cost_from_p: %.10f",mmp_count, neighbour_nodes.size(), each_item.first, each_item.second.second);}}}// ! Implement your own code inside the above loop

在这里插入图片描述

2.4 Rewire

该部分代码如下:

 /* 3.rewire */// TODO Rewire according to potential cost-from-start values// ! Hints:// !  1. Use map_ptr_->isSegmentValid(p1, p2) to check line edge validity;// !  2. Use changeNodeParent(node, parent, cost_from_parent) to change a node's parent;// !  3. the variable [new_node] is the pointer of X_new;// !  4. [Optional] You can test whether the node is promising before checking edge collison.// ! Implement your own code between the dash lines [--------------] in the following loop//如果没有rewire,这里的RRT*其实和RRT没有太大区别,唯一的区别就是选取parent时使用K-D树选取了最近邻的node,//而RRT遍历所有现有node,直接选取距离sampling node欧氏距离最近的node作为parentif(use_rewire_) {for (auto &curr_node : neighbour_nodes){double best_cost_before_rewire = goal_node_->cost_from_start;// ! -------------------------------------double cost_new_node_to_neighbor = calDist(new_node->x, curr_node->x);bool is_connected2neighbor = map_ptr_->isSegmentValid(new_node->x, goal_node_->x);bool is_better_path = curr_node->cost_from_start > cost_new_node_to_neighbor + new_node->cost_from_start;if(is_connected2neighbor && is_better_path) {changeNodeParent(curr_node, new_node, cost_new_node_to_neighbor);}// ! -------------------------------------//找到更优的解之后进行更新if (best_cost_before_rewire > goal_node_->cost_from_start){vector<Eigen::Vector3d> curr_best_path;fillPath(goal_node_, curr_best_path);path_list_.emplace_back(curr_best_path);solution_cost_time_pair_list_.emplace_back(goal_node_->cost_from_start, (ros::Time::now() - rrt_start_time).toSec());}}}/* end of rewire */

涉及到changeNodeParent()更改节点A的父节点,以及节点A的子节点的cost_from_start值。
因为改了节点A的父节点,则节点A的子节点的cost_from_start都会改变,需要更新,使用队列,层序遍历,changeNodeParent少量注释:

void changeNodeParent(RRTNode3DPtr &node, RRTNode3DPtr &parent, const double &cost_from_parent)
{//更改node A的parent,首先需要删掉node A与之前parent的edge,再添加新的edge,这里的操作就是remove旧nodeif (node->parent)node->parent->children.remove(node); //DON'T FORGET THIS, remove it form its parent's children list(因为是改变了前驱节点,所以之前的前驱节点要删掉)node->parent = parent;node->cost_from_parent = cost_from_parent;node->cost_from_start = parent->cost_from_start + cost_from_parent;parent->children.push_back(node);// for all its descedants, change the cost_from_start and tau_from_start;  改了节点A的父节点,则节点A的子节点的cost_from_start都会改变,需要更新,使用队列,层序遍历RRTNode3DPtr descendant(node);std::queue<RRTNode3DPtr> Q;Q.push(descendant);while (!Q.empty()){descendant = Q.front();Q.pop();for (const auto &leafptr : descendant->children){leafptr->cost_from_start = leafptr->cost_from_parent + descendant->cost_from_start;//主要是descendant->cost_from_start变了Q.push(leafptr);}}
}

2.5 对比实验

进行以下两组对比实验:

  1. 相同地图和start,不同goal时,RRT*有无rewire的对比
  2. 相同地图和start,不同goal时,RRT于RRT* 的对比(使用ZJU-FAST-LAB实验室的公开工作完成,其中RRT与RRT* 数据结构均为K-D tree)

2.5.1 实验1

由于RRT* 相较于RRT的改进主要在chooseParent和Rewire,而其中Rewire起主要作用,因为如果没有rewire,RRT*与RRT区别不大,所以实验1基于课程所给代码,通过是否添加Rewire来对比Rewire的优化效果。

fix地图和start(-15.4994, -20.669, 0.4),由于ramdom sampling存在随机性,所以针对每个fixed goal,每种算法跑5次,最终取均值,共选取4个不同位置的goal,如下图所示
在这里插入图片描述

  1. goal1:16.0521, 20.033, 0.64

RRT* without Rewire

组号first path time cost(ms)first path lenfinal path len
10.0018632456.416954.0687
20.0021394271.394555.3649
30.0008270360.8254.9993
40.00063471256.530355.5882
50.00044433163.24354.713
avg0.00590873361.6809454.94682

RRT* with Rewire

组号first path time cost(ms)first path lenfinal path len
10.0012977658.262653.4338
20.0022004860.822453.6165
30.00068617454.264952.3802
40.00068276159.324253.2708
50.00086719858.267852.2388
avg0.001146858.18852.988

goal1 RRT* with Rewire 与 RRT* without Rewire对比,first path time cost无明显差别,有Rewire操作时,final path len更优。

  1. goal2:2.6292, -16.682, 0.82

RRT* without Rewire

组号first path time cost(ms)first path lenfinal path len
10.00015310720.261219.0458
20.00051485320.794618.892
30.0001365723.804719.7088
40.0001081120.13819.326
50.00031274119.692819.1867
avg0.00024520.938319.2319

RRT* Rewire

组号first path time cost(ms)first path lenfinal path len
10.0025483921.355519.0077
20.001585962.13418.8554
30.00081632652.736618.9015
45.5032e-0524.238918.9784
50.00010163119.780819.0004
avg0.001036.049218.9487

goal2 RRT* with Rewire时first path time cost稍长,说明在这项相关性不强,仍然是with Rewire时final path更优。

  1. goal3:16.0176, -20.1551, 0.32

RRT* without Rewire

组号first path time cost(ms)first path lenfinal path len
10.0065900142.74132.7316
20.0026336353.116735.0014
30.00099861544.244433.8431
40.00031607955.437833.43
50.00096887537.143634.6206
avg0.002346.536733.9253

RRT* with Rewire

组号first path time cost(ms)first path lenfinal path len
10.014318756.055432.9007
20.0020517543.53433.8006
30.004313640.433233.0486
40.00094607641.524233.6446
50.00037117145.029532.6621
avg0.004445.315333.2113

goal3 with Rewire时path len更优。

  1. goal4:-12.3907 10.2501 1.74

RRT* without Rewire

组号first path time cost(ms)first path lenfinal path len
10.00023690252.821233.3467
20.00052635148.685435.6416
30.00013412642.512534.8541
40.0001427145.045933.6717
59.3826e-0535.194833.7898
avg0.000244.852034.2608

RRT* with Rewire

组号first path time cost(ms)first path lenfinal path len
10.0072862749.961434.5846
20.00067883941.10734.659
30.0037993639.873934.0933
40.001068641.036434.6245
50.00033840941.394132.8667
avg0.002642.674634.1656

goal3 with Rewire时path len更优。

整体结论:RRT* with Rewire时final patn len会更优(更短),对于fitst path time cost影响不大。即Rewire有效地提升了feasiable path的optimality。

2.5.2 实验2

其中还跑了RRT#,BRRT,BRRT*,后两种由于没有深入了解方法,就不在此介绍,下面根据实验结果得出初步结论。

结果

组号goalMethodfirst path use time(ms)first path lenfinal path len
1-19.5762, -13.4801, 0.82RRT0.00029947932.970332.9703
1-19.5762, -13.4801, 0.82RRT*0.00032290433.588623.9271
1-19.5762, -13.4801, 0.82RRT#13.2051739.10606223.8874
216.4704, 18.2802, 1.56RRT0.00019240763.180163.1801
216.4704, 18.2802, 1.56RRT*0.00064092356.57249.0807
216.4704, 18.2802, 1.56RRT#0.001382467.8848.8112
312.1996, -14.5833, 0.6RRT0.0010374349.025149.0251
312.1996, -14.5833, 0.6RRT*0.0013216337.714233.2019
312.1996, -14.5833, 0.6RRT#0.00033019940.870833.1705

在这里插入图片描述

在这里插入图片描述

结论

  1. RRT找到1条feasiable solution之后就不再进行优化,由于数据结构与RRT* 类似,所以找到first path时间与RRT相近,但RRT* first path通常比RRT更优。
  2. final path cost,RRT* 优于RRT,说明RRT* 的find parent和rewire对path起到了优化的作用。

T3 待做

在这里插入图片描述

在这里插入图片描述

ZJU-FAST-LAB代码中有Informed sampling和GuILD sampling的实现,都属于Informed RRT*,这个后面有时间再做。

本章完。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/250033.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySql主从同步,同步SQL_ERROR 1032解决办法

1.登录从库 mysql -u root -p 2.输入命令查看状态 SHOW SLAVE STATUS\G; 3.找到对应的错误数据位置 Slave_IO_Running: YesSlave_SQL_Running: NoReplicate_Do_DB: app_push_centerReplicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Tabl…

github连不上

github连不上 错误提示解决方案steam 采用Hosts加速 错误提示 fatal: unable to access ‘https://github.com/Ada-design/qianduan.git/’: Failed to connect to github.com port 443 after 21073 ms: Couldn’t connect to server 解决方案 下载steam https://steampp.ne…

Flutter的安装与环境配置

一、下载安装Futter&#xff1a; 1、Flutter中文文档&#xff1a; 安装和环境配置 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 2、下载 Futter SDK&#xff1a; Flutter中文文档 里面有&#xff0c;下载完成之后找个文件夹解压出来&#xff0c;最好不要将 Flu…

专业数据治理:数据中台系统塑造企业未来的数字化运营管理新秩序

随着信息化进程的快速推进&#xff0c;数据已然成为企业最为宝贵的资产。对于众多企业而言&#xff0c;如何有效整合、治理以及充分利用这些数据&#xff0c;使之成为推动业务发展的引擎&#xff0c;成为当前难题。数据中台应运而生&#xff0c;它被认为是企业数字化转型的至关…

深度解读NVMe计算存储协议-3

在NVMe计算存储架构中&#xff0c;Copy命令用于在不同类型的命名空间之间进行数据复制&#xff1a; Memory Copy命令&#xff1a;定义于SLM&#xff08;Subsystem Local Memory&#xff09;命令集&#xff0c;主要用于从非易失性存储命名空间&#xff08;NVM namespaces&#x…

“IT行业的黄金证书:你必须了解的顶级认证“

文章目录 每日一句正能量前言一、网络方向&#xff1a;思科认证/软考二、华为认证三、系统方向&#xff1a;红帽认证四、数据库方向&#xff1a;Oracle认证五、信息安全方向&#xff1a;CISP/CISSP认证六、管理方向&#xff1a;PMP认证IT行业证书的价值和作用后记 每日一句正能…

XML传参方式

export function groupLoginAPI(xmlData) {return http.post(/tis/group/1.0/login, xmlData, {headers: {Content-Type: application/xml,X-Requested-With: AAServer/4.0,}}) }import {groupLoginAPI} from "../api/user"; function (e) { //xml格式传参let groupX…

前端JavaScript篇之let、const、var的区别

目录 let、const、var的区别 let、const、var的区别 let、const和var是JavaScript中用于声明变量的关键字&#xff0c;它们之间有一些区别。 首先&#xff0c;var是在ES5中引入的关键字&#xff0c;而let和const是在ES6中引入的。在ES6之前&#xff0c;我们只能使用var来声明…

双非本科准备秋招(13.1)—— 力扣 栈、队列与堆

1、103. 二叉树的锯齿形层序遍历 昨天做的二叉树的层序遍历&#xff0c;把代码直接拿过来。 这个题要求的是一个Z型遍历&#xff0c;如下图。 用一个变量f记录正反顺序&#xff0c;然后使用LinkedList记录答案&#xff0c;下图可以看到LinkedList继承了Deque&#xff0c;所以…

【开源】JAVA+Vue.js实现电子元器件管理系统

目录 一、摘要1.1 项目简介1.2 项目录屏 二、研究内容三、界面展示3.1 登录&注册&主页3.2 元器件单位模块3.3 元器件仓库模块3.4 元器件供应商模块3.5 元器件品类模块3.6 元器件明细模块3.7 元器件类型模块3.8 元器件采购模块3.9 元器件领用模块3.10 系统基础模块 四、…

C++学习Day01之namespace命名空间

目录 一、程序及输出1.1 命名空间用途&#xff1a; 解决名称冲突1.2 命名空间内容1.3 命名空间必须要声明在全局作用域下1.4 命名空间可以嵌套命名空间1.5 命名空间开放&#xff0c;可以随时给命名空间添加新的成员1.6 命名空间可以是匿名的1.7 命名空间可以起别名 二、分析与总…

02.PostgreSQL运算符

1. 算术运算符 算术运算符 描述 示例 + 加法运算符 SELECT A+B - 减法运算符 SELECT A-B * 乘法运算符 SELECT A*B / 除法运算符 SELECT A/B % 取余运算符 SELECT A%B 1.1 加法与减法操作符 SELECT 100,100+11,100-11,100+23.0,100-23.0 运算结果 由此得出结论: 一个整数加上…

微服务-微服务Alibaba-Nacos 源码分析 (源码流程图)

客户端流程 客户端心跳与实例往服务端注册

Linux部署幻兽帕鲁服务器,PalWorld开服联机教程,保姆级教程

------另一个号申请积分-------- Linux系统搭建PalWorld私服&#xff0c;幻兽帕鲁开服联机教程&#xff0c;保姆级教程 最近这游戏挺火&#xff0c;很多人想跟朋友联机&#xff0c;如果有专用服务器&#xff0c;就不需要房主一直开着电脑&#xff0c;稳定性也好得多。 幻兽帕…

单细胞scRNA-seq测序基础知识笔记

单细胞scRNA-seq测序基础知识笔记 scRNA-seq技术scRNA-seq 分析流程数据预处理聚类标准化数据筛选有用的数据数据降维聚类 Clustering 注释细胞类型 scRNA数据分析结尾 该笔记来源于 B站up 江湾青年 scRNA-seq技术 首先是如何测序&#xff0c;上图瓶中有很多细胞&#xff0c;…

npm 和 yarn 的使用

安装 yarn npm i yarn -g查看版本 npm -v yarn --version切换 npm/yarn 的下包镜像源 // 查看当前的镜像源 npm config get registry// 切换淘宝镜像源 // 新的淘宝源&#xff0c;旧的淘宝源已于2022年05月31日零时起停止服务 npm config set registry https://registry.…

figure方法详解之清除图形内容

figure方法详解之清除图形内容 一 clf():二 clear():三 clear()方法和clf()方法的区别&#xff1a; 前言 Hello 大家好&#xff01;我是甜美的江。 在数据可视化中&#xff0c;Matplotlib 是一个功能强大且广泛使用的库&#xff0c;它提供了各种方法来创建高质量的图形。在 Mat…

Ajax 详解及其使用

Ajax&#xff08;Asynchronous JavaScript and XML&#xff09;是一种在客户端与服务器之间进行异步通信的技术&#xff0c;它允许网页在不重新加载整个页面的情况下&#xff0c;与服务器交换数据并更新部分网页内容。Ajax 的核心是XMLHttpRequest&#xff08;XHR&#xff09;对…

Java的JVM学习一

一、java中的内存结构如何划分 栈和堆的区别&#xff1a; 栈负责处理运行&#xff0c;堆负债处理存储。 区域名称作用虚拟机栈用于存储正在执行的每个Java方法&#xff0c;以及其方法的局部变量表等。局部变量表存放了便器可知长度的各种基本数据类型&#xff0c;对象引用&am…

部署实战--修改jar中的文件并重新打包成jar文件

一.jar文件 JAR 文件就是 Java Archive &#xff08; Java 档案文件&#xff09;&#xff0c;它是 Java 的一种文档格式JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中&#xff0c;多出了一个META-INF/MANIFEST.MF 文件META-INF/MANIFEST.MF 文件在生成 JAR 文件的时候…