Raspberry Pi 激光扫描仪





准备工作:
材料:
- 树莓派35.00 美元
- Raspberry Pi 相机 V2 30.00 美元
- LED、电阻器和电线
- 3D打印丝材
- 12x12x0.125 木板
- M3 硬件
- 步进电机-14美元
- 线激光- 8 美元
- LN298 步进电机驱动器- 2.65 美元
- 金属按钮- 5 美元
工具:
- 烙铁
- 激光切割机
- 3D 打印机
- 螺丝刀
- 钳
灵感




作为一名狂热的创客,我从事 3D 打印和实体建模已有数年。我使用过许多不同的原型制作工具,从 CNC 路由器到激光切割机再到 3D 打印机。我当地的创客空间尚未购买的一款设备是 3D 扫描仪 - 我可以告诉你为什么要买。
便宜的(几百美元)不可靠,需要完美的条件,而且结果仍然很差。昂贵的……嗯,很贵,高达几千美元,在许多情况下,它的功能不值得。最重要的是,更多的时候,我选择从头开始测量和设计模型,而不是处理从扫描生成的表面网格。
因此,我想构建一个经济型独立扫描仪,以查看使用现成的组件扫描物体的效果如何。
经过一番研究,我发现许多 3D 扫描仪使用旋转平台,然后使用各种不同的传感器来测量与中心的距离,以构建旋转模型。其中许多使用与 Kinect 类似的双摄像头。我最终偶然发现了 Yscanner,这是一款使用激光的低分辨率扫描仪。从简单性和可行性来看,这种激光技术(其中激光相对于相机偏移照射以测量与中心的距离)似乎是一条明确的前进道路。
高层设计




本设计的核心部件是投射到物体垂直切片上的线激光。该投影可在 picamera 上捕捉,其透视校正,然后在图像处理之前进行过滤。在图像处理中,可以收集线的每个部分与物体中心之间的距离。在径向坐标中,该图片将产生 r 和 z 分量。然后通过将物体旋转到新的切片来实现第三维 Θ。第一张图显示了这一概念。
为了执行上述操作,我使用了 Raspberry Pi 作为我们的中央计算单元。我将步进电机和电机驱动器连接到 Pi,由外部 5V 电源供电,并由 Pi 的 GPIO 引脚控制。将线激光器放在 Pi 上的 3.3 V 线上,并将 PiCam 连接到 Pi 上的摄像头输入端。最后,安装了一个简单的下拉按钮和一个状态 LED,以向用户指示系统处于什么状态。完整的系统总结在系统框图中。
从一开始,就计划将电子设备放在一个激光切割的盒子中,并用 T 型槽和 M3 硬件固定在一起。电子设备将隐藏在底部隔间中,盖子可以方便地将物体放置在旋转托盘上。这个盖子是必要的,以尽量减少泄漏到系统中的光量,因为这种外部光会在最终扫描中产生噪音。
硬件





如上所示,在开始激光切割或 3D 打印之前,我使用 Autodesk Fusion 360 制作了我们设计的详细 3D 模型。总而言之,该设备是一个带盖子的简单盒子,盖子上装有激光切割的铰链。该设备有两个主要层:电子床和主床,两层之间有供电线穿行的孔。
我们的盒子大部分是用激光切割机制造的,设计是在 Fusion 360 中制作的,并在 Epilog Zing 40 W 激光切割机上切割。我们的设计如上图所示。从左上角向右移动,这些部件是主床、电子床、两个盖子部件、后片、前片和两个侧片。在主床上,有三个主要切口:一个用于安装步进电机,一个用于布线来自激光器的电线,一个用于布线 PiCam 的宽电缆。床部件有安装孔,用于固定 Pi、面包板和电机驱动器,还有一个更大的切口用于接入步进电机。盖子部件简单地卡在一起形成上面看到的三角形部件,铰链是一个简单的挤压件,其宽度与侧板孔的直径相同。后片和其中一个侧片在侧面有插槽,以便可以轻松访问 Pi 的端口(HDMI、USB、以太网、电源)。正面是一个简单的部件,我最终用手钻在上面打了孔,以便安装按钮和 LED。 从所有部件上可以看出,我们的零件通过 T 型接头和槽由 M3 硬件固定在一起。这是一种将激光切割的零件正交且牢固地固定在一起的方法。零件的鳍片与其他零件的槽对齐,边缘上的 T 形切口为 M3 螺母提供了空间,可将其卡入其中而不会旋转。这样我们就可以使用 M3 螺钉将零件锁定在一起,几乎没有摆动空间,而无需完全固定组装。
我选择用激光切割机来制作大部分作品,因为它速度快,使用方便。但是,我仍然需要 3D 打印一些作品,因为它们的 3D 几何形状更难用切割机制作。第一件作品是线激光支架。该部件将安装在主床上,与相机视角成 45 度角,并有一个孔,以便激光器可以紧密地摩擦配合在其中。我还必须创建一个电机支架,因为电机的轴太长了。支架摩擦配合到激光切割件中,并降低电机连接的平面,使旋转平台与主床齐平。
电子设备

