电子网首页 > 开源与设计

【Let'sdo2025第三期活动】【拾色播放器DIY】颜色传

2026-01-01 20:45 | 来源:电子世界报

前言

这是"拾颜播放器"项目学习记录的第四部分,用颜色传感器检测物体的颜色。

TCS3200 是 DFRobot 的一款经典颜色传感器,内部有红、绿、蓝三种滤光片和光敏二极管阵列。它的工作原理是:白色 LED 照亮物体 → 物体反射光线 → TCS3200 通过不同滤光片测量 RGB 强度 → 输出频率信号 → MCU 计数并换算成颜色值。

一、硬件准备

项目用的硬件:

组件说明



ESP32-S3 Reverse TFT Feather主控板,240MHz 双核
DFRobot SEN0101TCS3200 颜色传感器
彩色纸片用于校准和测试(红、绿、蓝、白四色)

TCS3200 引脚说明引脚功能说明




VCC电源3.3V 或 5V
GND地线接地
S0频率缩放HIGH = 100%输出频率
S1频率缩放HIGH = 100%输出频率
S2滤波器选择控制红/绿/蓝/无滤波
S3滤波器选择控制红/绿/蓝/无滤波
OUT频率输出脉冲信号,频率与光强成正比
LEDLED 控制控制白光照明的开关

滤波器选择真值表S2S3选择通道




LOWLOW红色
LOWHIGH蓝色
HIGHHIGH绿色
HIGHLOW无滤波 (Clear)

注意:这个真值表与某些教程相反,需要以实际测试为准。本代码使用的映射与 Arduino 参考代码一致。

二、ESP32-S3 引脚连接

# 引脚配置(与Arduino代码一致)
S0_PIN = board.D9    # 频率缩放控制
S1_PIN = board.D10   # 频率缩放控制
S2_PIN = board.D11   # 滤波器选择
S3_PIN = board.D12   # 滤波器选择
OUT_PIN = board.D13  # 脉冲输出 (GPIO13)
LED_PIN = board.D18  # LED控制引脚

连接示意

TCS3200          ESP32-S3
--------        ---------
VCC     ──────►  3.3V 或 5V
GND     ──────►  GND
S0      ──────►  GPIO9 (D9)
S1      ──────►  GPIO10 (D10)
S2      ──────►  GPIO11 (D11)
S3      ──────►  GPIO12 (D12)
OUT     ──────►  GPIO13 (D13)
LED     ──────►  GPIO18 (D18)

image.pngimage.png三、初始化传感器3.1 基础初始化

from digitalio import DigitalInOut, Direction, Pullclass TCS3200Test:
    def __init__(self):
        self.s0 = None
        self.s1 = None
        self.s2 = None
        self.s3 = None
        self.out = None
        self.led = None
        self.enabled = False
        self.calibration_data = {}

    def setup(self):
        """初始化传感器引脚"""
        # S0 - 频率缩放控制
        self.s0 = DigitalInOut(S0_PIN)
        self.s0.direction = Direction.OUTPUT
        self.s0.value = True  # HIGH = 100%输出频率

        # S1 - 频率缩放控制
        self.s1 = DigitalInOut(S1_PIN)
        self.s1.direction = Direction.OUTPUT
        self.s1.value = True  # HIGH = 100%输出频率

        # S2, S3 - 滤波器选择
        self.s2 = DigitalInOut(S2_PIN)
        self.s2.direction = Direction.OUTPUT
        self.s3 = DigitalInOut(S3_PIN)
        self.s3.direction = Direction.OUTPUT        # OUT - 频率输出(输入引脚)
        self.out = DigitalInOut(OUT_PIN)
        self.out.direction = Direction.INPUT
        self.out.pull = Pull.UP        # LED控制
        self.led = DigitalInOut(LED_PIN)
        self.led.direction = Direction.OUTPUT
        self.led.value = True  # LED亮

        self.enabled = True
        return True

3.2 初始化流程说明

  1. S0 和 S1:设置为 HIGH,让传感器以 100% 频率输出。如果信号太弱,可以尝试降低频率(LOW, LOW = 2%)。

  2. S2 和 S3:初始化为 LOW(红色滤波器),之后会根据需要切换。

  3. OUT:设置为输入上拉,读取脉冲信号。

  4. LED:初始化为高电平,点亮白色 LED 照明。

四、读取颜色值4.1 选择颜色通道

