基于3D点云的散货库存体积计算

首先,你需要散货库存的点云。 我将使用 IntelRealSense 捕获的散货库存的 .ply文件。 然而,任何其他产生点云的成像技术都同样有效。

点击这里查看本教程的 Github 上的代码。

NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割 

1、定位点云

让我们打开点云并检查它在笛卡尔 3D 空间中的位置。 Open3D 拥有现成的点云可视化工具。 我们还将在可视化中添加轴,以便我们可以找到库存:

pcd = o3d.io.read_point_cloud("data/stockpile.ply")
axes = o3d.geometry.TriangleMesh.create_coordinate_frame()
o3d.visualization.draw_geometries([pcd, axes])

我们可以看到库存和地板。 我们还可以看到它相对于轴是无方向的。 为了计算体积,我们需要使地板平行于 XY 平面并将其平移到原点。 为此,我们需要对地板进行分割。 再次幸运的是,Open3D 为我们提供了帮助:

plane_model, inliers = pcd.segment_plane(distance_threshold=0.01,
                                         ransac_n=3,
                                         num_iterations=10000)
[a, b, c, d] = plane_model
plane_pcd = pcd.select_by_index(inliers)
plane_pcd.paint_uniform_color([1.0, 0, 0])
stockpile_pcd = pcd.select_by_index(inliers, invert=True)
stockpile_pcd.paint_uniform_color([0, 0, 1.0])
o3d.visualization.draw_geometries([plane_pcd, stockpile_pcd, axes])

现在我们已经分割了地板平面,我们可以使用一些数学方法通过旋转和平移点云将地板带到 XY 平面:

plane_pcd = plane_pcd.translate((0,0,d/c))
stockpile_pcd = stockpile_pcd.translate((0,0,d/c))
cos_theta = c / math.sqrt(a**2 + b**2 + c**2)
sin_theta = math.sqrt((a**2+b**2)/(a**2 + b**2 + c**2))
u_1 = b / math.sqrt(a**2 + b**2 )
u_2 = -a / math.sqrt(a**2 + b**2)
rotation_matrix = np.array([[cos_theta + u_1**2 * (1-cos_theta), u_1*u_2*(1-cos_theta), u_2*sin_theta],
                            [u_1*u_2*(1-cos_theta), cos_theta + u_2**2*(1- cos_theta), -u_1*sin_theta],
                            [-u_2*sin_theta, u_1*sin_theta, cos_theta]])
plane_pcd.rotate(rotation_matrix)
stockpile_pcd.rotate(rotation_matrix)
o3d.visualization.draw_geometries([plane_pcd, stockpile_pcd, axes])

我们可以丢弃地板,因为我们只关心库存:

o3d.visualization.draw_geometries([stockpile_pcd])

正如我们所看到的,我们有一些错误的分割点,特别是在 3D 图像的边界处。 我们需要删除这些异常值。 你知道这是怎么回事,Open3D 有一个专门用于此目的的函数:

cl, ind = stockpile_pcd.remove_statistical_outlier(nb_neighbors=30,
                                                    std_ratio=2.0)
stockpile_pcd = stockpile_pcd.select_by_index(ind)
o3d.visualization.draw_geometries([stockpile_pcd])

好的,我们终于对库存点云进行了分割并正确定向。 我们现在可以跳到体积计算部分。 耶!

2、计算体积

我们无法从点云计算体积,我们需要一个网格。 为了从点云到网格,我们可以使用表面重建算法。 表面重建是一个不适定(ill-posed)问题,这意味着没有完美的解决方案,算法基于启发式。 然而,我们可以通过利用库存(或实际上任何地形)的一个特殊特征来避免这些算法,即表面受 XY 平面约束。 没有 2 个点具有相同的 x 和 y,并且从 XY 平面上方观察该表面,我们将看到一个“平面”表面,也就是说,如果在任何点垂直穿过 XY 平面,只会穿过我们的表面一次。

我们将在 2D 空间中构建用于表面重建的三角化。 我们将通过删除 Z 值将 PCD 投影到 XY 平面。 我们将使用 Delaunay 算法获得 2D 点的三角测量。 然后我们将保留 Delaunay 输出的三角形,但我们将使用 3D 顶点而不是 2D 顶点。 这种方法有时称为“Delaunay 2.5D”

这听起来可能很复杂,但在代码中很容易看出:

downpdc = stockpile_pcd.voxel_down_sample(voxel_size=0.05)
xyz = np.asarray(downpdc.points)
xy_catalog = []
for point in xyz:
    xy_catalog.append([point[0], point[1]])
tri = Delaunay(np.array(xy_catalog)

tri 将为我们提供三角形的索引。 在 2D 空间中,它看起来像:

使用带有 3D 点的三角形将为我们提供库存的表面:

surface = o3d.geometry.TriangleMesh()
surface.vertices = o3d.utility.Vector3dVector(xyz)
surface.triangles = o3d.utility.Vector3iVector(tri.simplices)
o3d.visualization.draw_geometries([surface], mesh_show_wireframe=True)

一旦有了表面,我们就准备好获得体积了。 可以采用不同的方法。 我将计算表面每个三角形相对于 XY 平面(高度 0)的体积。 然后将所有三角形的体积相加。

Open3D 网格中的三角形定义顶点的索引,而不是顶点,因此我们需要平移三角形,以便它们的值实际上是 3D 点,而不是顶点列表的索引:

def get_triangles_vertices(triangles, vertices):
    triangles_vertices = []
    for triangle in triangles:
        new_triangles_vertices = [vertices[triangle[0]], vertices[triangle[1]], vertices[triangle[2]]]
        triangles_vertices.append(new_triangles_vertices)
    return np.array(triangles_vertices)

然后我们需要一个函数来计算每个三角形下的体积。 根据布尔巴克数学家凯文·布朗的文章,我们可以将这样的函数定义为:

def volume_under_triangle(triangle):
    p1, p2, p3 = triangle
    x1, y1, z1 = p1
    x2, y2, z2 = p2
    x3, y3, z3 = p3
    return abs((z1+z2+z3)*(x1*y2-x2*y1+x2*y3-x3*y2+x3*y1-x1*y3)/6)

然后,获取库存的体积就像添加所有三角形的体积一样简单:

volume = reduce(lambda a, b:  a + volume_under_triangle(b), get_triangles_vertices(surface.triangles, surface.vertices), 0)
print(f"The volume of the stockpile is: {round(volume, 4)} m3")

希望你喜欢这篇文章😃


原文链接:基于点云的散货体积计算 - BimAnt

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

二叉树的中序遍历 - LeetCode 热题 36

大家好!我是曾续缘😃 今天是《LeetCode 热题 100》系列 发车第 36 天 二叉树第 1 题 ❤️点赞 👍 收藏 ⭐再看,养成习惯 二叉树的中序遍历 给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。 示例 1: 输…

爬楼梯(c)

文章目录 描述分析思路关键代码运行结果 描述 给定一个整数数组 cost ,其中 cost[i]是从楼梯第i 个台阶向上爬需要支付的费用,下标从0开始。-旦你支付此费用,即可选择向上爬一个或者两个台阶 要求:请你计算并返回达到楼梯顶部的…

4.17

while(1) { HAL_ADC_Start(&hadc); adcVal HAL_ADC_GetValue(&hadc); TIM3->CCR3 adcVal-2000; } 1.总结串口的发送和接收功能使用到的函数 HAL_UART_Transmit_DMA(&huart1,"hello world",strlen("hello world")); HAL_UART_Tr…

Linux:如何删除指定时间之前修改的文件?

1、与文件有关的时间 在说明如何删除符合这种要求的文件之前,先来看看与文件有关的有哪些时间 简名全名中文名含义atimeaccess time访问时间文件中的数据最后被访问的时间mtimemodify time修改时间文件中的数据最后被修改的时间ctime change time变化时间文件的元…

JavaSE高阶篇-IO流

第一部分 file类 1)File类 计算机常识: 1.名字为".jpg"的一定是图片吗? 不一定,有可能是文件夹 2.什么叫做文本文档: 用记事本打开,人能看懂的文件 比如:.txt .html .css等 .doc -> 不是 …

如何安装 IntelliJ IDEA 最新版本——详细教程

IntelliJ IDEA 简称 IDEA,被业界公认为最好的 Java 集成开发工具,尤其在智能代码助手、代码自动提示、代码重构、代码版本管理(Git、SVN、Maven)、单元测试、代码分析等方面有着亮眼的发挥。IDEA 产于捷克,开发人员以严谨著称的东欧程序员为主…

vscode 搭建stm32开发环境记录(eide+cortex-debug+jlink)