该项目的接线硬件非常简单,因为 3D 扫描仪不需要太多外围设备。需要将电机、按钮、LED、激光器和摄像头连接到 Pi。如图所示,我确保将电阻器与我们使用的每个引脚串联,以保护引脚。一个 GPIO 引脚专用于控制状态 LED,当设备准备就绪时,LED 会亮起,当设备运行时,LED 会以 PWM 脉冲方式发出脉冲。另一个 GPIO 引脚连接到上拉按钮,当未按下按钮时显示 HIGH,按下按钮时显示 LOW。最后,我专门使用四个 GPIO 引脚来驱动步进电机。
由于我们的电机只需要步进一定程度而不需要控制速度,因此我们选择了一种更简单的步进电机驱动器 (L298N),它只需将控制线向上移动以馈入电机的输入即可。要了解如何在非常低的水平上操作步进电机,我们参考了 L298N 数据表和 Arduino 库。步进电机有一个磁芯,磁芯上伸出的手指极性交替。四根电线缠绕在一起以控制两个电磁铁,每个电磁铁为电机中每个相对的手指供电。因此,通过切换手指的极性,我们能够推动步进机一步。有了从硬件层面了解步进机工作原理的知识,我们能够更轻松地控制步进机。我们选择在实验室中使用 5V 电源为步进电机供电,而不是使用 Pi,因为它的最大电流消耗约为 0.8 A,这超过了 Pi 可以提供的电流。
软件






该项目的软件可分为四个相互作用的主要组件:图像处理、电机控制、网格创建和嵌入式功能。
作为软件的总结,我们可以看看第一张图。系统启动时,.bashrc 会自动登录 Pi 并开始运行我们的 Python 代码。系统点亮状态灯,让用户知道它已正确启动,并等待按下按钮。然后,用户可以放置要扫描的物品并关闭盖子。按下按钮后,LED 会闪烁,让用户知道设备正在工作。设备将在图像处理和电机控制之间循环,直到完成整个旋转并收集所有对象数据。最后,创建网格并将文件通过电子邮件发送到预先选择的电子邮件中。这将重新启动循环,机器只需按下按钮即可执行另一次扫描。
图像处理
首先要处理捕获的图像,以便将图像中存储的信息提取为可用于创建空间点阵列的形式。为此,我首先拍摄了平台上的物体以及由激光照射到盒子背面并散射而产生的所有背景噪音。这张照片的原始形式有两个主要问题。首先,以高角度观察物体,其次,背景噪音很多。我需要考虑的第一件事就是考虑这个观察角度,因为直接使用照片无法让我们确定一致的物体高度。如第二张图所示,倒置的“L”形的高度是一致的;但是由于一边比另一边长,它们在最靠近观察者的边缘似乎有不同的高度。
为了解决这个问题,我必须将图像中的工作区从之前的梯形转换为矩形。为此,我使用了此链接提供的代码,当给定一个图像和四个点时,它会在四个点之间裁剪图像并变换裁剪后的图像以补偿透视。此转换使用四个点创建一个矩形,而不是第三张图所示的梯形。
下一个需要解决的问题是外部光线和激光本身反射光线形式的背景噪声。为此,我使用 OpenCV 的 inRange() 函数过滤光线。我将阈值设置为仅拾取特定级别的红光。为了获得正确的值,我从一个宽松的阈值开始,并不断增加阈值级别,直到唯一拾取的光线是被扫描物体上的激光。 获得此图像后,我找到每行中最亮的像素,以获得每行一个像素的线,该线与激光线的最左侧接壤。然后将每个像素转换为 3D 空间中的顶点并存储在数组中,如网格创建部分所述。这些步骤的结果可以在第四张图中看到。
电机控制
在能够成功处理单幅图像以获取物体的切片后,我需要能够旋转物体以从不同角度拍摄新照片。为此,我控制了被扫描物体所在平台下方的步进电机。我通过创建一个变量来跟踪电机的状态,并通过切换四个电机输入中的每一个来微步进,从而为我们的步进功能奠定了基础。
网格创建 要从所有处理过的图像中创建网格,我首先必须将处理过的图像中的每个白色像素转换为 3D 空间中的顶点。因为我正在收集具有圆柱对称性的物体的各个切片,所以开始收集圆柱坐标是有意义的。这是有道理的,因为图片的高度可以表示 z 轴,与旋转台中心的距离可以表示 R 轴,步进电机的旋转可以表示 theta 轴。但是,由于我将数据存储在圆柱坐标中,因此我必须将每个顶点转换为笛卡尔坐标。
创建这些顶点后,它们会存储在一个列表中,该列表又存储在另一个列表中,该列表包含为每张捕获的图像创建的顶点列表。处理完所有图像并将其转换为顶点后,我必须选择最终网格中真正想要表示的顶点。我希望包括顶部顶点和底部顶点,然后根据分辨率,我为每个图像选择了等距的顶点数。由于并非所有顶点列表的长度都相同,因此我必须通过查找顶点数最少的列表并从所有其他列表中删除顶点,直到它们都相等,从而使它们均匀分布。 创建顶点列表后,我现在可以创建网格了。我选择使用 .obj 文件标准格式化我们的网格,因为它简单且可 3D 打印。
嵌入函数
在设备正常运行后,我通过添加完整的嵌入式功能对其进行了完善。这意味着移除键盘、鼠标和显示器,并在完成处理后通过无线方式向我们发送 .obj 文件。首先,我更改了 .bashrc 代码,使其在启动时自动登录并启动主 python 程序。这是通过使用 sudo raspi-config 并选择“Console Autologin”并将行“sudo python /home/pi/finalProject/FINAL.py”添加到 /home/pi/.bashrc 来完成的。 除此之外,我还添加了一个按钮和状态 LED 供用户输入和输出。按钮将让用户告诉设备何时开始扫描,LED 将告诉用户机器的状态。如果 LED 亮起,则表示设备已准备好开始新扫描。如果 LED 闪烁,则表示设备当前正在扫描。如果 LED 为办公室,则表示存在软件错误,需要重新启动系统。最后,我启用了设备通过电子邮件发送 .obj 文件的功能。这是通过使用 smtplib 和电子邮件库实现的。这种发送电子邮件的功能为我们提供了一种非常方便的无线方式,可以将生成的文件传递给用户,供用户在许多不同的平台上访问。
集成