def select_color(self, color):
    """选择颜色通道 (与Arduino代码一致)"""
    if color == 'red':
        self.s2.value = False
        self.s3.value = False
    elif color == 'green':
        self.s2.value = True
        self.s3.value = True
    elif color == 'blue':
        self.s2.value = False
        self.s3.value = True
    elif color == 'clear':
        self.s2.value = True
        self.s3.value = False

4.2 读取单通道原始值

def read_value(self, sample_time=0.15):
    """读取当前通道的原始值"""
    count = 0
    start = time.monotonic()
    while time.monotonic() - start < sample_time:
        if not self.out.value:  # 低电平计数
            count += 1
    return count

工作原理

  •  sample_time 时间内,统计 OUT 引脚为低电平的次数

  • 反射光越强,输出频率越高,低电平占比约 50%

  • 返回值范围通常是几百到几万

4.3 读取所有通道

def read_all_channels(self):
    """读取所有通道的原始值"""
    raw = {}
    for color_name in ['red', 'green', 'blue', 'clear']:
        self.select_color(color_name)
        time.sleep(0.005)  # 等待稳定
        raw[color_name] = self.read_value(0.1)
    return raw

返回值示例


{
    'red': 5234,    # 红色滤波器下的计数值
    'green': 2156,  # 绿色滤波器下的计数值
    'blue': 1876,   # 蓝色滤波器下的计数值
    'clear': 6421   # 无滤波下的计数值
}


五、校准功能5.1 为什么需要校准

TCS3200 的原始读数受以下因素影响:

  1. 光照条件:环境光强度变化

  2. 距离:物体与传感器的距离

  3. 物体材质:不同纸张的反射率不同

  4. 传感器个体差异:每个传感器略有不同

因此,需要在正式使用前进行校准,建立"标准颜色 → 原始读数"的映射关系。

5.2 校准流程

def calibration(self):
    """校准流程"""
    cal_colors = [
        ('white', '白色', (255, 255, 255)),
        ('red', '红色', (255, 0, 0)),
        ('green', '绿色', (0, 255, 0)),
        ('blue', '蓝色', (0, 0, 255)),
    ]

    for color_key, color_name, _ in cal_colors:
        print(f"请将 【{color_name}】 纸放在传感器上方(紧贴)")
        print("放置好后,按 【回车键】 开始读取...")
        input()  # 等待用户确认

        # 读取5次取平均,减少随机误差
        samples = []
        for i in range(5):
            raw = self.read_all_channels()
            samples.append(raw)
            time.sleep(0.1)

        # 计算平均值
        avg_raw = {}
        for channel in ['red', 'green', 'blue', 'clear']:
            avg_raw[channel] = sum(s[channel] for s in samples) / len(samples)

        self.calibration_data[color_key] = avg_raw

校准输出示例

校准数据摘要:
  白色: R=6421, G=6156, B=5890
  红色: R=8234, G=2156, B=1876
  绿色: R=2156, G=7890, B=2034
  蓝色: R=1876, G=2034, B=7456

5.3 校准注意事项

  1. 使用标准颜色:最好用标准色卡或打印的纯色块

  2. 保持距离一致:校准和正式使用时,物体距离传感器应相同

  3. 光线稳定:避免强光直射或光线突变

  4. 多测几次:可以多次校准取平均

六、颜色识别算法6.1 欧几里得距离匹配

校准后,识别颜色的方法是:计算当前读数与各校准颜色的距离,找出最近的:


def calculate_color_from_calibration(self, raw):
    """根据校准数据计算实际颜色"""
    best_match = None
    best_score = float('inf')

    for color_key in ['white', 'red', 'green', 'blue']:
        cal_data = self.calibration_data.get(color_key)
        if not cal_data:
            continue

        # 计算欧几里得距离
        diff = (
            (raw['red'] - cal_data['red']) ** 2 +
            (raw['green'] - cal_data['green']) ** 2 +
            (raw['blue'] - cal_data['blue']) ** 2
        )
        score = diff ** 0.5

        if score < best_score:
            best_score = score
            best_match = color_key
    return best_match, best_score


距离越小,说明当前颜色与校准颜色越接近。

6.2 RGB 归一化显示

除了颜色识别,还可以输出标准的 RGB 值:

