大数跨境
0
0

【光谱传感器测评】家庭常用光源对比

【光谱传感器测评】家庭常用光源对比 蘑菇云创造
2020-12-01
1
导读:精选测评项目

点击上方“蘑菇云创造”可以关注我们哦


引言


看过《石纪元》的小伙伴可能会喜欢里面的这样一句话:“在我们的时代里,不存在黑暗!”。自从爱迪生于1879年发明了世界上第一支具有实用价值的电灯泡,电灯已经有140多年的发展历史。对于人类历史来说,这只是短短的一瞬,但是这并不妨碍电灯成为人类历史上最重要的发明。在此期间,人类对电灯进行了多次的迭代,很多技术层出不穷。在这篇文章里我将用DFRobot出品的 AS7341 可见光谱传感器对家庭常用的几种灯泡进行详细对比。

原理


原理


首先,我们要了解关于光的一些基本知识。众所周知,太阳光是由赤橙黄绿青蓝紫其中颜色的光组成,然而这种表述并不准确,太阳光是由一段连续的光谱组成,能完整地覆盖人眼可见光的范围。


人类之所以能看到这个五彩缤纷的世界,全要感谢眼睛里的视杆细胞和视锥细胞,视杆细胞对弱光敏感,擅长在黑暗的环境下看清物体轮廓,夜行动物的眼睛多富有视杆细胞;相对的视锥细胞则能对不同颜色的光产生反应,还原物体的颜色。人类视网膜上存在着红、绿、蓝三种视锥细胞,一束光会对这三种视锥细胞造成不同程度的刺激,这个刺激值在大脑中就会被还原成光的颜色。也就是说,颜色是人大脑中的概念,如果你将红绿蓝三原色以正确的比例混合,就能够得到任何你想要的颜色,你眼前手机的、电脑的屏幕正是利用这个原理才能显示出各种各样的颜色的。



接下来是色温。从理论上说,任何有温度的物体都会主动向外界辐射电磁波,且温度越高,波长越短,看起来就是从黑变红,再到黄、白,最后发出蓝色光,打铁时铁受热会发红,煤气灶点然后发出的蓝光都是因为这个原理。普通白炽灯正常工作时,灯丝可达2500摄氏度,转换成绝对温标就是2773K,这就是白炽灯产生的光的色温,是一种黄白色的光。现代的很多灯泡像是荧光灯、LED灯采用了不同的发光原理,但是灯泡的包装上可能会写着“色温5000K”之类的字样,这并不是说灯泡在工作时,内部产生了那么高的温度,而是指光的颜色等效于同等温度下黑体产生的光的颜色,也就是相关色温的概念。


要理解计算色温非常复杂,但是这里我采用公式法拟合,将过程简化成了下面几个步骤:

  1.为了计算光的色温,我们需要利用传感器获得这个光的光谱,以及人眼三种视锥细胞对不同波长的光的敏感程度——三刺激函数。三刺激函数可以通过查表获得。


  2. 光谱分别与三刺激函数相乘再进行积分就能得到三刺激值(X、Y、Z)。


3. 通过公式转换为色坐标(x-y),任何一种颜色都能在色坐标中找到。


  4. 最后再通过色坐标计算色温。



代码实现


为了能更直观地看到光谱数据,我用Processing写了一个程序。


向上滑动阅览

/***************************************************************************

Created by dbc0301

***************************************************************************/

 

import processing.serial.*;

Serial port;

 

PFont myFont;

 

int tmp;

//int begin='$';//begin

int end='\r';//end

char rev[] = new char[15];//datas

int revFlag=0;

 

int[] data=new int[10];//F1,F2,F3,F4,F5,F6,F7,F8,Clear,NIR

//int F1,F2,F3,F4,F5,F6,F7,F8,Clear,NIR;

 

//刺激函数

float[] Fx={0.07763, 0.34806, 0.09564, 0.02910, 0.51205, 1.02630, 0.64240, 0.04677};

float[] Fy={0.00218, 0.02980, 0.13902, 0.60820, 1.00000, 0.75700, 0.26500, 0.01700};

