1. 项目概述:打造一个会旋转的桌面全息动画
如果你对那种在科幻电影里漂浮在空中的三维影像着迷,但又觉得它离现实生活很远,那么这个项目可能就是为你准备的。我们这次要做的,不是一个需要昂贵激光器和复杂光学平台的“真”全息,而是一种非常巧妙且成本可控的“伪全息”显示方案。它的核心原理,是利用人眼的视觉暂留和特定角度的镜面反射,将一个平面屏幕上播放的、经过特殊处理的二维动画,转化成一个仿佛悬浮在空中的立体影像。
整个系统的核心是一个由开源硬件驱动的“旋转投影仪”。我们使用Adafruit的PyPortal作为大脑和显示屏,它是一个集成了Wi-Fi、触摸屏和强大微控制器的物联网开发板。通过CircuitPython,我们可以轻松地让它循环播放我们准备好的GIF动画。然后,我们用一个3D打印的外壳,将PyPortal和一个连续旋转舵机固定在一起。当舵机带动整个屏幕匀速旋转时,屏幕上的动画也随之转动。最后,在屏幕上方放置一个透明的四棱锥(全息棱镜),旋转的屏幕光线经过棱镜各面的反射,就会在你的眼中合成一个看起来是360度可见的、悬浮的动画角色。
这个项目完美融合了嵌入式编程、3D建模打印和基础电子制作,最终成果不仅是一个酷炫的科技展示品,更是一个深入理解光、影、运动与视觉感知的绝佳实践。无论你是想为桌面增添一个独特的装饰,还是作为一个教学演示项目,它都能带来十足的成就感。
2. 核心硬件选型与电路设计解析
2.1 主控与显示单元:为什么是PyPortal?
在众多微控制器开发板中,选择Adafruit PyPortal是经过深思熟虑的。这个项目的核心需求是:一块能独立、流畅播放动画的屏幕,一个足够处理图形和舵机控制的处理器,以及便于快速开发的软件环境。
PyPortal基于ATSAMD51系列微控制器,主频高达120MHz,并配备了3.5英寸的320x240分辨率触摸屏。这个分辨率对于全息投影来说恰到好处:足够清晰以呈现细节,又不会给处理器和存储带来过大负担。更重要的是,Adafruit为其深度优化了CircuitPython支持,内置了显示驱动、网络等核心库,让我们可以像在电脑上写Python脚本一样操作硬件,极大地降低了开发门槛。你不需要从零开始编写底层的屏幕刷新代码,只需几行命令就能加载和播放GIF,这是选择它的决定性因素。
注意:市面上也有一些其他带屏的开发板,如ESP32搭配TFT屏的方案。它们可能更便宜,但通常需要自行焊接、连接排线,并处理更底层的驱动问题。PyPortal的“开箱即用”特性,对于希望快速聚焦于创意和效果实现,而非底层调试的制作者来说,价值巨大。
2.2 动力与旋转系统:连续旋转舵机的奥秘
普通的舵机(伺服电机)接收控制信号,会转动到指定的角度并保持住。而连续旋转舵机则完全不同,它接收的信号控制的是旋转的速度和方向,而非位置。这正是一个匀速旋转平台所需要的。
其内部原理是去掉了普通舵机的电位器(用于反馈当前位置),因此控制电路无法感知电机轴的实际位置,只会根据脉冲宽度调制(PWM)信号的占空比来持续驱动电机正转或反转。占空比的大小直接对应了转速。在本项目中,我们将通过PyPortal的PWM输出引脚,发送一个固定的占空比信号,让舵机以一个恒定的、舒缓的速度旋转,为全息影像提供稳定的运动基础。
2.3 供电方案:PowerBoost 1000C的考量
PyPortal和舵机都需要5V电压工作,而我们希望系统是无线、便携的。因此,一个集成了升压和充电管理功能的电路模块必不可少。PowerBoost 1000C正是为此而生。
它直接连接一块3.7V的锂聚合物电池,并将其升压至稳定的5V输出。其“负载共享”功能是关键:当插着USB线充电时,模块会优先使用USB电源为负载供电,并同时为电池充电;拔掉USB后,则无缝切换至电池供电,不会造成系统重启。这意味着我们的全息显示仪可以7x24小时不间断工作,没电时插上充电宝或手机充电器即可“续命”,用户体验非常流畅。
2.4 电路连接详解:从原理图到实际焊接
项目的电路连接其实非常简洁,遵循了“电源路径清晰,信号独立”的原则。下面这张表格清晰地梳理了所有连接关系:
| 连接起点 | 连接终点 | 线缆/接口 | 功能说明 |
|---|---|---|---|
| 锂聚合物电池 | PowerBoost 1000C | JST-PH 2mm接口 | 提供原始电源。注意正负极不要接反。 |
| PowerBoost 输出端 (5V & GND) | PyPortal 的D4端口 | 杜邦线或焊接导线 | 为PyPortal主板供电。务必确认电压是5V。 |
| PowerBoost 的EN和GND焊盘 | 滑动开关的两端 | 焊接导线 | 开关串联在使能端,用于物理切断PyPortal供电,实现硬关机。 |
| PyPortal 的D3端口 | 连续旋转舵机 | 3芯JST-PH转杜邦线 | D3端口提供PWM信号(白线)、5V电源(红线)和地线(黑线)给舵机。 |
| 舵机信号线(通常为橙色/黄色) | PyPortal D3 的信号引脚(白线) | 杜邦线 | 传输PWM控制信号。 |
| 舵机电源线(红色) | PyPortal D3 的5V引脚(红线) | 杜邦线 | 提供5V工作电压。 |
| 舵机地线(棕色/黑色) | PyPortal D3 的GND引脚(黑线) | 杜邦线 | 形成完整回路。 |
焊接与组装心得:
- 先规划,后焊接:建议先将所有组件在3D打印外壳内比划一下,确定走线路径,尤其是电池和PowerBoost模块的位置,要确保外壳能顺利盖上。
- 开关处理:教程中提到将滑动开关的第三个引脚剪掉,这是为了让它能平整地贴在PowerBoost板上。如果你不想破坏开关,也可以将其弯折或使用一小段热熔胶固定空置的引脚,避免短路。
- 线缆管理:使用扎带或一点点蓝丁胶将多余的线缆固定在壳体内侧,避免其缠绕到旋转的舵机轴上,这是保证长期稳定运行的关键。
3. 动画素材的制备:从普通GIF到全息专用
这是整个项目中最具创意也最需要耐心的一环。全息效果的好坏,80%取决于素材准备是否得当。
3.1 素材选择的核心原则
并非所有动画都适合做全息投影。你需要寻找或制作符合以下特征的素材:
- 主体明确,背景干净:动画的主体应该是一个独立的物体、角色或标志,最好能有清晰的轮廓。复杂的、充满细节的背景(如风景、房间)在反射后会产生混乱的重影,破坏立体感。
- 黑色背景是最佳拍档:将主体放在纯黑色(RGB: 0,0,0)背景上。在全息棱镜反射时,黑色部分会“消失”,只留下发光的主体,从而产生悬浮的幻觉。如果原素材背景杂乱,可以使用像Photoshop、GIMP或在线工具Remove.bg进行抠图。
- 多角度视图:理想情况是能找到同一个动画的四个不同角度(例如:前、后、左、右视图)。这样当屏幕旋转时,每个棱镜面反射出对应的角度,立体感最强。如果找不到,也可以用同一个动画,但你需要接受最终效果是四个面都一样的“旋转平面图”,而非真正的3D模型。
3.2 使用Google Slides进行精准排版
为什么用演示文稿软件?因为它能提供精准的对齐和参考线,这对于将四个GIF排列到正确位置至关重要。
- 创建画布:新建一个Google Slides演示文稿,删除所有默认文本框。将幻灯片背景设置为纯黑色。这将是我们的“数字工作台”。
- 导入与排列:将准备好的四个GIF文件拖入幻灯片。每个GIF代表最终全息影像的一个面。
- 关键布局:将四个GIF分别放置在一个假想正方形的四个角上。具体来说,让它们的中心点分别对准正方形的四个顶点,并且确保每个GIF的朝向都是“向外”的,即朝向正方形的中心。你可以利用Slides的“对齐”和“分布”工具,确保它们既“相对”又“等距”。
- 录制屏幕:播放这四张GIF,确保它们都在同步循环。然后使用电脑的屏幕录制功能(macOS的QuickTime Player,Windows的Xbox Game Bar或OBS Studio),录制大约2-3个动画循环的片段。录制区域要严格框选住这四个GIF,边缘不要留多余的黑边或桌面。
3.3 最终格式转换与优化
录屏得到的是一个视频文件(如MP4),我们需要将其转换回GIF,并适配PyPortal的屏幕。
- 使用EZGIF.com:这是一个免费且强大的在线GIF工具。选择“Video to GIF”功能,上传你的录屏视频。
- 关键参数设置:
- 尺寸调整(Resize):这是最重要的一步。必须将GIF的尺寸设置为宽度240像素,高度320像素。这正好是PyPortal屏幕的原始分辨率(尽管屏幕是320x240,但因为我们旋转了90度使用,所以这里宽高互换)。直接输入240x320,并确保锁定宽高比,避免变形。
- 帧率与裁剪:如果生成的GIF文件过大,可以适当降低帧率(如从30fps降到15fps)或裁剪掉不必要的帧(只保留一个完整循环)。PyPortal播放高分辨率或高帧率GIF时可能会卡顿。
- 导出与测试:将处理好的GIF保存到电脑,可以先在普通电脑上播放一下,检查四个动画是否清晰、对齐,背景是否为纯黑。
4. 软件环境配置与固件烧录
4.1 为PyPortal刷入CircuitPython固件
PyPortal出厂可能运行其他固件,我们首先需要将其变为一个CircuitPython设备。
- 进入引导加载程序模式:使用一条数据线(不仅仅是充电线)将PyPortal连接到电脑。快速双击板子上的复位(Reset)按钮。此时板载的NeoPixel RGB灯会变为绿色,电脑上会出现一个名为
PORTALBOOT的U盘。 - 下载与烧录固件:
- 访问CircuitPython官网,找到PyPortal的页面,下载最新的
.uf2格式固件文件。 - 将下载好的
.uf2文件直接拖入或复制到PORTALBOOT盘符的根目录。PyPortal会自动重启。
- 访问CircuitPython官网,找到PyPortal的页面,下载最新的
- 确认成功:重启后,电脑上会出现一个新的U盘,名为
CIRCUITPY。这就意味着你的PyPortal现在已经是一个CircuitPython“解释器”了,你可以直接向这个盘里拖放Python代码文件来运行。
4.2 部署全息动画播放代码
Adafruit为此项目提供了一个非常方便的一键式代码包。
- 准备GIF文件夹:在
CIRCUITPY盘中,新建一个名为gifs的文件夹。将你在上一步制作好的最终GIF文件拖入这个文件夹。你可以放多个GIF,代码会按顺序播放。 - 加载专用代码:
- 下载项目专用的
GIF_SERVO.UF2文件。 - 再次双击复位按钮,让PyPortal进入
PORTALBOOT模式。 - 将下载的
GIF_SERVO.UF2文件复制到PORTALBOOT盘,并重命名为code.py。这是CircuitPython的约定:主板重启后会自动执行根目录下的code.py文件。 - 复制完成后,PyPortal会自动重启并运行新代码。
- 下载项目专用的
4.3 代码功能浅析与配置
虽然我们直接使用了现成的.uf2文件,但了解其背后的大致逻辑有助于调试和自定义:
# 伪代码逻辑示意 import board, displayio, pulseio import adafruit_imageload, adafruit_motor.servo # 1. 初始化屏幕,旋转90度以适应竖屏播放 display = board.DISPLAY display.rotation = 90 # 2. 初始化连续旋转舵机,设置速度 pwm = pulseio.PWMOut(board.D3, frequency=50) servo = adafruit_motor.servo.ContinuousServo(pwm) servo.throttle = 0.1 # 设置一个慢速正转,值通常在-1.0到1.0之间 # 3. 加载gifs文件夹下的所有GIF文件 gif_files = list_files_in_folder("gifs") # 4. 主循环:依次播放每个GIF while True: for gif_path in gif_files: # 将GIF解码为TileGrid并显示在屏幕上 bitmap, palette = adafruit_imageload.load(gif_path) tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette) group = displayio.Group() group.append(tile_grid) display.show(group) # 计算GIF播放时长并等待 duration = get_gif_duration(gif_path) time.sleep(duration)配置选项:项目中提到的GIF Playback Configuration文件是一个可选的config.json。如果你需要更精细的控制,比如指定每个GIF的播放顺序、时长、或者舵机的精确转速,可以创建并编辑这个配置文件。对于初次尝试,直接跳过配置,使用默认值是最简单的。
5. 3D打印外壳的制作与优化
5.1 模型设计与切片要点
项目提供的STL文件已经将结构设计得非常周全,包含了PyPortal的支架、PowerBoost的安装位、电池仓、舵机座以及一个防止侧面漏光的前盖。
- 打印方向:作者已优化了打印方向,通常建议按照STL文件默认的朝向进行打印,这样可以最大限度地减少支撑材料的使用,并保证关键受力面(如螺丝柱)的强度。
- 层高与壁厚:使用0.2mm层高和0.4mm喷嘴是精度和速度的良好平衡。确保设置至少2层外壁(Perimeter)和3层顶底壳(Top/Bottom Layers),以保证外壳的坚固性和密闭性,防止漏光。
- 填充密度:20%的填充率对于这个尺寸的装饰性部件来说足够了。可以使用网格或蜂窝填充模式来平衡强度和耗材用量。
- 关键设置:裙边(Brim):由于模型中有一些细长的立柱(如固定舵机的柱子),为了防止打印时翘曲或脱落,务必启用裙边(Brim),建议设置5-10圈。这会在模型底部外围打印一圈薄层,极大地增加附着力,打印完成后再轻轻剥离即可。
5.2 打印后处理与组装技巧
- 清理支撑:仔细清除所有支撑材料,特别是PyPortal屏幕开口内部、螺丝孔内部的残留,确保没有塑料毛刺阻碍安装。
- 试装配:在真正拧螺丝之前,将所有电子部件(PyPortal, PowerBoost, 电池,舵机)先放入壳体内比划一下,确认走线空间足够,所有接口都能顺利对接。
- 螺丝的选择:使用M2.5规格的螺丝。固定PyPortal和PowerBoost时,5mm长度的螺丝通常正好。在拧入尼龙材质的打印件时,动作要轻柔,感觉有较大阻力时即可停止,避免滑丝。可以先用手动螺丝刀预拧,再用电动工具微调。
- 舵机安装:将舵机推入底座卡槽时,听到“咔哒”一声表示到位。舵机自带的圆形舵盘需要用力按压到舵机输出轴上。然后将这个舵盘再按压到3D打印的大圆形底座中。这个连接是摩擦配合,如果觉得太松,可以在舵盘边缘涂抹一点点瞬间胶(401胶水)加固,但要注意不要流到舵机轴承里。
6. 系统总装、调试与效果优化
6.1 分步组装流程
遵循“从内到外,从下到上”的逻辑进行组装:
- 供电核心:先将PowerBoost模块用螺丝固定在底壳指定位置。焊接好开关和输出线缆。
- 主板抬升:将四颗尼龙立柱安装在PyPortal背面的四个螺丝孔上。这样做的目的是为了在PyPortal和底板之间创造空间,以容纳PowerBoost和电池的厚度。
- 连接与放置:将电池放入电池仓,连接其JST插头到PowerBoost。将PowerBoost的输出线(红黑线)连接到PyPortal的D4端口。
- 安装主板:将已安装立柱的PyPortal对准底壳上的四个孔位,用螺丝从底部固定。
- 舵机系统:将舵机的三芯线从底壳侧面的线孔穿入,连接到PyPortal的D3端口(注意颜色对应:红-红,黑-黑,信号线-白)。然后将舵机本体卡入底座。
- 最终整合:将舵机圆形底座按压到旋转轴上。盖上防止侧光的前盖。最后,将全息棱镜小心地放置在屏幕中央,确保其四个表面对准屏幕上四个动画的区域。
6.2 调试与效果优化实战
组装完成后,打开电源开关,你应该能看到屏幕亮起并开始播放动画,同时舵机开始缓慢旋转。
- 问题1:动画播放卡顿或不流畅
- 排查:首先检查GIF文件尺寸是否为240x320,且文件大小是否过大(建议单个GIF在几百KB以内)。过大的文件会超出PyPortal的内存或处理能力。
- 解决:回到“EZGIF.com”进一步优化GIF,减少颜色数(64色或更少),降低帧率(10-15fps),或缩短动画时长。
- 问题2:全息影像模糊或有重影
- 排查:这通常是因为环境光太强,或者屏幕亮度不足,导致反射光与背景对比度不够。
- 解决:在暗室或光线较暗的环境中观看效果最佳。此外,可以在PyPortal的代码中尝试提高屏幕亮度(如果代码支持配置)。确保棱镜表面干净无指纹。
- 问题3:舵机不转或转速不合适
- 排查:检查D3端口的接线是否牢固。舵机可能有一个可调电位器(用小螺丝刀调节)。
- 解决:用小螺丝刀轻轻旋转舵机上的电位器,可以微调其零点和转速。顺时针或逆时针缓慢调节,直到获得一个稳定、速度合适的旋转。注意:不同品牌舵机,电位器调节方向可能不同,请慢慢尝试。
- 问题4:影像无法在棱镜四面正确拼接
- 排查:根本原因是屏幕上的四个GIF位置没有与棱镜的四个表面对齐。
- 解决:关闭旋转,静态观察。调整棱镜在屏幕上的位置,直到你从某一个角度看去,反射的动画正好位于棱镜面的中央。然后开启旋转,观察效果。这可能需要对GIF的原始排版进行微调并重新制作。
6.3 进阶玩法与扩展思路
当基础项目成功运行后,你可以尝试更多创意:
- 多动画循环:在
gifs文件夹中放入多个不同主题的GIF,代码会自动轮播,打造一个动态变化的展示柜。 - 交互触发:PyPortal带有触摸屏!你可以修改代码,实现触摸切换动画、调节旋转速度或改变亮度。
- 网络更新:利用PyPortal的Wi-Fi功能,编写代码让它定时从互联网(如指定的云存储、RSS源)下载新的GIF,让你的全息显示内容永不重复。
- 自定义模型:使用Fusion 360或Tinkercad修改3D外壳,比如设计一个更科幻的底座,或者将整个装置嵌入到一个更大的场景模型中。
这个项目从电路焊接、代码上传到机械组装,完成了一次完整的“造物”体验。当你第一次看到自己喜爱的动画角色在棱锥中悠然旋转、仿佛触手可及时,那种跨越虚拟与现实的震撼,正是创客精神最好的回报。