基于QT+VTK的概率导向的传统RRT算法

 传统RRT在三维导向或者复杂环境中会出现迭代终止仍然抵达不了目标点,可将随机导向改为概率偏向目标点,代码在VS2019中编写,集成了QT5.14.2和VTK8.2.0,不会QT和VTK的建议学一学再来看,么么哒,代码如下:

 1.坐标系函数

void myRRT::CartesianCoordinateSystem()
{
    // 创建坐标轴演员
    vtkSmartPointer<vtkAxesActor> axesActor = vtkSmartPointer<vtkAxesActor>::New();
    axesActor->SetPosition(0, 0, 0);
    axesActor->SetTotalLength(1000, 1000, 1000);
    axesActor->SetScale(0.1, 0.1, 0.1);
    axesActor->SetXAxisLabelText(" ");
    axesActor->SetYAxisLabelText(" ");
    axesActor->SetZAxisLabelText(" ");
    // 将坐标轴演员添加到渲染器中
	vtkSmartPointer<vtkCamera> Camera = vtkSmartPointer<vtkCamera>::New();
    Camera->SetPosition(1000,-600,1000);
    Camera->SetFocalPoint(0, 200, 0);
    Camera->SetViewUp(0, 0, 1);
    renderer->AddActor(axesActor);
    renderer->SetActiveCamera(Camera);
	renderWindow->Render();
}

2. 立方体障碍物函数

void myRRT::DrawCube(double CubePosition[3], double CubeSize[3])
{
	vtkSmartPointer<vtkCubeSource> CubeSource = vtkSmartPointer<vtkCubeSource>::New();
	CubeSource->SetXLength(CubeSize[0]);
	CubeSource->SetYLength(CubeSize[1]);
	CubeSource->SetZLength(CubeSize[2]);
	CubeSource->Update();
	vtkSmartPointer<vtkPolyDataMapper> mapper_cube = vtkSmartPointer<vtkPolyDataMapper>::New();
	vtkSmartPointer<vtkActor> actor_cube = vtkSmartPointer<vtkActor>::New();
	mapper_cube->SetInputConnection(CubeSource->GetOutputPort());
	actor_cube->SetMapper(mapper_cube);
	actor_cube->GetProperty()->SetColor(0.5, 0.6, 0.6);
	actor_cube->SetPosition(CubePosition);
	renderer->AddActor(actor_cube);
	renderWindow->Render();

}

 3.球的绘制与立方体同理,使用的库为vtkSphereSource,一方面是生成障碍物,另一方面是使节点清晰可见

void myRRT::DrawSphere(double Center[3], double Randius)
{
	vtkSmartPointer<vtkSphereSource> sphereSource = vtkSmartPointer<vtkSphereSource>::New();	//创建球对象
	sphereSource->SetCenter(Center);															//设置球心位置
	sphereSource->SetRadius(Randius);															//设置球半径
	sphereSource->Update();
	vtkSmartPointer<vtkActor> actor_sphere = vtkSmartPointer<vtkActor>::New();
	vtkSmartPointer<vtkPolyDataMapper> mapper_sphere = vtkSmartPointer<vtkPolyDataMapper>::New();
	mapper_sphere->SetInputConnection(sphereSource->GetOutputPort());									//将球对象与映射器链接
	actor_sphere->SetMapper(mapper_sphere);														//映射器与演员绑定
	actor_sphere->GetProperty()->SetColor(1, 1, 1);												//设置球的颜色
	renderer->AddActor(actor_sphere);
	renderWindow->Render();
}

 4.绘制直线:直线的绘制是为了连接RRT算法在迭代时生成的节点

void myRRT::DrawLine(double StartPoint[3], double TargetPoint[3])
{
	vtkSmartPointer<vtkLineSource> LineSource = vtkSmartPointer<vtkLineSource>::New();
	vtkSmartPointer<vtkActor> actor_line = vtkSmartPointer<vtkActor>::New();
	vtkSmartPointer<vtkPolyDataMapper> mapper_line = vtkSmartPointer<vtkPolyDataMapper>::New();
	LineSource->SetPoint1(StartPoint);
	LineSource->SetPoint2(TargetPoint);
	LineSource->Update();
	mapper_line->SetInputConnection(LineSource->GetOutputPort());
	actor_line->SetMapper(mapper_line);
	actor_line->GetProperty()->SetColor(1, 0, 0);
	renderer->AddActor(actor_line);
	renderWindow->Render();
}

