环状点云内插思路分享

大家好啊,今天来聊一下环状点云内插的事儿,介绍一下我的思路。

如下图,有一片环状点云,如果需要向其内部插入点,应该怎么做?

我的思路其实非常简单,

1、抽稀环状点云

2、将抽稀后环状点云排序并构成闭合多边形

3、计算抽稀后的环状点云的包围盒,在包围中随机生成若干点,判断这些点是否在上一步构成的闭合多边形内部

4、输出结果

给大家看一下效果吧,这是抽稀后的环状点云:

这是向其内部插值填充后的结果:

完整代码如下,引用的函数我也补充在下面了:

void fullfill2(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
    //1、环状点云抽稀
    cloud = Evenly_Dilute(cloud, 50);
	//2、环状点云排序
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud2(new pcl::PointCloud<pcl::PointXYZ>);
    sortPointCloudLoop(cloud, cloud2);
	//3、环状点云包围盒计算
    std::vector<float> edge_length;
    pcl::PointXYZ minPoint, maxPoint;
    pcl::getMinMax3D(*cloud2, minPoint, maxPoint);
	//4、生成随机点并验证是否在多边形内
    pcl::PointCloud<pcl::PointXYZ>::Ptr result(new pcl::PointCloud<pcl::PointXYZ>);
    for (int i = 0; i < 10000; i++)
    {
        pcl::PointXYZ randomPoint;
        randomPoint.x = generateRandomDouble(minPoint.x, maxPoint.x);
        randomPoint.y = generateRandomDouble(minPoint.y, maxPoint.y);
        randomPoint.z = 0;
        if (pointcloud_polygonsContain(cloud2, randomPoint))
        {
            result->points.push_back(randomPoint);
        }
    }
    //5、保存结果并退出
    pcl::io::savePCDFile("pIVp.pcd", *cloud);
}

均匀抽稀这块的函数我就省略了,大家自己写吧,不是很困难。抽稀主要是为了方便下一步排序,如果点数过多,存在排序错误的风险。