float[] Fz={0.37130, 1.78260, 0.81295, 0.11170, 0.00575, 0.00110, 0.00005, 0.00000};

 

int textHight=25;

float rt=1;//长度缩放比例

 

void receiveDatas(){

    for(int i=0;port.available()>0;i++){

      tmp=port.read();

      if(tmp!=end){

        rev=char(tmp);

      }else{

        rev=char(tmp);

        revFlag=1;

        tmp=port.read();

        break;

      }

    }

}

 

void setup(){

  //size(1074,241);

  size(1250,301);

  background(0);//white255 black0

  noStroke();

  myFont = createFont("微软雅黑", 20);

  textFont(myFont);

 

  println(Serial.list()[0]);

  port = new Serial(this,Serial.list()[0],115200);

}

 

void draw(){

  receiveDatas();

  if(revFlag==1){

    String[] m=match(new String(rev), "(.*?):(.*?)\r");//正则表达式匹配

    //printArray(m);

    try{

      if(m[1].equals("F1")){

        data[0]=int(m[2]);

      }else if(m[1].equals("F2")){

        data[1]=int(m[2]);

      }else if(m[1].equals("F3")){

        data[2]=int(m[2]);

      }else if(m[1].equals("F4")){

        data[3]=int(m[2]);

      }else if(m[1].equals("F5")){

        data[4]=int(m[2]);

      }else if(m[1].equals("F6")){

        data[5]=int(m[2]);

      }else if(m[1].equals("F7")){

        data[6]=int(m[2]);

      }else if(m[1].equals("F8")){

        data[7]=int(m[2]);

      }else if(m[1].equals("Clear")){

        data[8]=int(m[2]);

      }else if(m[1].equals("NIR")){

        data[9]=int(m[2]);

      }else{

        print("Wrong datas!");

      }

    }catch(NullPointerException e){

      println(rev);

      printArray(m);

    }finally{}

    revFlag=0;

    //printArray(data);

    //delay(10);

  }

 

  /*显示*/

  //rectMode(CORNER);

  background(0);

  textSize(20);

 

  fill(#8b3dc5);

  rect(0,0,data[0]*rt,30);//F1

  text(data[0], data[0]*rt, 0+textHight);//textHeight是文字的垂直高度

 

  fill(#00528e);

  rect(0,30,data[1]*rt,30);//F2

  text(data[1], data[1]*rt, 30+textHight);

 

  fill(#00b1ed);

  rect(0,60,data[2]*rt,30);//F3

  text(data[2], data[2]*rt, 60+textHight);

 

  fill(#01ffcd);

  rect(0,90,data[3]*rt,30);//F4

  text(data[3], data[3]*rt, 90+textHight);

 

  fill(#00af50);

  rect(0,120,data[4]*rt,30);//F5

  text(data[4], data[4]*rt, 120+textHight);

 

  fill(#ffff01);

  rect(0,150,data[5]*rt,30);//F6

  text(data[5], data[5]*rt, 150+textHight);

 

  fill(#ffc000);

  rect(0,180,data[6]*rt,30);//F7

  text(data[6], data[6]*rt, 180+textHight);

 

  fill(#c10005);

  rect(0,210,data[7]*rt,30);//F8

  text(data[7], data[7]*rt, 210+textHight);

 

  fill(#ffffff);

  rect(0,240,data[8]*rt,30);//Clear

  text(data[8], data[8]*rt, 240+textHight);

 

  fill(#888888);

  rect(0,270,data[9]*rt,30);//F8

  text(data[9], data[9]*rt, 270+textHight);

 

  //delay(1);

 

 

  /*色温*/

  float X,Y,Z,x,y,n,temp;

  X=(data[0]*Fx[0] + data[1]*Fx[1] + data[2]*Fx[2] + data[3]*Fx[3] + data[4]*Fx[4] + data[5]*Fx[5] + data[6]*Fx[6] + data[7]*Fx[7]);//20/1000;//20是积分区间,1000是为了将数值转换成0-1之间的实数

  Y=(data[0]*Fy[0] + data[1]*Fy[1] + data[2]*Fy[2] + data[3]*Fy[3] + data[4]*Fy[4] + data[5]*Fy[5] + data[6]*Fy[6] + data[7]*Fy[7]);//20/1000;//但在这里乘上这个数没什么意义,反正在下一步就约掉了

  Z=(data[0]*Fz[0] + data[1]*Fz[1] + data[2]*Fz[2] + data[3]*Fz[3] + data[4]*Fz[4] + data[5]*Fz[5] + data[6]*Fz[6] + data[7]*Fz[7]);//20/1000;

 

  x=X/(X+Y+Z);

  y=Y/(X+Y+Z);

 

  n=(x-0.3320)/(0.1858-y);

  temp=437*n*n*n+3601*n*n+6831*n+5517;

  print(temp);

  println("K\n");

 

}


以及对应的Arduino程序


向上滑动阅览

#include "DFRobot_AS7341.h"


DFRobot_AS7341 as7341;


void setup(void)

{

  Serial.begin(115200);

  //Detect if IIC can communicate properly 

  while (as7341.begin() != 0) {

    Serial.println("IIC init failed, please check if the wire connection is correct");

    delay(1000);

  }

//  //Integration time = (ATIME + 1) x (ASTEP + 1) x 2.78µs

//  //Set the value of register ATIME, through which the value of Integration time can be calculated. The value represents the time that must be spent during data reading.

  as7341.setAtime(29);

//  //Set the value of register ASTEP, through which the value of Integration time can be calculated. The value represents the time that must be spent during data reading.

  as7341.setAstep(599);

//  //Set gain value(0~10 corresponds to X0.5,X1,X2,X4,X8,X16,X32,X64,X128,X256,X512)

  as7341.setAGAIN(2);//测试时用2

//  //Enable LED

//  //as7341.enableLed(true);

//  //Set pin current to control brightness (1~20 corresponds to current 4mA,6mA,8mA,10mA,12mA,......,42mA)

//  //as7341.controlLed(10);

}

void loop(void)

{

  DFRobot_AS7341::sModeOneData_t data1;

  DFRobot_AS7341::sModeTwoData_t data2;


  //Start spectrum measurement 

  //Channel mapping mode: 1.eF1F4ClearNIR,2.eF5F8ClearNIR

  as7341.startMeasure(as7341.eF1F4ClearNIR);

  //Read the value of sensor data channel 0~5, under eF1F4ClearNIR

  data1 = as7341.readSpectralDataOne();


  Serial.print("F1:");//(405-425nm)

  Serial.println(data1.ADF1);

  Serial.print("F2:");//(435-455nm)

  Serial.println(data1.ADF2);

  Serial.print("F3:");//(470-490nm)

  Serial.println(data1.ADF3);

  Serial.print("F4:");//(505-525nm)

  Serial.println(data1.ADF4);

  Serial.print("Clear:");

  Serial.println(data1.ADCLEAR);

  Serial.print("NIR:");

  Serial.println(data1.ADNIR);

  //delay(50);


  as7341.startMeasure(as7341.eF5F8ClearNIR);

  //Read the value of sensor data channel 0~5, under eF5F8ClearNIR 

  data2 = as7341.readSpectralDataTwo();

  Serial.print("F5:");//(545-565nm)

  Serial.println(data2.ADF5);

  Serial.print("F6:");//(580-600nm)

  Serial.println(data2.ADF6);

  Serial.print("F7:");//(620-640nm)

  Serial.println(data2.ADF7);

  Serial.print("F8:");//(670-690nm)

  Serial.println(data2.ADF8);

  Serial.print("Clear:");

  Serial.println(data2.ADCLEAR);

  Serial.print("NIR:");

  Serial.println(data2.ADNIR);

  delay(50);

}



其中的as7341.setAGAIN(2)可以调节数值的增益。光线较暗的环境下也可以通过调整as7341.setAtime(29)和as7341.setAstep(599)里面的数值来增加积分时间。


下面是测试的效果图,从上到下的十根色柱分别是传感器的F1-F8、无滤镜通道,以及红外通道。同时下面还在不停打印计算出来的色温值,由于传感器本身精度,而且也未经过专业仪器校准,可能会有几百开尔文的误差,结果仅供参考。



测试部分


用快递箱改装的试验箱


下面是参赛选手


已知数据


首先上场的是1号选手——25W白炽灯,可以看出光谱的曲线非常平滑,而且红外的成分非常高,看起来超出了传感器的测量范围,色温大致在2650K左右波动。



测试后,整个测试箱都是温热的,可见25W的功率绝大部分都被转换为了热量。

接下来是2号至4号LED,之所以放在一起是因为它们都出自同一个厂家。从光谱上可以看到这组灯泡在435-455nm的波段不约而同的有一个小尖刺。2号的色温大致在4700K,和标注的6000K差一大段距离,3号色温在4750K左右浮动,4号则4830K上下,可见它们很有可能采用了同一种LED灯珠。


2号


3号


4号


下面是5号荧光灯,虽然标注着5W,然而从光谱上看好像还不如3W的LED亮,能效比堪忧啊。然而荧光灯刚打开时数值不是很稳定,看起来还没有进入状态,过了3-5分钟后,亮度稍有提升,但还是远低于3W的LED。


未进入状态


3-5分钟后


最后一名选手是宜家的5W2700K的LED,实测2680K左右,看来还是挺准的。作为选手中最贵的灯泡,它有着最平滑的光谱曲线。



闪烁频率


AS7341传感器还具有测量光源闪烁频率的功能,不过只能得出无闪烁、50HZ、60HZ和位置频率闪烁这四种结果,我用传感器分别测量了每个灯泡的闪烁频率,发现它们都以50Hz在闪烁,这是因为我国交流电是50Hz的,然而同样是在闪烁,但它们给人的感受是不同的。用手机调高快门或是开启慢镜头摄影后发现,最便宜的2号LED灯闪的厉害,而且偏冷的色温让人有炫光的感觉,最好的是6号LED,基本上察觉不到在闪烁。这是因为2号LED采用的是阻容降压,几乎没有过滤掉交流电里的交流分量,而6号LED采用专业的恒流驱动芯片,可以将交流分量控制在很小的范围。而普通的白炽灯由于没有任何滤波电路,在手机镜头下也是在快速闪烁,但是却没有2号LED闪烁那么刺眼。(以下图片均是在x32的慢镜头下拍摄)


2号LED


6号LED

1号白炽灯


综上所述,LED光能转化率完爆其它灯泡,但为了节省成本,不少厂家都会选用显色率低的普通灯珠,而且没有恒流电路,结果LED产生很大的闪烁,一般选择大厂生产的灯泡即可,不要贪图便宜。另一方面,日常使用时可以选择色温偏暖的灯泡,虽然这样会让周围的一切看起来发黄发红,但是对人眼刺激小,不易产生炫光。
假如是摄影之类,为了还原物体的真实色彩,也可以选用5600K高显色指数的灯泡。


参考资料


1、1nm间隔的三刺激函数:

https://wenku.baidu.com/view/db2c6b9cf7ec4afe05a1dfbc.html


2、色温所对应的RGB颜色表;

https://wenku.baidu.com/view/f7bfc59168dc5022aaea998fcc22bcd126ff4237.html



资料下载,请点击“阅读原文”获取!



本文来源:DF创客社区

作者:dbc0301





蘑菇云创造

微信号 : mgystem

扫码关注,了解更多





推荐阅读:


【声明】内容源于网络
0
0
蘑菇云创造
蘑菇云是DFRobot旗下专注于AI人工智能、创客、STEAM、劳动教育的科技创新教育品牌;以为中国培养下一代科技创新人才为使命,为学校提供k12全龄段科技创新教育解决方案。
内容 969
粉丝 0
蘑菇云创造 蘑菇云是DFRobot旗下专注于AI人工智能、创客、STEAM、劳动教育的科技创新教育品牌;以为中国培养下一代科技创新人才为使命,为学校提供k12全龄段科技创新教育解决方案。
总阅读1.7k
粉丝0
内容969