缘起

"用冷却法测金属的比热容"是大学物理实验中的经典项目,我觉得它适合做成一个3D场景,把它搬进浏览器——打开网页就能操作,不用安装软件,不用担心烫伤。仿真实验在线地址:https://aphysics.cn/new3D/bireshang.html

最终呈现:一个浏览器里的完整实验台

打开页面,你会看到一个用 three.js 搭建的三维实验台。左边是加热仪器——底座、立柱、齿轮齿条、手轮、可升降的加热炉体、透明风罩、隔热盖,一样不少。右边是测试仪器——银色箱体上嵌着两块 LED 数字屏(一块显示热电势、一块显示计时),还有加热指示灯、加热电流输出口、热电偶输入端子。两台仪器之间,热电偶引线和加热导线按照实际走线方式连接:黑红相间的热电偶线从探针底部接到插孔,再经保温杯(冰水混合物,冷端参考)分路到红黑端子;褐色加热线从输出口绕到仪器面板前方,再折回到加热器顶部。

实验流程被拆成了 8 个步骤,顶部有一排步骤指示点实时高亮。操作者首先从"铜 Cu""铁 Fe""铝 Al"三个按钮中选择样品(页面上有一行小字说明:实际实验中三种样品表面都是银白色的,这里用不同颜色是为了方便识别),然后依次执行:下降加热器、设置目标温度并加热、等待自动恒温后加热器升起、盖上隔热盖开始冷却测量。

冷却阶段是整个实验的核心。系统基于牛顿冷却定律做数值模拟,热电偶信号通过一张 Cu-Constantan 分度表实时换算——当热电势从 4.20 mV 降到 4.00 mV,系统自动完成计时,记录 Δt。每种金属重复测量 6 次,三种金属共 18 次测量全部完成后,系统以铜的标准比热容为基准,用比较公式算出铁和铝的比热容,弹出结果面板。

整个过程中,右侧状态面板实时显示当前样品、质量、热电势、温度、计时和加热状态;右下方数据表格逐行填入每次测量的原始数据,并自动汇总各金属的平均 Δt。三维场景支持鼠标拖拽旋转和滚轮缩放,方便从不同角度观察。

需要说明的是,这个虚拟实验并不追求与真实实验完全一致。在一些细节上我做了取舍——比如加热过程简化为线性升温、散热系数做了理想化处理、部分操作步骤的顺序和真实实验有差异——这样做的目的是减少开发工作量,把精力集中在核心物理过程的模拟上。操作流程也不完全照搬真实实验,毕竟虚拟环境有自己的交互特点,有些步骤用鼠标点击比手动操作更自然。但实验的目的完全一致:通过比较三种金属在相同条件下的冷却时间,用已知比热容的铜作为基准,推导出铁和铝的比热容。

开发手记:在反复打磨中逼近真实

从零到一:先让东西"能看"

最初版本只有几个基本几何体——方盒子当底座,圆柱体当炉子,勉强能看出是台仪器。然后一步步细化:给立柱加上齿轮齿条(用 InstancedMesh 做了 25 个小方块),给加热器装上可旋转的手轮(圆环加辐条),给测试仪器面板贴上 Canvas 纹理的 LED 数字显示。场景从"能看"慢慢变成"像那么回事"。

热电偶的布线是早期花心思比较多的地方。真实的实验台上,线缆在仪器之间蜿蜒穿梭,如果直接用直线连接就会穿模——线从箱体中间穿过去,视觉上很假。我改用 CatmullRomCurve3 来定义空间曲线,让线缆沿着合理的路径绕行,经过保温杯、贴着面板前方走,最终到达端子。每根线用 TubeGeometry 生成管状网格,视觉上就有了线缆的质感。

物理模拟:让数据"说得通"

三维场景搭好之后,真正的挑战是物理模拟。这个实验的灵魂在于冷却过程的数据必须自洽——三种金属的冷却时间之比要能反推出正确的比热容关系。

根据牛顿冷却定律,样品的温度变化率 dT/dt = -k(T - T₀)/(M·c),其中 k 是散热系数,M 是质量,c 是比热容。当三种样品几何形状相同、表面状态相同(实验中都是银白色),k 可以视为相同,于是冷却时间 Δt 正比于 M·c 的乘积。代入比较公式 C₂ = C₁ × (M₁/M₂) × (Δt₂/Δt₁),就能从铜的已知比热容推出铁和铝的值。

调参的过程颇为曲折。最初我凭直觉设了一组冷却常数,结果铜的 Δt 只有几秒,铁铝的偏差更大,算出来的比热容跟理论值相去甚远。后来我反过来从目标 Δt 出发,先确定铜的冷却时间约 15+ 秒,再根据 M·c 的比值推算铁和铝的冷却时间,由此反推出每种金属的 coolK 参数。最终代入公式后铁算出 0.1121(理论值 0.112)、铝算出 0.2171(理论值 0.217),完美吻合。