前言 clion使用的快过期了,所以就准备使用vscode 来代替clion作为代码开发环境 vscode 插件安装 创建个空白工程 添加项目相关的源文件,和配置宏定义和头文件目录 编译和烧录(ok) 结合cortex-debug 结果(测试ok)

数据可视化-ECharts Html项目实战(13)

在之前的文章中,我们深入学习ECharts动态主题切换和自定义ECharts主题。想了解的朋友可以查看这篇文章。同时,希望我的文章能帮助到你,如果觉得我的文章写的不错,请留下你宝贵的点赞,谢谢。 数据可视化-ECharts Html项…

Linux执行命令监控详细实现原理和使用教程,以及相关工具的使用

Linux执行命令监控详细实现原理和使用教程,以及相关工具的使用。 0x00 背景介绍 Linux上的HIDS需要实时对执行的命令进行监控,分析异常或入侵行为,有助于安全事件的发现和预防。为了获取执行命令,大致有如下方法: 遍…

MySQL-笔记-06.数据高级查询

目录 6.1 连接查询 6.1.1 交叉连接(cross join) 6.1.2 内连接(inner join) 6.1.3 外连接(outer join) 6.1.3.1 左外连接(left [outer] join) 6.1.3.2 右外连接(rig…

第2章:车辆纵向控制

2.1 车辆纵向动力学模型 注:车辆的纵向控制是指控制车辆行驶方向上的加减速,使得汽车可以按照期望的速度行驶,并保持安全的前后车距(即对汽车油门 / 刹车的控制); 2.1.1 车辆纵向受力模型 :轮胎…

SpringBootSpringCloud升级可能会出现的问题

1.背景 之前负责过我们中台的SpringBoot和Cloud的升级,特次记录分享一下项目中可能出现的问题,方便后续的人快速定位问题。以及下述选择的解决方案都是基于让升级的服务影响和改动最小以及提供通用的解决方案的提前进行选择的。 1.1版本说明 升级前&a…

OpenCV基本图像处理操作(十)——图像特征harris角点

角点 角点是图像中的一个特征点,指的是两条边缘交叉的点,这样的点在图像中通常表示一个显著的几角。在计算机视觉和图像处理中,角点是重要的特征,因为它们通常是图像中信息丰富的区域,可以用于图像分析、对象识别、3D…

JavaSE中的String类

1.定义方式 常见的三种字符串构造 public class Test1 {public static void main(String[] args) {// 使用常量串构造String str1 "abc";System.out.println(str1);// 直接newString对象String str2 new String("ABC");System.out.println(str2);// 使用…

【Linux学习】Linux指令(四)

文章标题 🚀zip/unzip指令:🚀tar指令(重要):🚀uname –r指令:🚀关机指令🚀几个常用操作 🚀zip/unzip指令: zip 与 unzip的安装 yum i…

Day20-【Java SE高级】单元测试 反射 注解 动态代理

一、单元测试 就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试。 1. 咱们之前是如何进行单元测试的?有啥问题? 只能在main方法编写测试代码,去调用其他方法进行测试。无法实现自动化测试,一个方法测试失败,可能…

学习在Debian系统上安装Shadowsocks教程

学习在Debian系统上安装Shadowsocks教程 安装shadowsocks-libev及其所需的依赖启动Shadowsocks服务:如果你想要通过代理本地流量,你可以使用ss-local:启动并设置ss-local:查看状态本地连接 安装shadowsocks-libev及其所需的依赖 …

量化交易为什么独宠Python

“我在学一门叫Python的语言”。“什么是Python,没听说过啊,为什么不学C啊”。这是发生在2014年,上海的一家量化基金,量化研究员和老板之间的对话。 “我想问一下关于Python的课程,什么时候能开班”。“Python啊&#…

数据结构-栈和队列刷题集(长期更新)

文章目录 万能计算器的实现以及源码分析1. leetcode 150 逆波兰表达式求值 万能计算器的实现以及源码分析 /*** 我们尝试写一个完整版的计算器,由于计算机不能很好的识别括号,所以一般要转换为逆波兰表达式求解* 思路解析 :* 1. 输入一个 中缀表达式* 2. 中缀表达式转化为list…

Python 数据结构和算法实用指南(一)

原文:zh.annas-archive.org/md5/66ae3d5970b9b38c5ad770b42fec806d 译者:飞龙 协议:CC BY-NC-SA 4.0 前言 数据结构和算法是信息技术和计算机科学工程学习中最重要的核心学科之一。本书旨在提供数据结构和算法的深入知识,以及编程…