bool sortPointCloudLoop(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_in,
	pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_out) 
{
	// 检查输入点云
	if (cloud_in->empty()) {
		std::cerr << "输入点云为空!" << std::endl;
		return false;
	}

	int n_points = cloud_in->size();
	if (n_points <= 1) {
		*cloud_out = *cloud_in;
		return true;
	}

	// 创建KD树用于近邻搜索
	pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;
	kdtree.setInputCloud(cloud_in);

	// 用于存储排序结果的索引
	std::vector<int> sorted_indices;
	sorted_indices.reserve(n_points);

	// 从第一个点开始
	int current_point_idx = 0;
	sorted_indices.push_back(current_point_idx);

	// 用于标记点是否已被访问
	std::vector<bool> visited(n_points, false);
	visited[current_point_idx] = true;

	// 找出所有点的顺序
	for (int i = 0; i < n_points - 1; ++i) {
		std::vector<int> pointIdxNKNSearch(2);
		std::vector<float> pointNKNSquaredDistance(2);

		// 查找当前点的最近邻
		if (kdtree.nearestKSearch(cloud_in->points[current_point_idx], 2, pointIdxNKNSearch, pointNKNSquaredDistance) < 2) {
			std::cerr << "无法找到足够的邻居点!" << std::endl;
			continue;
		}

		// 获取最近的未访问点
		int next_point_idx = -1;

		// 检查第一个最近邻是否已访问
		if (!visited[pointIdxNKNSearch[0]]) {
			next_point_idx = pointIdxNKNSearch[0];
		}
		// 检查第二个最近邻是否已访问
		else if (pointIdxNKNSearch.size() > 1 && !visited[pointIdxNKNSearch[1]]) {
			next_point_idx = pointIdxNKNSearch[1];
		}
		// 如果最近的两个邻居都已访问,查找下一个最近的未访问点
		else {
			float min_dist = std::numeric_limits<float>::max();
			for (int j = 0; j < n_points; ++j) {
				if (!visited[j]) {
					float dist = pcl::squaredEuclideanDistance(cloud_in->points[current_point_idx], cloud_in->points[j]);
					if (dist < min_dist) {
						min_dist = dist;
						next_point_idx = j;
					}
				}
			}
		}

		// 如果找不到下一个点,说明排序已完成
		if (next_point_idx == -1) {
			break;
		}

		// 添加下一个点并标记为已访问
		sorted_indices.push_back(next_point_idx);
		visited[next_point_idx] = true;
		current_point_idx = next_point_idx;
	}

	// 构建排序后的点云
	cloud_out->clear();
	cloud_out->reserve(sorted_indices.size());

	for (int idx : sorted_indices) {
		cloud_out->push_back(cloud_in->points[idx]);
	}

	return true;
}
double generateRandomDouble(double min, double max) 
{
    std::random_device rd; // 随机设备
    std::mt19937 gen(rd()); // 使用Mersenne Twister算法生成随机数
    std::uniform_real_distribution<> dis(min, max); // 定义范围
    return dis(gen); // 生成随机数
}
/// <summary>
/// 判断二维点是否在二维多边形内
/// </summary>
/// <param name="polygon">点云指针表征的多边形区域</param>
/// <param name="point">二维点坐标</param>
/// <returns>包含结果</returns>
bool pointcloud_polygonsContain(const pcl::PointCloud<pcl::PointXYZ>::Ptr& polygon, const pcl::PointXYZ& point)
{
	int n = polygon->size();
	if (n < 3) 
	{
		return false;
	}

	int intersections = 0;
	pcl::PointXYZ p1 = polygon->points[0];
	for (int i = 1; i <= n; ++i) 
	{
		pcl::PointXYZ p2 = polygon->points[i % n];

		if (point.y == p1.y && point.y == p2.y && point.x >= std::min(p1.x, p2.x) && point.x <= std::max(p1.x, p2.x)) 
		{
			return true;
		}

		if (p1.y <= point.y && p2.y > point.y || (p2.y <= point.y && p1.y > point.y)) 
		{
			float vt = (point.y - p1.y) / (p2.y - p1.y);
			if (p1.x + vt * (p2.x - p1.x) < point.x) 
			{
				intersections++;
			}
		}

		p1 = p2;
	}

	return intersections % 2 == 1;
}

最后我想说这个算法效率肯定不是很高,没有经过打磨,但是原理比较简单,大家可以试着对它进行二创,另外以本例来说,在包围盒内洒一万个豆子,能在多边形内的也就五百个,数量上可以根据自己的需求进行更改。

基于STM32 F4的永磁同步电机无位置传感器控制策略研究内容概要:本文围绕基于STM32 F4的永磁同步电机(PMSM)无位置传感器控制策略展开研究,重点探讨在不依赖物理位置传感器的情况下,如何通过算法实现对电机转子位置和速度的精确估计与控制。文中结合嵌入式开发平台STM32 F4,采用如滑模观测器、扩展卡尔曼滤波或高频注入法等先进观测技术,实现对电机反电动势或磁链的估算,进而完成无传感器矢量控制(FOC)。同时,研究涵盖系统建模、控制算法设计、仿真验证(可能使用Simulink)以及在STM32硬件平台上的代码实现与调试,旨在提高电机控制系统的可靠性、降低成本并增强环境适应性。; 适合人群:具备一定电力电子、自动控制理论基础和嵌入式开发经验的电气工程、自动化及相关专业的研究生、科研人员及从事电机驱动开发的工程师。; 使用场景及目标:①掌握永磁同步电机无位置传感器控制的核心原理与实现方法;②学习如何在STM32平台上进行电机控制算法的移植与优化;③为开发高性能、低成本的电机驱动系统提供技术参考与实践指导。; 阅读建议:建议读者结合文中提到的控制理论、仿真模型与实际代码实现进行系统学习,有条件者应在实验平台上进行验证,重点关注观测器设计、参数整定及系统稳定性分析等关键环节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值