5.随机障碍物生成,其中ObstacleNum为障碍物数量,ObstacleInfomation为存储障碍物信息的数组,随机概率生成球或立方体障碍物,且位置随机,大小随机,数量取随机概率,大于0.5为球,小于0.5为立方体

void myRRT::RandomObstacle(int ObstacleNum)
{
	double ObstaclePosition[3]; double ObstacleSize[3];
	// 使用当前时间作为随机数生成器的种子  
	std::srand(std::time(0));
	//随机树生成
	std::uniform_real_distribution<double> selectCubeOrSphere(0.0, 1.0);
	std::default_random_engine select1(std::rand());
	std::uniform_real_distribution<double> selectObstaclePosition(100, 400);
	std::default_random_engine select3(std::rand());
	std::uniform_real_distribution<double> selectObstacleSize(30, 50);
	std::default_random_engine select4(std::rand());
	for (int i = 0; i < ObstacleNum; i++)
	{
		if (selectCubeOrSphere(select1) < 0.5)
		{
			//方形障碍物
			ObstaclePosition[0] = selectObstaclePosition(select3); ObstaclePosition[1] = selectObstaclePosition(select3); ObstaclePosition[2] = selectObstaclePosition(select3);
			ObstacleSize[0] = selectObstacleSize(select4); ObstacleSize[1] = selectObstacleSize(select4); ObstacleSize[2] = selectObstacleSize(select4);
			DrawCube(ObstaclePosition, ObstacleSize);
			ObstacleInformation[i][0] = ObstaclePosition[0]; ObstacleInformation[i][1] = ObstaclePosition[1]; ObstacleInformation[i][2] = ObstaclePosition[2];
			ObstacleInformation[i][3] = ObstacleSize[0]; ObstacleInformation[i][4] = ObstacleSize[1]; ObstacleInformation[i][5] = ObstacleSize[2];
			ObstacleInformation[i][6] = 1;
		}
		else
		{
			//球形障碍物
			ObstaclePosition[0] = selectObstaclePosition(select3); ObstaclePosition[1] = selectObstaclePosition(select3); ObstaclePosition[2] = selectObstaclePosition(select3);
			ObstacleSize[0] = 0; ObstacleSize[1] = selectObstacleSize(select4); ObstacleSize[2] = 0;
			DrawSphere(ObstaclePosition, ObstacleSize[1]);
			ObstacleInformation[i][0] = ObstaclePosition[0]; ObstacleInformation[i][1] = ObstaclePosition[1]; ObstacleInformation[i][2] = ObstaclePosition[2];
			ObstacleInformation[i][3] = ObstacleSize[0]; ObstacleInformation[i][4] = ObstacleSize[1]; ObstacleInformation[i][5] = ObstacleSize[2];
			ObstacleInformation[i][6] = 0;
		}
	}
	
}

 6.碰撞检测函数(注:这里的碰撞检测函数有点堆屎山代码的味道了),将RRT中的最近节点和新节点分为30段,每一段进行碰撞检测。立方体碰撞检测原理是点和不在立方体内部就行,遍历所有立方体,距离超过一定范围直接略过,球就简单了,直接判断点到球心的距离和半径进行比较就行了。(注:上一步随机障碍物生成时存储的障碍物信息的数组在这里就有用了)

 

