800 流明的灯,为什么照到 1.6 米就只剩 25 勒克斯
一键开通华为云码道 CodeArts 代码智能体 :https://developer.huaweicloud.com/codeartsco.html?source=dmzntgwatomgit1&sourcead=dmzntgwatomgithd
先说这东西是什么
一个零依赖的单页网页。填四个数:灯泡总光通量、灯到桌面的距离、光线入射角、你想要的照度。它告诉你桌面实际有多少勒克斯、离目标差多少、这盏灯能均匀照亮多大一块圆、以及按光效折算这大概是几瓦的灯。
仓库在 atomgit.com/yl4928/desk-lux,MIT,双击 index.html 就能跑。
为什么做这个
我给我家书桌换过一次灯。买的时候盯着瓦数看,觉得 9 瓦 LED 挺亮,装上去一照,桌面还是暗。后来才搞明白我看错了两个东西:
一,瓦数是耗电,不是亮度。同样 9 瓦,光效 60 lm/W 和 110 lm/W 差将近一倍。
二,就算看流明也没用。流明是灯泡往外发的总量,桌面收到多少是另一回事,中间隔着距离、角度和受光面积。
"阅读要 300 勒克斯"这句话,从来没人和自己家那盏灯对上过。所以我把这个换算做成了一个能填数字的页面。
四条式子就把事情说完了
均匀照明 E = Φ / A 照度 = 光通量 / 受光面积 点光源强度 I = Φ / (4π) 各向同性假设下 点光源照度 E = I · cosθ / r² θ 是光线与桌面法线的夹角 反推需求 Φ = E · A单位:Φ 流明(lm)、I 坎德拉(cd)、E 勒克斯(lx)、A 平方米(m²)、r 米。
把 800 流明代进去,看看它随距离怎么塌:
800 lm 各向同性点光源,垂直入射: 0.2 m -> 1591.5 lx 亮到刺眼 0.3 m -> 707.4 lx 0.5 m -> 254.6 lx ← 台灯典型高度,还差一点到 300 0.8 m -> 99.5 lx 1.2 m -> 44.2 lx 1.6 m -> 24.9 lx ← 吊灯挂在这个高度,桌面就是"能看见"级别这张表解释了一件很常见的事:为什么吊灯亮堂堂、书桌却暗。灯到地面的高度是 2.4 米,你干活的地方在 1.6 米以下,同一盏灯到你眼睛前面那本书记的照度,只有到地面的一半不到——而人的直觉是"房间挺亮"。
角度那一栏也值得看:同一盏灯同样的距离,光线斜 45 度入射,照度直接乘 cos45° = 0.707。
白色系
这一批三篇统一白色系。这一篇的强调色是琥珀 #a86a12(灯光的暖调),警示色 #b3261e 只在"差多少 lx"那句和不达标的进度条上出现。全部内联、零外部资源、file:// 双击可跑,观感目标是一张打印出来的检测记录单。
提示词钉到函数级
这一篇的规格我写得比上一批更克制——因为上一批学到:规格越重,码道越容易空转。
1) illuminance(flux, area) E = flux / area;area <= 0 时 throw RangeError 2) luminousIntensity(flux) I = flux / (4π) 3) pointIlluminance(I, dist, cosT) E = I × cosθ / dist²;dist <= 0 时 throw RangeError 8) diskIlluminance(luminance, R, h, n) 数值积分:同心环面元,第 k 环半径 r_k = (k-0.5)R/n,环面积 dA = 2π·r_k·(R/n), 斜距平方 d² = h² + r_k²,两侧入射角余弦同为 h/√d²,每环贡献 dE = L·dA·cosθ·cosθ/d² 注意:dA 必须是 2πr·dr,不能写成 R²/n 或 πR²/n——上一版有人在这里丢了一个 π。最后那句"不能写成 R²/n"不是随手写的,是我在自己的差分脚本里真丢过一次 π,才知道这个坑有多好踩。
最硬的一条对拍:圆盘照度有解析解
点光源是理想化。真实台灯灯罩是个发光的曲面,所以我在引擎里放了一个有解析解可对照的数值积分:
半径 R、亮度 L 的均匀发光朗伯圆盘,在轴线上距盘 h 处的照度,教科书答案是
E = π·L·R² / (R² + h²)推导很短:取同心环 dA = 2πr·dr,斜距 d² = h² + r²,两侧余弦都是 h/d,于是
dE = L · dA · (h/d)² / d² = 2πL·h²·r·dr / (h² + r²)² ∫₀ᴿ = 2πL·h² · [ R² / (2(h²+R²)) ] = πL·R²/(R²+h²)引擎里的diskIlluminance用环带数值积分实现,我的参考实现用 30 位定点整数算同一个积分,两边都跟解析解比:
L=1000, R=0.1, h=0.3,解析值 314.159265359 n=200 误差 3.738e-4 n=2000 误差 3.738e-6 n=20000 误差 3.738e-8 n=200000 误差 3.738e-10网格每加密 10 倍,误差正好降 100 倍——这是 O(h²) 中矩形法则该有的样子,而且四位有效数字一点没飘。这条对拍比"两个数相等"有力得多,因为它验的是收敛行为,写错面元面积、写错余弦、漏掉 π,全都活不过这一关。
护城河:连 π 都是自己算的
上一批我把可信度押在国标全文上,结果取不到一手文本,只能写"公开转载"。这一批三个项目统一改成:锚点放在纯数学上,标准值只当可修改的输入。
所以参考实现里我不允许自己用Math.PI:
π = 16·arctan(1/5) − 4·arctan(1/239) (Machin 公式,arctan 用整数级数) ln(x) = 先用 2 的幂把 x 归一到 [0.5, 2],再用 2·artanh((x−1)/(x+1)) exp(x) = 参数约化到 |y| ≤ 1 做泰勒,再连乘还原 √x = 定点牛顿迭代 全程 BigInt,比例尺 10³⁰,只有整数运算先自检常数,再判引擎:
pi = 3.141592653590 (与 Math.PI 一致) ln2 = 0.693147180560 sqrt(2) = 1.414213562373然后跑差分,8585 次跨实现断言,0 分歧。脚本就在evidence/diff.mjs,一条命令可重放。
测试里我还专门留了一条"防丢 π"的断言:
// T7 面元面积不能丢 π// 闭式在 L=1、R=1、h=0.05 处是 πR²/(R²+h²) = π/1.0025 = 3.133753...assert.ok(Math.abs(eng.diskIlluminance(1,1,0.05,200000)-Math.PI/1.0025)<1e-4);如果实现把 dA 写成R²/n(丢了 2πr 也丢了 π),这条会差出好几个数量级。
300 勒克斯这个数,我把它降级成了输入
界面默认目标照度填 300 lx,来源是 GB 50034《建筑照明设计标准》里住宅书写阅读档的公开转载值。这台机器上我取不到该标准可访问的一手全文。
上一批我在 README 里写"标准规定",评审直接指出这是替标准说话。这次我改了三件事:
一,300 做成可修改输入,不做常量;二,页脚和 README 都明写"未取到一手全文,取自公开转载,仅作默认输入参考";三,把"点光源近似"这个更大的假设写在最前面——真实灯具的配光曲线不均匀,本工具算出来的是理想化下限估计,也就是"最多这么暗,实际可能更暗或更亮"。
工具最容易骗人的地方不是算错,是把一个近似说成结论。
这一轮我自己踩的坑
规格写太重,界面轮空转。第一版 UI 提示词我写了 2.7 KB,把每个像素和每条禁令都展开了。码道跑到正文 10.5 万字、贴了 62 个代码块,就是不落盘。停掉,压到 1.8 KB,只保留 id、公式、色值清单和禁止项,一次过。给 AI 的规格不是越详细越好,是越"能一次做完"越好。
上下文涨到 12 万字之后,什么都变慢。README 那轮在一个跑了七八个回合的老会话里,正文 11.8 万字、结束但没提交。我新开一个会话重发同一条提示词,60 秒落地。会话用久了要换新,别指望它自己瘦下来。
合并脚本时撞了标识符。我想把数学库和差分驱动合并成一个自包含文件提交进仓库,结果库里已有const PI、const pow2,驱动里也各有一个,直接 SyntaxError。修法是合并时剔除库里那两个由驱动自定义的顶层声明。这类"看起来只是复制粘贴"的活,恰恰最容易翻车。
"够不够"那一列我一开始写死了目标值。界面表格第三列原本是照 300 lx 判的,把目标改成 100 lx 之后表格还在说"差 200 lx"。改成随 target 动态判定才对。
提效数据
| 轮次 | 交付 | 结果 |
|---|---|---|
| R1 | src/engine.js 8 个函数 12 条锚点 | 一次过 |
| R2 | package.json + 7 个测试 | 一次过,本地 npm test 7/7 |
| R3 | index.html(2.7 KB 规格) | 空转,停 |
| R4 | index.html(1.8 KB 规格) | 一次过,控制台干净 |
| R5 | README + LICENSE(老会话) | 结束但未提交 |
| R6 | README + LICENSE(新会话) | 60 秒落地 |
| R7 | evidence/diff.mjs 入库 | 一次过,仓库内跑出 8585 断言 0 分歧 |
已知边界,不粉饰
各向同性点光源近似。灯到桌面的距离小于灯具自身尺寸时(比如台灯灯罩几乎贴着桌面),误差很大。
不含墙面、天花的反射光,也不含灯具效率(同样的灯珠,罩子吃掉 20~40%),所以算出来的照度普遍偏低于实测。
未计维护系数(灯用久了会衰减 20~30%)。
多盏灯需要逐盏算完再线性相加,界面只算单灯。
300 lx 这类参考值不替代专业照明设计;真正要设计得看 GB 50034 原文和照度计实测。
把"瓦数思维"换成"流明-距离思维"
同一张桌子上,我把几个常见组合用仓库里的引擎算了一遍(数值就是页面算出来的):
| 灯 | 光通量 | 到桌面距离 | 桌面照度 | 够不够 300 lx |
|---|---|---|---|---|
| 60 W 白炽的 LED 替换泡 | 806 lm | 0.45 m | 316.7 lx | 够 |
| 客厅吸顶灯 | 1500 lm | 1.6 m | 46.6 lx | 差 253.4 lx |
| 小台灯 | 400 lm | 0.35 m | 259.8 lx | 差 40.2 lx |
| 台灯 | 800 lm | 0.4 m | 397.9 lx | 够 |
结论很反直觉:一盏 1500 流明的主灯,在你读书的位置上,比不上一盏 400 流明的台灯。因为距离是平方反比,而流明只是总量。
1500 lm @ 1.6 m -> 46.6 lx 400 lm @ 0.35 m -> 259.8 lx 800 lm @ 0.4 m -> 397.9 lx客厅那 100 勒克斯呢
把目标改成 100 lx(客厅一般活动的公开转载参考值),一只 1600 流明的吸顶灯挂在 2.4 米:
1600 lm @ 2.4 m -> 22.1 lx 连 100 lx 都差 77.9也就是说,"客厅很亮"和"客厅够亮"是两回事——人眼对数响应加上天花板反射,会让你觉得亮,但桌面照度计不会同意。这张截图想说的是:达标与否完全取决于你把哪个面当受光面,同一个房间里"沙发区够亮"和"书桌够亮"是两件事。
交付回执与仓库
差分脚本入库后我重新 clone 验证过,在仓库目录里直接跑:
$nodeevidence/diff.mjs 跨实现断言8585次,分歧0条把数学库和驱动合并成自包含文件时,两边都有const PI、const pow2,直接撞出 SyntaxError。不在仓库目录里跑一遍,这种问题根本发现不了。
为什么用 4π,以及它什么时候不成立
各向同性点光源是个假得不能再假的模型——真实灯泡上方有灯头遮挡、台灯有灯罩、吸顶灯是面光源。我还是用了它,理由有两个:
一,它有一个可以手算的解析对照。圆盘那个例子里,数值积分和 πLR²/(R²+h²) 必须收敛到同一个数,这能验出实现层面的错。二,它是下限估计:把光均匀撒到整个球面上,任何方向的实际照度都不会比"总量守恒地摊开"更乐观地偏离太多。
但它有明确的失效区,我在 README 的已知边界里写了,这里说得更具体:
当 观察距离 < 灯具自身尺寸 时,点光源假设崩 例:灯罩直径 20 cm,桌面离灯罩 15 cm —— 此时 r 比灯具还小 点光源公式会给出偏高的照度(因为把整个灯罩当成一个点) 正确做法:把灯罩面拆成多个面元逐个叠加(本工具的 diskIlluminance 就是这条路)也就是说,界面里那个"理想化下限估计"的说法,对台灯贴得极近的场景其实偏乐观。这是我愿意承认的模型边界,而不是藏起来。
这个工具不管的三件事
买灯还有三个维度比照度更影响体验,本工具一概不算,免得让人误以为"算出来够亮就行":
均匀度 桌面中心 500 lx、边缘 50 lx 也很难受,本工具只算单点 眩光 灯珠裸露、遮光角小,照度够但刺眼,需要看 UGR 与灯具配光 显色性 Ra/Rg 与色温决定"看着舒不舒服",与流明无关上一批我做过台灯色温那篇,那管处理光谱,这管处理几何。两者不冲突,也不互相替代。
复用同一份数学库意味着什么
这一批三篇共用同一个 30 位定点库(π 用 Machin、ln 用 atanh 级数、exp 用泰勒、开方用牛顿迭代)。复用的代价是我在合并成自包含文件时踩了标识符冲突(两边都有 const PI),好处是:同一个 bug 只可能出现在一处,被查到一次就三篇都干净。
分贝篇要 log10,照度篇要 π 和开方,咖啡因篇要 ln2 和 exp——三个项目对数学库的覆盖面不一样,合起来才算把这份库真正压过一遍。
不打开网页时的三步心算
工具是给较真用的,日常判断有个粗糙版就够:
第一步 估强度 I ≈ Φ / 12.6 (4π ≈ 12.566,取 12.6 好算) 第二步 除距离方 E ≈ I / r² (r=0.5 -> 除 0.25,即乘 4) 第三步 比目标 阅读 300 lx、厨房操作 300 lx、客厅活动 100 lx 例一 800 lm 台灯,灯头离桌面 0.4 m I ≈ 800/12.6 ≈ 63 cd ;E ≈ 63/0.16 ≈ 398 lx -> 够读 例二 1500 lm 吸顶灯,挂 2.2 m,人坐桌前 r ≈ 1.45 m I ≈ 119 cd ;E ≈ 119/2.10 ≈ 57 lx -> 差得远,加台灯第二步最容易错的是"距离"要算灯到桌面,不是灯到地面。坐着时桌面高约 0.75 m,吸顶灯挂 2.2~2.4 m,r 只有 1.45~1.65 m。
这套心算和页面的精确值差在 5% 以内(因为我把 4π 圆成了 12.6)。页面上是精确数,心算用来在建材市场做判断——两者对得上,说明我没有把工具当成权威。
怎么自己验一次这个换算
我不要求你信页面。手上有一台带照度计 App 的手机(多数安卓的 lux API 精度有限,iOS 需要外接传感器),可以做一次粗验证:
1. 关灯,把手机放在桌面固定位置,记录本底照度 E0(通常 < 5 lx) 2. 只开这一盏灯,同一位置记录 E1 3. 量出灯发光中心到传感器平面的垂直距离 r,以及光线与法线的夹角 θ 4. 查灯泡包装上的标称流明 Φ 5. 比对:页面算的 Φ·cosθ/(4πr²) 与实测 (E1 - E0)差多少算正常?我的经验判断是 20~40%,来源有三个:手机传感器余弦响应不好(斜射光记不满)、灯具不是各向同性(灯罩把光往下压,实测会高于页面值)、房间反射(也会偏高)。所以如果实测明显低于页面值,大概率是距离量错了或者灯泡实际流明虚标——这恰恰是页面能帮你发现的问题。
这一段没有对应的自动化测试,因为它是现实。工具能验的是算术,验不了世界。
为什么值得为"够不够亮"单独做一个工具
照度是这一批三个项目里最"看不见"的量:分贝你听得见,咖啡因你有体感,而勒克斯没有直接感觉——人眼会自动适应整体亮度,你能判断"这屋比那屋亮",但判断不出"300 还是 150"。
这恰恰是它需要被算的原因。买灯时包装上写流明、商家说瓦数、装修图上标照度,三套单位互相不认。把它们串起来只需要两条式子(I = Φ/4π、E = I·cosθ/r²),但没人会在建材市场掏笔算。所以这个工具的价值不在算法,在于把两条式子做成一个输入框。
它也算不了全部:均匀度、眩光、显色性都还得看别的(前面那节写了我不算什么)。至少你从此不会因为"买了 1500 流明的灯"而以为书桌一定够亮;真要说够,得把灯到桌面的距离量出来,让平方反比替你说话。
本地怎么跑
gitclone https://atomgit.com/yl4928/desk-lux.gitcddesk-luxnpmtest# 零依赖,7 个用例nodeevidence/diff.mjs# 定点参考实现 vs 引擎,8585 次跨实现断言# 界面双击 index.html写在最后
这一篇最让我满意的是那条圆盘对拍。
它不是"我算了一遍你也算了一遍结果一样",而是"一个用积分算、一个用解析解算,中间把网格加密十倍,误差必须老老实实降一百倍"。这种验证不会因为改错一行代码而侥幸通过——它验的是形状,不是点。
至于 300 勒克斯该不该是 300,那是标准的事,我把它做成输入框;灯到底够不够亮,你自己填三个数就能算出来。
(本工具用于理解光通量与照度的关系及选购参考,不构成照明设计或用眼健康建议。)