2、颜色基础理论:RGB色彩模型、HSV/HSL色彩模型、色温与亮度控制、人眼视觉特性
做氛围灯控制,说白了就是跟颜色打交道。但颜色这东西,看着简单,真要把它调准了、调顺了,背后那套理论还真得吃透。我刚开始做嵌入式灯控那会儿,就吃过亏——RGB值随便一填,灯是亮了,但颜色过渡生硬得像台阶,用户反馈说“这灯闪得我眼睛疼”。嗯,从那以后我才老老实实把颜色模型从头捋了一遍。
2.1 RGB色彩模型:最直接,也最反直觉
RGB模型,大家应该不陌生。红绿蓝三个通道,每个0~255,组合出1600多万种颜色。LED灯珠本身就是RGB三颗独立芯片,所以这个模型天然适合硬件驱动。
但这里有个坑——RGB不是线性变化的。你想想看,把R从0调到128,和从128调到255,人眼感受到的亮度变化完全不一样。我在项目中遇到过,直接用线性PWM调RGB,结果中间段亮度跳变特别明显,暗部又几乎没变化。
核心要点:RGB适合硬件输出,但不适合人眼感知调节。直接线性调RGB值,视觉上是不均匀的。
举个例子,你要调一个从红到蓝的渐变:
// 错误做法:直接线性插值
for (int i = 0; i <= 255; i++) {
R = 255 - i;
G = 0;
B = i;
setRGB(R, G, B);
delay(10);
}
// 结果:中间会出现难看的紫色,而且亮度不均匀
为什么会这样?因为RGB三个通道同时变化时,人眼对中间混合色的敏感度跟两端完全不同。我个人习惯是,做颜色过渡时,尽量避开RGB直接插值,改用后面要讲的HSV模型。
2.2 HSV/HSL色彩模型:更符合人的直觉
HSV和HSL这两个模型,说白了就是把人感知颜色的方式拆成了三个维度:
- H(Hue,色相):0°~360°,决定是什么颜色。红橙黄绿青蓝紫,转一圈。
- S(Saturation,饱和度):0%~100%,颜色鲜艳程度。0%是灰色,100%是纯色。
- V/L(明度/亮度):0%~100%,颜色的明亮程度。
HSV和HSL的区别,我简单说一下:HSV的V是“明度”,纯色时V=100%就是最亮;HSL的L是“亮度”,纯色时L=50%才是标准亮度。做氛围灯,我个人更推荐HSV,因为它的V值跟LED的PWM占空比映射起来更直接。
我的经验:在嵌入式里做HSV到RGB的转换,别自己手写数学公式。用查表法,256级色相查一次,速度快得多。我曾经在STM32上试过,查表比实时计算快了将近10倍。
看一个实际例子,用HSV做彩虹渐变:
// HSV转RGB的简化实现(嵌入式常用)
void hsv2rgb(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b) {
uint8_t region, remainder, p, q, t;
if (s == 0) {
*r = v; *g = v; *b = v;
return;
}
region = h / 43; // 60度一个区域
remainder = (h - (region * 43)) * 6;
p = (v * (255 - s)) >> 8;
q = (v * (255 - ((s * remainder) >> 8))) >> 8;
t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
switch(region) {
case 0: *r=v; *g=t; *b=p; break;
case 1: *r=q; *g=v; *b=p; break;
case 2: *r=p; *g=v; *b=t; break;
case 3: *r=p; *g=q; *b=v; break;
case 4: *r=t; *g=p; *b=v; break;
default: *r=v; *g=p; *b=q; break;
}
}
这段代码我用了好几年,基本没出过问题。注意那个h / 43,其实就是把360°分成6个60°区域,每个区域对应一种主色过渡。
2.3 色温与亮度控制:氛围灯的灵魂
色温这个概念,做氛围灯的人必须懂。简单说:色温越低,光越暖(偏黄红);色温越高,光越冷(偏蓝白)。单位是开尔文(K)。
| 色温值 | 典型场景 | RGB近似值 |
|---|---|---|
| 2700K | 白炽灯、烛光 | R=255, G=147, B=41 |
| 4000K | 暖白光、办公室 | R=255, G=197, B=143 |
| 6500K | 日光、标准白光 | R=255, G=255, B=255 |
| 10000K | 阴天、冷白光 | R=180, G=210, B=255 |
亮度控制这块,有个关键点——人眼对亮度的感知是对数的,不是线性的。你想想看,在黑暗房间里点一根蜡烛,感觉特别亮;但在大太阳底下点同样的蜡烛,几乎看不见。这就是韦伯-费希纳定律在起作用。
避坑指南:我曾经直接拿PWM的占空比当亮度值,结果用户说“调到10%以下灯就灭了,调到50%以上感觉没变化”。后来改用Gamma校正曲线,把PWM值做非线性映射,才解决了这个问题。
一个常用的Gamma校正公式:
// Gamma校正,gamma值通常取2.2
uint8_t gamma_correct(uint8_t input, float gamma) {
float normalized = input / 255.0f;
float corrected = powf(normalized, gamma);
return (uint8_t)(corrected * 255.0f);
}
// 实际使用时,可以预计算256级查找表
uint8_t gamma_table[256];
for (int i = 0; i < 256; i++) {
gamma_table[i] = gamma_correct(i, 2.2);
}
有了这个查找表,每次设置亮度时查一下表就行,CPU几乎零开销。
2.4 人眼视觉特性:做灯控必须懂的生理学
这部分内容,说白了就是“人眼是怎么看颜色的”。搞懂了它,你才能做出让用户觉得舒服的灯光效果。
第一,人眼对绿光最敏感。 在相同功率下,绿色LED看起来比红色和蓝色亮得多。所以做RGB混色时,绿色通道的PWM值通常要压低一些,否则整体颜色会偏绿。我一般会把绿色通道的系数设为0.7~0.8。
第二,人眼对暗部细节更敏感。 这就是为什么低亮度下颜色稍微一变,人就觉得“颜色不对”。反过来,高亮度下颜色偏差反而不容易被察觉。所以做低亮度模式时,颜色精度要更高。
第三,人眼有“色适应”能力。 你在暖色灯光下待久了,再看白纸会觉得它是白的。这个特性在做氛围灯动态切换时特别重要——色温切换不能太快,要给眼睛适应的时间。我一般把色温切换时间控制在1~3秒,用户反馈说“很自然”。
总结一下我的经验:
- 颜色过渡用HSV,别用RGB直接插值
- 亮度控制加Gamma校正,别线性调PWM
- 绿色通道系数压低,避免整体偏绿
- 色温切换给1~3秒缓冲,别瞬间跳变
嗯,这些基础理论看着枯燥,但每一条都是我在项目里踩过坑换来的。下一章我们聊聊具体的通信协议,怎么把这些颜色数据从主控发到灯板上。