只需要将物体放入到一个盒子里,就可以得到它的 3D 模型数据,是不是很方便呢?
今天我们就用树莓派制作一台三维激光扫描建模仪。能够将物体数字化为 .obj 网格文件,然后再使用 3D 打印来「复制」它。
项目通过线激光发射器和树莓派摄像头来实现计算机视觉数据采集。激光定位的角度与激光成 45 度倾斜,同时在物体的垂直切片上投射出一条红线。摄像头检测切片到中心的距离并给出网格切片。物体在托盘上慢慢的旋转,通过不断的重复这个过程,扫描完成整个物体,十分的方便。
创作灵感
开始制作前,我做了一些基础研究。发现许多 3D 扫描仪都使用旋转平台,然后使用各种不同的传感器来测量距中心的距离,建立旋转模型。很多采用了与 Kinect 扫描仪类似的双摄像头。
我的项目是基于 Yscanner 扫描仪开发的,采用单摄像头。它是一种低分辨率的激光扫描仪,项目简单、可行,而且这种激光技术是激光相对于摄像机的偏移量来进行照射的,测量到中心的距离,如图你可以清晰的看见那条线。
motor-bracket.stl
[36884 Bytes at 2020-02-25, 80 次下载]
Scanner.py
[10710 Bytes at 2020-02-25, 99 次下载]
motor-couple.stl
[44784 Bytes at 2020-02-25, 67 次下载]
box-scanner.dxf
[50604 Bytes at 2020-02-25, 71 次下载]
laser-bracket.stl
[41584 Bytes at 2020-02-25, 64 次下载]
树莓派 × 1
树莓派摄像头V2 × 1
LED × 1
电阻 × 1
电线 × 若干
3D 打印灯丝 × 若干
木板 × 1
M3 硬件 × 若干
步进电机 × 1
线激光发射器 × 1
LN298 步进电机驱动器 × 1
金属按钮 × 1
烙铁 × 1
激光切割机 × 1
3D 打印机 × 1
螺丝刀 × 1
钳子 × 1
该项目核心组件就是能够垂直投射在物体上的线激光发射器。它通过树莓派摄像头获取投影,对其进行透视校正,然后在图像处理前进行过滤。在图像处理中,收集线的每个部分到物体中心的距离。在径向坐标中,此图片将同时产生 r 和 z 的分量,然后通过将物品的旋转得到新的切片来实现三维效果。
为了达到设计效果,我使用树莓派作为中央计算单元。
1、将树莓派与步进电机和电机驱动器连接。由外部 5V 电源供电并由树莓派的 GPIO 引脚控制。
2、将线激光发射器连接到树莓派的 3.3V 线上,将树莓派摄像头连接到摄像头的输入上。
3、安装一个简单的下拉按钮和一个用来显示状态的 LED 来向用户展示系统所处的状态。
需要注意的地方:
1、将电子设备安装到由 T 型槽和 M3 硬件搭建的激光切割盒中。
2、将电子设备隐藏在底部隔间中,盖子上放置旋转托盘,便于放置物品。这个盖子可以最大程度地减少遗漏到系统中的光源,而这些光在最终扫描中会产生噪声。
使用 Autodesk Fusion 360 设计出 3D 外壳模型。外壳设计很简单,一个盒子和带活页的盖子(都是 3D 打印而成)。设备主要分为两层:电子设备层和主体层,两层之间有可穿电线的孔。
使用 Epilog Zing 40 W 激光切割机切割。如图所示,外壳主要由主体层、电子设备层、两个盖子部件、前面板块、后面板块以及两个侧面板块组成。
在主体层上,有三个切口:一个用于安装步进电机,一个用于放置激光器的电线,另一个用于放置树莓派摄像头的软排线。
底座有用于固定树莓派、面包板和电机驱动器的安装孔,以及用于放入步进电机的较大切口。
外壳的盖子可以简单地卡在一起,形成一个类似三角形状的盖子,合页宽度为侧板孔的直径长。
背面板块和其中一个侧面板块在侧面都有插槽,因此可以轻松访问树莓派的端口(HDMI、USB、以太网以及电源)。前面板块是一个简单的部分,手动钻孔用以安装按钮和 LED。
所有的零件由 M3 硬件、 T 型接头和插槽固定在一起。组拼时可以使用 M3 螺钉将零件固定在一起。
我使用激光切割机来处理大部分组件是因为它具有速度和便捷性的优势。虽然 3D 的几何形状在裁纸器上却很难创建。
第一个组件是线激光发射器支架。该组件将被安装在与摄像头视线成 45 度角的主体层上,有一个孔,可将激光器固定到位。
另外还需一个马达支架,因为马达的轴太长了。支架产生的摩擦力不妨碍激光切割的组件,并降低了电机连接的平面,从而使旋转平台与主体层齐平。
项目所需的文件请在项目文件库中下载:
https://make.quwj.com/project/204
该项目的硬件接线部分非常简单,只需要将电机、按钮、LED、激光以及摄像头连接到树莓派。
1、将电阻与每个引脚串联,以保护引脚。GPIO 的一个引脚专用于控制 LED,当设备准备好后,LED 会亮起,当设备运行时,它会使用 PWM 进行脉冲控制。
2、将 GPIO 的另一个引脚连接到上拉按钮上,未按下按钮时为高电平,按下按钮时为低电平。
3、GPIO 的四个引脚用来驱动步进电机。
步进电机
由于项目中的电动机只需要一定程度上的步进而无需控制速度,因此选用了一款很简单的步进电动机驱动器(L298N)。该驱动器只需将控制线升压以馈入电动机的输入即可。
为了了解步进电机如何在非常低的水平上操作,我参考了 L298N 数据表和 Arduino 库。步进电动机的磁芯带有可交替极性的中心轴。使用四根线缠绕去控制两个电磁体,这两个电磁体分别为电动机中的每个相对的轴供电。通过切换轴的极性,就可以推动步进器。
了解完步进电机的工作原理,就能够更容易地控制步进电机。由于步进电机的最大电流约为 0.8A,超出了树莓派的供电能力,我最后选用 5V 电源而不是树莓派为其供电。
该项目的软件部分由四个方面组成:图像处理,电机控制,网格创建和嵌入式功能。
1、如图所示,随着系统启动 .bashrc 自动登录到树莓派并开始运行 python 代码。系统点亮状态灯,告知用户它已正确启动,并等待按下按钮。
2、用户可以放置要扫描的物品并合上盖子。按下按钮后,LED 会闪烁,告知用户设备正在工作。设备将在图像处理和电机控制之间循环,直到旋转完成收集物品的所有数据为止。最后,创建网格并通过电子邮件将文件发送到预选的电子邮箱。
3、设备可以重新启动循环,只需按一下按钮就可以执行另一次扫描。
图像处理
第一步是处理获取的图像,以便将存储在图像中的信息提取为可用于创建空间点阵列的形式。
首先拍摄了平台上物体的照片,以及由激光照射到盒子背面并分散后产生的所有背景噪声。该图片的原始形式存在两个主要问题。一是拍摄物体的角度较高,二是背景噪音很大。视角的问题是第一个要考虑的,因为按原样使用照片将无法确定的物体一致高度。
如图所示,倒置的 “L” 形的高度是一致的。但是,由于一侧比另一侧长,它们在最靠近观察者的边缘处的高度似乎不同。
为了解决此问题,我必须将图像中的工作空间从以前的梯形形状转换为矩形。为此,我使用了链接中提供的代码。https://www.pyimagesearch.com/2014/08/25/4-point-opencv-getperspective-transform-example/
当给定一个图像和四个点时,该代码将在四个点之间裁剪图像并补偿转换裁剪后的图像。使用四个点来创建矩形,而不是之前的梯形形状。
下一个需要解决的问题是外部光形成的背景噪声和激光本身反射的光。我使用 OpenCV 的 inRange() 函数过滤光。我将阈值设置为仅在特定水平上拾取红光。为了获得正确的值,从较大的阈值开始,不断提高阈值水平,直到唯一被拾取的光是来自扫描物体上的激光。
获得此图像后,我发现每行中最亮的像素是每行的一个像素线,该线与激光线的最左侧相接。最后,将每个像素转换为 3D 空间中的顶点并存储在数组中,如网格创建部分所述。
电机控制
在成功处理单个图像以获取物体的切片之后,我还需要旋转物体获得不同角度的新照片。为此要控制好扫描物体平台下的步进电机。我通过创建一个变量来跟踪电机的状态和细分控制四个电机的输入,这些是实现步进机功能的基础。
网格创建
要从所有处理的图像创建网格,我必须首先将处理的图像中的每个白色像素转换为 3D 空间中的顶点。我要收集具有圆柱对称性的单个对象切片,所以要收集圆柱的坐标。图片的高度可以用 z 轴表示,到旋转台中心的距离可以用 R 轴表示,步进电机的旋转可以用 theta 轴表示。由于我将数据存储在圆柱坐标中,因此必须将每个顶点转换为笛卡尔坐标。
顶点创建好后,将它们存储在一个列表中。然后再将所述列表存储在另一个列表中,该列表包含为获取的每个图像创建的顶点列表。处理完所有图像并将其转换为顶点后,在最终网格中显示顶点。其中,最好包含顶部顶点和底部顶点,然后根据分辨率,我选择了均匀分布的顶点数以用于每个图像。因为并非所有顶点列表的长度都相同,所以我不得不通过找到顶点数量最少的列表并将所有其他列表中的顶点移除,直到它们均等为止。
创建顶点列表后就可以创建网格。我使用 .obj 文件标准设置网格格式,因为它很简单并且支持 3D 打印。
嵌入式功能
当设备正常运行后,我通过添加完整的嵌入式功能对其进行了完善。这意味着移除键盘,鼠标和显示器,处理完成后以无线方式将 .obj 文件发送过去。
1、将 .bashrc 代码更改为自动登录并在启动时启动 python 主程序。执行 sudo raspi-config 命令,设置 “Console Autologin”,并将 “sudo python /home/pi/finalProject/FINAL.py” 行添加到 /home/pi/.bashrc。
2、添加一个按钮和 LED 状态显示,用于用户输入和输出。按钮将会告知用户设备何时开始扫描,LED 将告诉用户机器的状态。
如果 LED 点亮,则表明设备已准备好开始扫描。如果 LED 闪烁,则表明设备当前正在扫描。如果 LED 报错,则表明软件错误,要求重启系统。
最后,我通过电子邮件向设备发送 .obj 文件。使用 smtplib 和 email 库来完成。这种无线的发送方式很方便,可以将生成的文件传递给用户也可以在不同的平台上访问。
完成以上步骤后,现在可以将组件组合在一起。
1、组装外壳箱体。
2、将照相机和激光安装到盒子里。
3、安装其他电子设备。
4、树莓派的背面,可使用树莓派端口和 5V 电动机输入。
5、安装好设备正面以及带有 LED 状态指示灯的按钮。
激光 3D 扫描仪能够精确地扫描物体。物体的特征是独特且可识别的,并且使用切片软件(例如Repetier)可以很容易地对零件进行 3D 打印。
通过测试最大的发现是设备的一致性。在对同一物品进行多次试验中,即使稍微改物品的位置,扫描程序也都能生成非常相似的 .obj 文件。
如图所示,三次扫描的结果都非常的相似,能够获取相同的细节。系统的一致性还是不错的。
可调整的变量之一是扫描的分辨率。因为步进器有 400 步,所以我可以选择每个 ΔΘ 来决定角度分辨率。在默认情况下,我将角度分辨率设置为 20 次迭代,这意味着电机每帧旋转 20 步(400/20=20)。选择此项主要是为了节省时间,以这种方式完成扫描大约需要 45 秒。但是如果想要更高质量的扫描,就需要将迭代次数增加到 400。这为 3D 构造模型提供了更多的点,从而可以进行更详细的扫描。
除了角分辨率外,还可以调整垂直分辨率,或选择沿激光切片扫描多少个不同的点。为了节省时间,我将默认值设置为 20,如果你想得到更好的效果可以增加数值。
如图所示,通过改变角分辨率和空间分辨率的参数,可以显示出不同的扫描结果。每个标签的格式设置为角度分辨率x空间分辨率。从默认的扫描设置中可以看出鸭子的功能是可识别的,但并不明显。
但是,随着分辨率的提高,精确的功能开始显现出来,包括眼睛、嘴巴、尾巴和鸭子的翅膀。最高分辨率的图像大约需要 5 分钟扫描。高分辨率的效果是非常成功的。每个标签的格式设置为角度分辨率x空间分辨率。从默认扫描设置中可以看出,鸭子的细节可识别出来,但并不明显。随着分辨率的提高,细节的准确性越来越高,包括眼睛、嘴巴、尾巴和鸭子的翅膀。
尽管该项目取得了成功,但在设计和实施方面仍然存在一些局限性。激光发射器的光散射问题,我扫描的物体都是半透明,很亮或者很暗的物体在表面的反射就存在缺陷。如果物体是半透明的,光线就会被吸收并散射,从而使切片的读数非常嘈杂。在发亮和黑暗的物体中,光线将会被反射或吸收,以至于难以拾取的程度。
此外,我使用摄像头获取物体的特征,就能感受到视线遮挡的限制,凹陷的物体和锐角通常会被物体的其他部分阻挡。小黄鸭的尾巴在扫描中就失去曲线率。摄像头只能检测物体表面结构,无法获取孔或者内部为几何形状的物体。这一常见问题也在其他扫描机中存在。
虽然项目整体效果还不错,但还有可以优化的地方:
1、在当前状态下,只能通过更改代码中的硬编码分辨率变量来更改扫描分辨率。为了使项目更具嵌入式性,可以添加一个分辨率电位计,以便用户可以更改分辨率,而不用将监视器和键盘插入扫描仪。
2、扫描仪在创建时的图像效果不是很好。为了解决这个问题,可以使用网格平滑技术来平滑不规则和粗糙的位置。
3、像素坐标无法很好地缩放到现实世界中。我创建的网格比实际物品大六到七倍。