在制造完设备的各个部件后,我将其组装在一起。上图按顺序显示:
(a)组装好的盒子外部
(b)组装好的盒子内部装有摄像头和激光器
(c)电子床内部视图
(d)Pi 的背面,可连接 Pi 端口和 5V 电机输入
(e)设备正面带有 LED 环和状态灯的按钮
结果






激光 3D 扫描仪能够以相当高的精度扫描物体。物体的特征清晰可辨,使用 Repetier 等切片软件可以轻松 3D 打印零件。上图显示了一块木头和一只小黄鸭的一些扫描样本。
我在测试过程中发现的最大的发现和成功之一是设备的一致性。在对同一物体进行多次试验的过程中,即使我们稍微改变了物体的位置,扫描仪每次都能生成非常相似的 .obj 文件。从三次单独的扫描中可以看出,它们看起来都非常相似,捕捉到的细节和细节数量都相同。总的来说,我对我们系统的一致性和稳健性印象深刻。
我真正能够调整的变量之一是扫描的分辨率。由于步进机中有 400 个步骤,我可以选择每个 ΔΘ 的大小来决定角度分辨率。默认情况下,我将角度分辨率设置为 20 次迭代,这意味着每帧,电机旋转 20 步(400/20 = 20)。选择这个主要是为了节省时间——以这种方式完成扫描大约需要 45 秒。但是,如果我想要更高质量的扫描,我可以将迭代次数一直增加到 400。这为构建模型提供了更多点,从而实现了更详细的扫描。除了角度分辨率,我还可以调整垂直分辨率,或者我选择沿激光切片轮询多少个不同的点。出于对时间的类似兴趣,我将此默认设置为 20,但我可以增加它以获得更好的结果。在使用这些角分辨率和空间分辨率参数时,我能够在最后一张图中汇编不同扫描的结果。每个标签的格式都是角分辨率 x 空间分辨率。如默认扫描设置所示,鸭子的特征是可识别的,但不详细。然而,随着我增加分辨率,开始显示单个精确的特征,包括鸭子的眼睛、喙、尾巴和翅膀。最高分辨率图像大约需要 5 分钟才能扫描完成。看到如此高的分辨率是一个非常大的成功。
限制
尽管该项目取得了成功,但设计和实施方面仍然存在一些限制。使用激光会带来很多与光线散射有关的问题。我尝试扫描的许多物体要么是半透明的,要么是闪亮的,要么是极暗的,但光线从表面反射的方式却很麻烦。如果物体是半透明的,光线会被吸收和散射,导致切片读取非常嘈杂。在闪亮和黑暗的物体中,光线要么被反射,要么被吸收,以至于很难拾取。此外,因为我使用相机来捕捉物体的特征,所以它的感应受到视线的限制,这意味着凹面物体和锐角经常被物体的其他部分阻挡。这在我们的橡皮鸭示例中得到了体现,因为尾巴有时会在扫描中失去曲率。相机也只能检测表面结构,这意味着无法捕捉到孔或内部几何形状。然而,这也是许多其他扫描解决方案都存在的一个常见问题。
下一步
虽然我对我们项目的结果很满意,但仍有一些可以改进的地方。首先,在当前状态下,只能通过更改代码中的硬编码分辨率变量来更改扫描分辨率。为了使项目更加嵌入式,可以包含一个分辨率电位器,这样用户就可以更改分辨率,而无需将显示器和键盘插入扫描仪。此外,扫描仪创建的图像有时看起来参差不齐。为了解决这个问题,可以实施网格平滑技术来平滑不规则和尖角。最后,我发现像素坐标不能很好地缩放到现实世界。我创建的网格比实际对象大 6 到 7 倍。将来,实施一种缩放网格的方法将大有裨益,这样它们就可以更准确地反映对象的实际大小。