def get_normalized_rgb(self, raw):
    """计算归一化的RGB值"""
    max_val = max(raw['red'], raw['green'], raw['blue'])
    if max_val > 0:
        r = min(255, int(raw['red'] * 255 / max_val))
        g = min(255, int(raw['green'] * 255 / max_val))
        b = min(255, int(raw['blue'] * 255 / max_val))
    else:
        r = g = b = 0
    return r, g, b

归一化原理:将最大值通道设为 255,其他通道按比例缩放,这样可以消除光照强度的影响。

七、完整使用流程7.1 主程序

def main():
    test = TCS3200Test()
    if test.setup():
        test.run()def run(self):
    """运行测试(校准后监测)"""
    # 1. 校准
    if not self.calibration():
        print("校准失败")
        return

    # 2. 实时监测
    while True:
        raw = self.read_all_channels()
        r, g, b = self.get_normalized_rgb(raw)
        hex_color = (r << 16) | (g << 8) | b        # 识别颜色
        matched_color, score = self.calculate_color_from_calibration(raw)

        print("R=0x%02X G=0x%02X B=0x%02X #%06X  %s" %
              (r, g, b, hex_color, matched_color))

        time.sleep(0.3)

7.2 运行结果

46c76de3099a4bad5c8ca60de12c7090.jpg


准备的测试颜色卡:

29b098a04fc5ca6ff4737c5899c72295.jpg

f2f113cda6bf754283a67766fdabf64f.jpg

image.pngimage.png


image.png


八、常见问题Q1: 读数一直不变

可能原因:

  • LED 没有亮起(检查 GPIO18 连接)

  • S2/S3 引脚接错

  • 物体距离太远

Q2: 所有颜色都显示白色

可能原因:

  • 校准数据有问题

  • 传感器灵敏度太高,需要增加物体距离

  • 环境光干扰

Q3: 红色显示蓝色,蓝色显示红色

这是滤波器选择映射错误。本代码使用与 Arduino 参考代码一致的映射,如果实际相反,修改 select_color() 函数中的真值表。

Q4: 读数波动大

解决方法:

  1. 增加采样时间 read_value(0.2) 而不是 0.1

  2. 多次读数取平均

  3. 确保物体稳定不移动

Q5: 如何识别更多颜色

 cal_colors 列表中添加更多颜色,然后更新颜色识别逻辑:

cal_colors = [
    ('white', '白色', (255, 255, 255)),
    ('red', '红色', (255, 0, 0)),
    ('green', '绿色', (0, 255, 0)),
    ('blue', '蓝色', (0, 0, 255)),
    ('yellow', '黄色', (255, 255, 0)),  # 新增
    ('purple', '紫色', (128, 0, 128)),  # 新增]

九、进阶用法9.1 颜色相似度阈值

可以设置一个阈值,拒绝识别置信度太低的颜色:

def get_color_with_confidence(self, raw, threshold=1000):
    """获取颜色及其置信度"""
    matched_color, score = self.calculate_color_from_calibration(raw)
    if score > threshold:
        return 'unknown', score    return matched_color, score

9.2 连续监测模式

在实时监测时,可以添加变化检测:

last_color = Nonewhile True:
    raw = self.read_all_channels()
    color, _ = self.calculate_color_from_calibration(raw)

    if color != last_color:
        print(f"颜色变化: {last_color} -> {color}")
        last_color = color

    time.sleep(0.3)

9.3 数据记录

将读取的值保存到文件:

def log_color_data(self, filename='color_log.txt'):
    """记录颜色数据"""
    with open(filename, 'a') as f:
        while True:
            raw = self.read_all_channels()
            r, g, b = self.get_normalized_rgb(raw)
            f.write(f"{time.time()}, {raw['red']}, {raw['green']}, {raw['blue']}, {r}, {g}, {b}\n")
            time.sleep(1)

十、总结

通过 TCS3200 颜色传感器,可以实现:

  1. 颜色检测:读取红、绿、蓝三通道的原始值

  2. 颜色校准:建立标准颜色的基准数据

  3. 颜色识别:根据校准数据识别未知颜色

  4. RGB输出:输出标准格式的 RGB 值

关键要点:

  • 校准是准确识别的关键

  • 保持测试条件与校准条件一致

  • 可以用欧几里得距离或曼哈顿距离进行匹配

  • 采样时间越长,读数越稳定

在"拾颜播放器"项目中,颜色传感器负责检测用户放置的物体颜色,然后系统根据颜色播放对应风格的音乐,实现了"检测 → 识别 → 反馈"的完整交互。


推荐技术

返回顶部