bool myRRT::isCollisionChecking(double NearestNode[3], double NewNode[3])
{

	int LineDiv = 30;//将两个点的间距分割为30份
	double MiddleCheckNode[3];//中间点存储
	for (int j = 0; j < ObstacleNumber; j++)
	{
		if (ObstacleInformation[j][6] == 1)
		{
			double CubeAllowDistance = sqrt((ObstacleInformation[j][3] / 2) * (ObstacleInformation[j][3] / 2) + (ObstacleInformation[j][4] / 2) * (ObstacleInformation[j][4] / 2) + (ObstacleInformation[j][5] / 2) * (ObstacleInformation[j][5] / 2));
			double NodeToCube = sqrt((NearestNode[0] - ObstacleInformation[j][0]) * (NearestNode[0] - ObstacleInformation[j][0]) + (NearestNode[1] - ObstacleInformation[j][1]) * (NearestNode[1] - ObstacleInformation[j][1]) + (NearestNode[2] - ObstacleInformation[j][2]) * (NearestNode[2] - ObstacleInformation[j][2]));
			if (NodeToCube > (2*CubeAllowDistance))
			{
				continue;
			}
			else
			{
				for (int i = 1; i <= LineDiv; i++)
				{
					MiddleCheckNode[0] = NearestNode[0] + (NewNode[0] - NearestNode[0]) * i / LineDiv;
					MiddleCheckNode[1] = NearestNode[1] + (NewNode[1] - NearestNode[1]) * i / LineDiv;
					MiddleCheckNode[2] = NearestNode[2] + (NewNode[2] - NearestNode[2]) * i / LineDiv;
					//判断该点是否在障碍物内
					if (MiddleCheckNode[0] < (double(ObstacleInformation[j][0]) - double(ObstacleInformation[j][3]) / 2) || MiddleCheckNode[0] > (double(ObstacleInformation[j][0]) + double(ObstacleInformation[j][3] / 2)) || MiddleCheckNode[1] < (double(ObstacleInformation[j][1]) - double(ObstacleInformation[j][4] / 2)) || MiddleCheckNode[1] > (double(ObstacleInformation[j][1]) + double(ObstacleInformation[j][4] / 2)) || MiddleCheckNode[2] < (double(ObstacleInformation[j][2]) - double(ObstacleInformation[j][5] / 2)) || MiddleCheckNode[2] > (double(ObstacleInformation[j][2]) + double(ObstacleInformation[j][5] / 2)));
					else
					{
						return true;
					}
				}
			}
		}
		else
		{
			double NodeToSphere = sqrt((NearestNode[0] - ObstacleInformation[j][0]) * (NearestNode[0] - ObstacleInformation[j][0]) + (NearestNode[1] - ObstacleInformation[j][1]) * (NearestNode[1] - ObstacleInformation[j][1]) + (NearestNode[2] - ObstacleInformation[j][2]) * (NearestNode[2] - ObstacleInformation[j][2]));
			if (NodeToSphere >= (2 * double(ObstacleInformation[j][4])))
			{
				continue;
			}
			else
			{
				for (int k = 1; k <= LineDiv; k++)
				{
					MiddleCheckNode[0] = NearestNode[0] + (NewNode[0] - NearestNode[0]) * k / LineDiv;
					MiddleCheckNode[1] = NearestNode[1] + (NewNode[1] - NearestNode[1]) * k / LineDiv;
					MiddleCheckNode[2] = NearestNode[2] + (NewNode[2] - NearestNode[2]) * k / LineDiv;
					double MiddleNodeToSphere = sqrt((MiddleCheckNode[0] - ObstacleInformation[j][0]) * (MiddleCheckNode[0] - ObstacleInformation[j][0]) + (MiddleCheckNode[1] - ObstacleInformation[j][1]) * (MiddleCheckNode[1] - ObstacleInformation[j][1]) + (MiddleCheckNode[2] - ObstacleInformation[j][2]) * (MiddleCheckNode[2] - ObstacleInformation[j][2]));
					if (MiddleNodeToSphere > ObstacleInformation[j][4]);
					else
					{
						return true;
					}
				}
			}
		}
	}
	return false;

}

 7.RRT运行迭代主体,按照传统RRT步骤来的(不懂的可以去找找其他博客,这里不过多讲解),但是因为传统的随机性太强,所以加入了概率导向,随机概率小于0.6就生成随机点,大于则就以目标点为随机点。参数比如起始点,目标点啥的根据自己的要求调整就行

 