热电偶的分度表也是一个细节。我用了 Cu-Constantan 的标准数据,从 -10°C 到 200°C 逐 10°C 一个节点,线性插值实现双向换算。4.20 mV 对应约 98.3°C,4.00 mV 对应约 94.0°C,5°C 的冷却区间在时间尺度上足够拉开差异,又不会让等待太久。

反复打磨:细节里藏着魔鬼

开发过程中最耗时间的不是大功能的实现,而是一个个细节的修正。

加热线的连接就是一例。褐色加热线从测试仪器的输出口连到加热器顶部,最初端点位置不对,跟输出口之间差了约 0.2 的距离,悬在空中。我用 getWorldPosition 加 worldToLocal 动态计算端口在局部坐标系中的精确位置,再把曲线的第一个控制点显式设为该位置,才彻底解决了"对不上"的问题。后来用户又反馈线缆从仪器面板中间穿了过去,于是加了 z=1.2 的前绕路径,让线缆先走到面板前方再折回来。高处弧顶最初比加热器顶部高出太多,调了几轮才降到合适的位置。这根线前后改了四五轮,每次都是肉眼可见的小问题,但修起来需要精确定位坐标系和曲线参数。

手轮的旋转也是反复调过的。手轮通过一根轴连到立柱上,加热器升降时手轮跟着上下移动,同时绕轴旋转——模拟真实齿轮传动的效果。旋转角度用 lerp 平滑插值逼近目标值,视觉上就有了"转着圈升上去"的感觉。用户要求手轮整体向 z 负方向移动 0.2,虽然只是一行代码的改动,但说明他在认真观察三维场景里每个部件的相对位置。

元素符号的显示也踩过坑。最初用 toUpperCase() 来显示金属符号,结果铜显示成"CU"、铁显示成"FE"、铝显示成"AL"——化学上正确的写法应该是 Cu、Fe、Al,第二个字母小写。后来在 METALS 对象里加了 sym 属性,所有显示位置统一改用 sym,才修正了这个看起来不起眼但化学老师一定会指出的问题。

还有一个逻辑上的坑:切换金属样品时,最初代码会清空测量数据数组,导致前一种金属的 6 次测量数据全部丢失。用户希望三种金属的数据同时保留在表格里,最后一起参与计算。去掉那一行清空语句后,数据表格就能完整展示 18 次测量的全部记录了。

加热状态的显示也有类似问题。状态面板里的"加热状态"始终显示"关闭",但仪器面板上的 LED 指示灯是对的。原因是 LED 状态在物理更新函数里实时刷新,而状态面板的文字只在步骤切换时才更新。把加热状态的刷新也放进物理更新的定时显示逻辑里,问题就解决了。

一些让自己满意的小设计

隔热盖的联锁逻辑是我比较满意的一个设计。真实实验中,隔热盖放在风罩顶部之前,加热器不能下降(否则会撞到盖子);加热器升起之后才能放盖子。在仿真里,隔热盖初始状态在台面左侧,只有当加热器处于升起位置时才能被放到风罩口上;而加热器只有在隔热盖回到台面时才能下降。这种操作顺序的约束避免了物理上不可能的状态,也让操作流程更接近真实实验的手感。

数据表格的设计也花了心思。最终版本是一个统一的表格,三种金属的测量数据按时间顺序排列在一起,下方自动汇总每种金属的平均 Δt。这样用户可以直观地对比三种金属的冷却时间差异,而不是在三个分开的表格之间来回切换。

最后一步的计算结果面板会展示完整的公式推导过程:以铜为标准,列出每种金属的平均 Δt 和计算得到的比热容,让用户能清楚地看到从原始数据到最终结果的完整链路。

回头看

这个项目前前后后经历了十几轮迭代,从最初的"几个方块搭个架子"到现在这个有完整实验流程、物理模拟自洽、交互细节到位的版本。每一轮修改都来自实际使用中的观察——有时候是我自己操作时发现数据不对、视觉穿模,有时候是用户反馈的"这里差了一点""那里不太合理"。

做虚拟仿真最大的挑战不是技术本身,而是如何在"简化"和"真实"之间找到平衡。太简化了,学生学不到东西;太复杂了,又变成了另一个让人望而却步的实验装置。我希望这个版本刚好卡在中间:操作足够直观,数据足够真实,过程足够完整——让学生能在安全的虚拟环境里,把整个实验从头到尾做一遍,真正理解冷却法测比热容的原理。

一个网页,打开就能用。这大概就是虚拟仿真最朴素的价值。

最后提一句性能方面的事。三维场景在浏览器里实时渲染,对硬件还是有一定要求的。我在开发过程中做了不少优化——帧率限制在 30fps、像素比上限 1.5 倍、关闭抗锯齿、用 InstancedMesh 合并重复几何体、加热线的几何体每帧重建但其他部分保持静态——整体 CPU 占用率控制在了比较合理的范围。不过还是建议在 PC 上使用,手机和平板上跑这么重的三维场景体验会打折扣。

标签: none

添加新评论