void myRRT::RRTStartButton()
{
	RRT_tree[0][0] = x_start; RRT_tree[0][1] = y_start; RRT_tree[0][2] = z_start;		//子节点
	RRT_tree[0][3] = x_start; RRT_tree[0][4] = y_start; RRT_tree[0][5] = z_start;		//父节点
	RRT_tree[0][6] = 0;																	//父节点到该节点的距离,取欧氏距离
	int count = 1;																		//节点计数
	double MapXLength = 500; double MapYLength = 500; double MapZLength = 500;			//设置地图的大小
	double RandNode[3];																	//随机节点
	double NearestNode[3];																//最近的节点
	double MinDistance = 2000;															//寻找随机节点到所有树中节点的最小距离值
	int index = 0;																		//寻找最近节点在RRT_tree中的位置
	double distance;																	//随机节点到最近节点的距离
	double NewNode[3];																	//创建存储新节点位置信息的数组
	double NewtoGoalDistance;															//新节点到目标点的距离
	for (int iter = 0; iter < 2000; iter++)
	{
		//RRT树的初始化	
		//开始搜索并构建树

		//Step1:在地图上随机采样一个点
		// 使用当前时间作为随机数生成器的种子  
		std::srand(std::time(0));
		//为了提高算法导向性,从0-1随机生成一个数,小于0.5则生成随机点,大于0.5则以目标点为随机点
		std::uniform_real_distribution<double> select(0.0, 1.0);
		std::default_random_engine selectnum(std::rand());
		// 创建一个均匀分布的随机数生成器,范围是0到1000  
		std::uniform_real_distribution<double> distribution(100.0, MapXLength);
		std::default_random_engine generator(std::rand());

		if (select(selectnum) <= 0.6)
		{
			RandNode[0] = distribution(generator);//随机点生成
			RandNode[1] = distribution(generator);
			RandNode[2] = distribution(generator);
		}
		else
		{
			RandNode[0] = x_goal;
			RandNode[1] = y_goal;
			RandNode[2] = z_goal;
		}
		//Step2:遍历树,从树中找到最近的节点
		for (int i = 0; i < count; i++)
		{
			//计算随机节点到树上节点的距离
			distance = (RandNode[0] - RRT_tree[i][0]) * (RandNode[0] - RRT_tree[i][0]) + (RandNode[1] - RRT_tree[i][1]) * (RandNode[1] - RRT_tree[i][1]) + (RandNode[2] - RRT_tree[i][2]) * (RandNode[2] - RRT_tree[i][2]);
			distance = sqrt(distance);
			if (i == 0) MinDistance = distance;
			if (distance < MinDistance)
 			{
				MinDistance = distance;
				index = i;								//找到最近的节点后,使用index记录该节点在树中的位置
			}
		}
		NearestNode[0] = RRT_tree[index][0];			//将最近的节点的xyz值传给NearestNode
		NearestNode[1] = RRT_tree[index][1];
		NearestNode[2] = RRT_tree[index][2];
		//Step3:扩展得到NewNode新节点
		NewNode[0] = NearestNode[0] + (RandNode[0] - NearestNode[0]) * ExpansionStep / MinDistance;//根据扩展步长和已知的最近节点距离算出扩展的新节点的位置
		NewNode[1] = NearestNode[1] + (RandNode[1] - NearestNode[1]) * ExpansionStep / MinDistance;
		NewNode[2] = NearestNode[2] + (RandNode[2] - NearestNode[2]) * ExpansionStep / MinDistance;
		if (isCollisionChecking(NearestNode, NewNode) == true)
		{
			std::this_thread::sleep_for(std::chrono::milliseconds(50));	//目前已知在此处加入一段延迟可以解决问题(单次循环调试正常运行,直接运行则会跳出整个迭代循环)
			continue;
		}
		count++;
		//Step 4:将NewNode插入树中
		RRT_tree[count-1][0] = NewNode[0]; RRT_tree[count-1][1] = NewNode[1]; RRT_tree[count-1][2] = NewNode[2];
		RRT_tree[count-1][3] = NearestNode[0]; RRT_tree[count-1][4] = NearestNode[1]; RRT_tree[count-1][5] = NearestNode[2];
		RRT_tree[count-1][6] = MinDistance;
		//Step 5:检查新节点是否到达目标点附近
		NewtoGoalDistance = sqrt((x_goal - RRT_tree[count-1][0]) * (x_goal - RRT_tree[count-1][0]) + (y_goal - RRT_tree[count-1][1]) * (y_goal - RRT_tree[count-1][1]) + (z_goal - RRT_tree[count-1][2]) * (z_goal - RRT_tree[count-1][2]));												//设置目标点阈值
		if (NewtoGoalDistance <= TargetPointThreshold)
		{
			double TargetPoint[3] = { x_goal, y_goal, z_goal };
			DrawSphere(NewNode, 3.0);
			DrawLine(NewNode, TargetPoint);
			DrawLine(NearestNode, NewNode);
			break;
		}
		//Step 6:将NearestNode和NewNode连接
		DrawSphere(NewNode, 3.0);
		DrawLine(NearestNode, NewNode);
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}

 8.代码运行结果如下:

 

 

 9.注:由于算法太过传统,仅供学习用,如果VS集成QT和VTK不会,CSDN上面一搜一大把。

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值