基于GP2Y1010AU0F的PM2.5监测:从光学散射原理到Arduino实践
1. 项目概述:从一颗灰尘说起
你可能没意识到,我们每天呼吸的空气中,除了看不见的氧气和氮气,还飘浮着无数微小的固体颗粒,也就是我们常说的粉尘或PM(颗粒物)。这些小家伙的直径从几微米到零点几微米不等,来源五花八门,比如室外汽车尾气、工业排放、建筑扬尘,室内的烹饪油烟、吸烟、甚至是你抖被子时飞起的纤维。长期暴露在高浓度的颗粒物环境中,对呼吸系统和心血管系统的健康影响是确凿无疑的。所以,了解并监测我们身边的空气质量,特别是颗粒物浓度,从一个DIY爱好者的角度看,既是一项有趣的电子项目,也是一件挺有实际意义的事儿。
今天我们要拆解和实操的核心,就是一颗专门用来“看见”这些灰尘的电子元件——夏普的GP2Y1010AU0F光学灰尘传感器。它不像专业的、动辄上万的检测仪那样复杂,但其原理却一脉相承。通过这个项目,你不仅能亲手搭建一个简易的空气质量监测装置,更能彻底搞懂这类传感器是如何工作的,从硬件连接到数据处理,再到校准和实际应用中的那些“坑”,我都会结合自己的实测经验,带你走一遍。无论你是刚接触Arduino的新手,还是想给智能家居项目增加环境感知能力的老玩家,这篇内容都会给你提供可以直接“抄作业”的完整方案和避坑指南。
2. 传感器工作原理深度解析
2.1 光学散射法的基本原理
GP2Y1010AU0F采用的方法叫做“光学散射法”,这是目前低成本颗粒物检测中最主流的技术之一,甚至在很多专业设备中也能见到它的身影。它的核心思想其实非常直观:用光去“照亮”空气中的颗粒,然后看有多少光被这些颗粒“撞飞”了。
想象一下在一个略微昏暗的房间里,有一束明亮的激光笔光柱。如果空气非常干净,你几乎看不到光柱的路径。但如果你在光柱前拍打一块毛毯,瞬间就能看到光柱中无数灰尘颗粒在闪闪发光,这是因为灰尘将光线散射到了你的眼睛里。GP2Y1010AU0F传感器内部就构建了这样一个微型的“光路实验室”。
具体来说,传感器内部有一个红外发光二极管(IRED)作为光源,以及一个光电晶体管作为光信号接收器。关键的设计在于,这两者并非直线对准。它们以一定的角度(通常是锐角)放置,中间有一个让空气流通的采样区域。当IRED发射出特定波长的红外光时,如果采样区域内没有颗粒物,那么光线会直接穿过,几乎不会被侧面的光电晶体管接收到。一旦有颗粒物通过这个区域,红外光照射到颗粒上就会发生散射,其中一部分散射光的方向恰好会落到光电晶体管上。颗粒物越多、越大,散射的光就越强,光电晶体管接收到的信号也就越强。
注意:这里测量的是“散射光强度”,而不是“透射光衰减”。这与激光打印机检测碳粉余量的原理不同,那种是测量穿透介质后的光强减弱。散射法对低浓度、小颗粒更敏感,非常适合室内空气质量的测量场景。
2.2 GP2Y1010AU0F的构造与信号特征
拆开(当然,不建议你真的拆,会损坏)或者看它的结构图,你会发现它的精巧之处。IRED和光电晶体管被精心布局,以最大化对散射光的收集效率,同时最小化来自传感器内壁反射的背景光干扰。它还有一个内置的风道引导空气流过检测区域。
这个传感器输出的并不是一个直接表示浓度值的电压,而是一个模拟电压信号。这个电压值与采样区域内颗粒物的密度大致成正比关系。但这里有一个非常重要的细节:为了降低环境光干扰并提高信噪比,GP2Y1010AU0F需要以脉冲方式工作。也就是说,我们不能简单地给IRED通上恒定的电流让它一直亮着。
数据手册要求我们以周期性的方式驱动IRED:先让IRED熄灭至少0.32毫秒,然后将其点亮0.32毫秒,就在这点亮结束前的0.28毫秒这个非常精确的时刻,去读取光电晶体管输出的电压值。这个电压是一个峰值电压,反映了在IRED点亮期间,颗粒物散射光的强度。之后,IRED再次熄灭,进入下一个循环。这种“采样-保持”式的脉冲工作模式,能有效滤除环境光缓慢变化带来的直流分量干扰,让测量更加稳定。
输出的模拟电压通常在0到3V左右变化(具体取决于供电电压和颗粒物浓度)。在清洁空气中,它有一个固有的“零点”电压(通常约0.9V),这个电压是由传感器内部元件本身的特性以及微小的背景光反射造成的,并不代表颗粒物浓度为零。真正的颗粒物浓度信息,蕴含在电压相对于这个“零点”的升高量中。
3. 硬件电路设计与连接要点
3.1 最小系统电路搭建
要让GP2Y1010AU0F正常工作,仅把它接到Arduino的模拟输入口上是行不通的。它需要一个严格遵循时序的驱动电路。根据数据手册,一个典型的最小应用电路包含以下几个关键部分:
IRED驱动回路:IRED的阴极(较短引脚)通过一个串联的150欧姆电阻连接到单片机(如Arduino)的一个数字IO引脚。这个电阻用于限制电流,保护IRED。IRED的阳极(较长引脚)接电源正极(VCC,通常是5V)。这样,当我们把那个数字IO引脚设置为低电平时,IRED两端获得压差而点亮;设置为高电平时,IRED熄灭。
光电晶体管输出回路:光电晶体管本质上是一个光控电流源。它通常连接一个负载电阻(数据手册推荐值为150欧姆)到地(GND),传感器的Vo引脚(模拟输出)就是从光电晶体管的集电极和这个负载电阻之间引出的。这个Vo引脚直接连接到Arduino的某个模拟输入引脚(如A0)。负载电阻的作用是将光电晶体管产生的电流信号转换为电压信号,其阻值大小会影响输出的电压范围和灵敏度。
电源去耦:在传感器的VCC和GND引脚之间,紧挨着传感器放置一个220μF的电解电容。这一点至关重要。因为IRED在脉冲点亮瞬间会吸入较大的电流,可能引起电源线上的电压瞬间跌落,这个波动不仅会影响传感器本身的稳定工作,还可能通过电源线干扰Arduino或其他电路。大电容的作用就是作为一个本地的小“水池”,在IRED点亮的瞬间提供瞬时电流,平滑电源电压。
一个完整的连接示意图(以Arduino Uno为例)如下:
- 传感器VCC-> Arduino5V引脚
- 传感器GND-> ArduinoGND引脚
- 传感器LED-> 通过一个150Ω电阻连接到 Arduino数字引脚2(或其他任意数字引脚)
- 传感器Vo-> Arduino模拟引脚A0
- 传感器LED-GND-> 直接连接 ArduinoGND
- 在传感器VCC和GND引脚之间,并联一个220μF电解电容(注意极性,正极接VCC)。
3.2 元件选型与布局心得
虽然电路不复杂,但元件选择和PCB布局(如果是自制电路板的话)上的一些细节会显著影响测量稳定性。
首先是那个150欧姆的负载电阻。数据手册推荐的是150欧姆,但你可以根据需要的输出电压范围进行微调。阻值增大,相同光电流下产生的电压更高,灵敏度增加,但输出阻抗也变大,更容易受到噪声干扰。阻值减小则相反。对于Arduino的5V系统和3.3V ADC参考电压,150欧姆是一个比较均衡的选择。我曾试过使用100欧姆和220欧姆,实测下来150欧姆在信噪比和量程上表现最好。
其次是电容的选择。220μF电解电容是必须的,最好选用低ESR(等效串联电阻)的型号,这样响应速度更快。电容要尽可能靠近传感器的电源引脚焊接,引线过长会引入寄生电感,削弱其缓冲效果。此外,我习惯在220μF大电容旁边再并联一个0.1μF的陶瓷电容,用于滤除更高频率的噪声。
关于布局,最关键的一点是避免将传感器的信号线(Vo)与数字控制线(如连接IRED的线)或电源线长距离平行走线。数字信号的快速跳变会通过电磁耦合干扰微弱的模拟信号。如果使用面包板,尽量让连接线短而直。如果自制PCB,应将模拟地(传感器GND到Arduino GND的路径)单独走线,最后在一点接入电源地,形成“星型接地”,这能有效避免地线噪声。
实操心得:在面包板上搭建这个电路时,我最常遇到的问题是读数跳动大。十有八九是去耦电容没接好或容量不足。务必确保220μF电容正确连接。另一个常见问题是Vo引脚悬空或接触不良,导致读数随机乱跳。用万用表检查Vo引脚与A0引脚之间的连通性,以及电压是否在合理范围(0-3V之间),是快速排查的第一步。
4. 软件驱动与数据采集实现
4.1 精确的时序控制程序
驱动GP2Y1010AU0F的核心在于软件必须严格遵循其工作时序。下面是一个基于Arduino的经典驱动函数,我对其中的关键步骤加了详细注释:
// 定义引脚 const int dustSensorLED = 2; // 连接传感器LED引脚的IO const int dustSensorVo = A0; // 连接传感器Vo引脚的模拟输入 void setup() { Serial.begin(9600); pinMode(dustSensorLED, OUTPUT); digitalWrite(dustSensorLED, HIGH); // 初始状态,LED熄灭(低电平点亮) } void loop() { float dustDensity = readDustDensity(); Serial.print("Dust Density: "); Serial.print(dustDensity); Serial.println(" ug/m3"); delay(1000); // 每秒读取一次 } float readDustDensity() { // 步骤1: 确保LED熄灭至少0.32ms digitalWrite(dustSensorLED, HIGH); // LED OFF delayMicroseconds(320); // 等待0.32ms // 步骤2: 点亮LED digitalWrite(dustSensorLED, LOW); // LED ON delayMicroseconds(280); // 保持点亮0.28ms // 步骤3: 在点亮结束前(即此刻)读取模拟值 int rawValue = analogRead(dustSensorVo); // 步骤4: 完成0.32ms的点亮周期 delayMicroseconds(40); // 0.28ms + 0.04ms = 0.32ms digitalWrite(dustSensorLED, HIGH); // 再次熄灭LED // 将ADC原始值转换为电压 (假设Arduino ADC参考电压为5V) float voltage = rawValue * (5.0 / 1024.0); // 转换为粉尘浓度(使用经验公式,需校准) // 公式:浓度 (ug/m3) = K * (电压 - V0) // 其中 V0 是清洁空气下的电压,K 是灵敏度系数 float V0 = 0.9; // 示例零点电压,需要实际测量 float K = 0.5; // 示例灵敏度系数,需要校准 float density = K * (voltage - V0); // 确保浓度不为负值 if (density < 0) density = 0; return density; }这段代码的精髓在于delayMicroseconds()函数的使用,它提供了微秒级的延迟,对于满足传感器0.32ms的时序要求至关重要。analogRead()函数本身需要大约100微秒的时间,所以我们选择在点亮LED 0.28ms后读取,这个时间点既保证了散射光信号已经建立稳定,又为ADC读取留出了时间。
4.2 模拟信号读取与滤波处理
Arduino的analogRead()函数返回一个0到1023的整数,对应0到基准电压(默认5V)。直接使用单次读取的值是非常不稳定的,因为信号中混杂了各种噪声(电源噪声、ADC量化噪声、环境光微小波动等)。
移动平均滤波是最简单有效的软件滤波方法。它的原理是维护一个最近N次采样值的队列,每次新的读数到来时,将其加入队列,并剔除最旧的一个,然后计算队列中所有值的平均值作为当前输出。这能平滑掉随机尖峰噪声。
#define FILTER_WINDOW 10 float filterBuffer[FILTER_WINDOW]; int bufferIndex = 0; float filteredReadDustDensity() { float sum = 0; // 连续采样FILTER_WINDOW次,每次间隔至少一个完整脉冲周期 for(int i = 0; i < FILTER_WINDOW; i++) { sum += readDustDensity(); // 调用之前的单次读取函数 delay(1); // 短暂延时,确保每次采样独立 } float averageDensity = sum / FILTER_WINDOW; // 或者使用滑动窗口平均(更高效) // filterBuffer[bufferIndex] = readDustDensity(); // bufferIndex = (bufferIndex + 1) % FILTER_WINDOW; // for(int i = 0; i < FILTER_WINDOW; i++) { sum += filterBuffer[i]; } // averageDensity = sum / FILTER_WINDOW; return averageDensity; }窗口大小FILTER_WINDOW需要权衡:窗口越大,曲线越平滑,但对浓度变化的响应也越迟钝。对于室内空气质量监测,浓度通常变化缓慢,我一般使用10到20的窗口。如果你要检测比如抽烟瞬间的浓度飙升,可能需要减小窗口到5左右,并配合其他算法(如中值滤波)来抗脉冲干扰。
ADC参考电压的选择也影响精度。Arduino Uno默认使用5V作为ADC参考,这意味着每个ADC步进值代表约4.9mV (5V/1024)。如果你使用3.3V供电的Arduino板(如某些ESP8266/ESP32开发板),并且传感器也能在3.3V下工作(需查手册),那么可以使用analogReference(EXTERNAL)或内部1.1V基准来获得更精细的电压分辨率,从而提高对小浓度变化的检测能力。
5. 数据校准与浓度换算实战
5.1 理解传感器输出特性与校准公式
GP2Y1010AU0F的输出电压与粉尘浓度之间的关系并非完美的线性,尤其是在高浓度区域可能会饱和。但在一定的浓度范围内(通常是0到几百微克/立方米),我们可以用一个线性模型来近似:浓度 = 灵敏度系数 × (测量电压 - 零点电压)。
这里有两个关键参数需要你通过实验来确定:
- 零点电压 (V0):这是传感器在“清洁空气”中输出的电压。注意,它通常不是0V。你需要找一个你认为空气干净的环境(比如开启空气净化器一段时间后的室内,或雨后室外),让传感器稳定运行几分钟,然后读取其平均电压值,这就是V0。
- 灵敏度系数 (K):这个系数表示每单位电压变化对应多少浓度。它是最需要校准的参数。你需要一个已知浓度的参考源。最专业的方法是用标准粉尘发生器,但这对于爱好者不现实。
一个可行的替代校准方法是使用“相对校准”或“交叉比对”。找一台你认为靠谱的、显示数字浓度的商用空气质量检测仪(例如小米、镭豆等,虽然它们也不一定是绝对准确,但作为相对参考是可行的)。将你的传感器和参考设备放在同一位置,避免通风直吹,同时记录一段时间内(例如几个小时)两者的读数。记录多组数据:你的传感器电压值(或计算出的原始浓度)和参考设备的浓度值。然后通过线性回归计算,得出最适合你手中这个传感器的K值。
5.2 实操校准过程与数据处理
假设你测得清洁空气下传感器平均电压为0.92V,这就是你的V0。 然后你进行交叉比对,获得如下一组数据(示例):
| 参考设备浓度 (ug/m3) | 你的传感器电压 (V) | 电压差值 (V - V0) |
|---|---|---|
| 15 | 1.02 | 0.10 |
| 35 | 1.22 | 0.30 |
| 80 | 1.72 | 0.80 |
| 120 | 2.32 | 1.40 |
你可以用Excel或任何编程工具进行线性拟合。以电压差值(V-V0)为X轴,参考浓度为Y轴。拟合出的直线斜率就是灵敏度系数K,截距理论上应为0(因为差值0对应浓度0)。
计算后,假设你得到公式:浓度 = 0.0857 * (电压 - 0.92)。那么这里的K=0.0857 ug/m3/mV(注意单位换算,如果电压单位是V,浓度单位是ug/m3,则K的单位是ug/m3/V)。
将这个公式写入你的Arduino程序:
float V0_calibrated = 0.92; float K_calibrated = 0.0857; float calculateDensity(float voltage) { float density = K_calibrated * (voltage - V0_calibrated); return (density > 0) ? density : 0; }重要提示:这个校准系数仅对你当前这个特定的传感器有效。即使是同一型号、同一批次的传感器,由于LED发光效率、光电晶体管灵敏度、内部结构微小差异等因素,其V0和K值都可能不同。因此,如果你要做多个传感器,或者更换了传感器,都需要重新校准。此外,温度、湿度也会对光学散射产生影响,对于要求高的应用,可能需要建立温湿度补偿模型,但这会复杂很多。对于一般室内监测,在温湿度相对稳定的环境下,上述线性校准已经能提供有参考价值的趋势性数据。
6. 项目集成与可视化应用
6.1 构建本地空气质量监测站
有了稳定的传感器读数和校准后的浓度数据,我们就可以将其集成到一个完整的系统中。一个简单的方案是使用Arduino Uno配合一个LCD显示屏(如1602 I2C屏)来制作一个桌面空气质量监测仪。
接线方面,在之前传感器电路的基础上,将LCD的SDA和SCL引脚分别连接到Arduino的A4和A5(Uno的I2C接口),VCC和GND接好。软件上,你需要包含LiquidCrystal_I2C库,并在循环中定期将计算出的粉尘浓度显示在屏幕上。
更进一步的,可以增加一个温湿度传感器(如DHT22或SHT31),同时监测PM2.5、温度和湿度。这些数据可以一起显示在LCD上,让你对环境有一个全面的了解。
#include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27, 16, 2); // 根据你的LCD地址修改 void setup() { lcd.init(); lcd.backlight(); lcd.print("Air Monitor"); } void loop() { float dust = readAndFilterDust(); // 你的粉尘读取过滤函数 // float temp = readTemperature(); // 读取温度 // float humi = readHumidity(); // 读取湿度 lcd.setCursor(0, 0); lcd.print("PM2.5:"); lcd.print(dust, 1); // 显示一位小数 lcd.print(" ug/m3"); // lcd.setCursor(0, 1); // lcd.print("T:"); // lcd.print(temp,1); // lcd.print("C H:"); // lcd.print(humi,0); // lcd.print("%"); delay(2000); // 每2秒更新一次 }6.2 数据上传与远程监控
如果你希望远程查看数据,或者记录历史数据进行分析,就需要将设备连接到网络。ESP8266(如NodeMCU)或ESP32是绝佳的选择,它们内置Wi-Fi,且兼容Arduino开发环境。
你可以将之前写好的传感器驱动代码移植到ESP平台上。然后,可以选择多种方式上传数据:
- MQTT协议:将数据发布到本地的Home Assistant、Node-RED或者云端的MQTT Broker(如EMQX Cloud)。这是一个轻量、实时的方案,非常适合智能家居集成。
- HTTP API:将数据以JSON格式POST到你自己的服务器、腾讯云/阿里云函数,或者直接发送到支持HTTP接入的物联网平台(如ThingsBoard、Blynk)。
- 直接入库:如果ESP设备连接了本地数据库(如InfluxDB),也可以直接写入。
这里给出一个使用ESP8266和MQTT的简单示例框架:
#include <ESP8266WiFi.h> #include <PubSubClient.h> const char* ssid = "你的WiFi"; const char* password = "你的密码"; const char* mqtt_server = "mqtt服务器地址"; WiFiClient espClient; PubSubClient client(espClient); void setup_wifi() { /* ... 连接WiFi ... */ } void reconnect() { /* ... 重连MQTT ... */ } void setup() { setup_wifi(); client.setServer(mqtt_server, 1883); // 初始化传感器引脚... } void loop() { if (!client.connected()) { reconnect(); } client.loop(); static unsigned long lastMsg = 0; if (millis() - lastMsg > 10000) { // 每10秒发送一次 lastMsg = millis(); float dust = readAndFilterDust(); char msg[50]; snprintf(msg, 50, "{\"pm25\":%.1f}", dust); client.publish("home/air_sensor/dust", msg); } }在服务器端,你可以用Node-RED订阅这个MQTT主题,将数据存储到数据库,并创建实时图表和报警规则(例如,当PM2.5超过75 ug/m³时,发送通知到手机)。
7. 常见问题、误差分析与优化技巧
7.1 典型问题排查清单
在实际制作和运行中,你几乎一定会遇到下面这些问题。这里我整理了一个快速排查表:
| 现象 | 可能原因 | 排查与解决方法 |
|---|---|---|
| 读数始终为0或接近0 | 1. 传感器Vo引脚未连接或接触不良。 2. I/O引脚控制逻辑反了(LED常灭)。 3. 负载电阻未接或开路。 4. 传感器已损坏。 | 1. 用万用表检查Vo引脚与ADC引脚间电压,在LED点亮瞬间应有变化。 2. 确认代码中 digitalWrite(dustSensorLED, LOW)是点亮LED(根据你的接线逻辑)。3. 检查150Ω负载电阻是否焊接牢固。 4. 更换传感器测试。 |
| 读数异常高且不变化 | 1. Vo引脚对VCC短路或受到强光直射。 2. ADC参考电压设置错误。 3. 零点电压V0设置过低。 | 1. 检查电路是否有短路,将传感器置于阴暗处测试。 2. 确认 analogRead()的参考电压与实际一致。3. 重新在清洁空气中测量并校准V0。 |
| 读数波动(跳动)非常大 | 1. 电源去耦不足(220μF电容未接或失效)。 2. 软件滤波未启用或窗口太小。 3. 环境气流剧烈(如正对风扇/空调)。 4. 电路板布局不佳,数字噪声干扰模拟信号。 | 1.首要检查:确保220μF电解电容正确并联在传感器VCC和GND之间。 2. 启用移动平均滤波,并增大滤波窗口(如20)。 3. 将传感器置于气流稳定的位置,可考虑加一个简单的防尘防气流罩。 4. 检查接线,尽量缩短模拟信号线,远离数字线路。 |
| 浓度计算值为负数 | 1. 当前环境电压低于校准的零点电压V0。 2. 校准环境不够干净,V0测得偏高。 | 1. 这是正常现象,说明空气比校准时更干净。在程序中应将负值钳位到0。 2. 在更洁净的环境(如开启净化器的密闭房间)重新测量V0。 |
| 对不同污染源响应不一致 | 传感器对不同成分、不同粒径颗粒的散射效率不同。 | 这是光学散射传感器的固有局限。它测量的是“等效浓度”。对于定性监测和趋势观察足够,如需精确化学成分分析,需用其他原理的传感器。 |
7.2 精度提升与长期稳定性维护
要获得更可靠的数据,除了基础的电路和校准,还需要注意以下几点:
1. 定期清洁与校准:传感器进气口和内部光学腔体长期使用后会积灰,严重影响精度。建议每3-6个月,用压缩气罐(或吹气球)轻轻吹扫传感器进气口。切勿用棉签伸入或液体清洗,会损坏光学元件。清洁后,应在稳定环境中重新测量零点电压V0。如果条件允许,每隔一年左右重新做一次交叉比对校准,更新K值。
2. 温湿度补偿探索:空气的温湿度会影响颗粒物的物理特性(如吸湿增长)以及光散射本身。如果你同时测量温湿度,可以观察浓度读数与温湿度的相关性。例如,在湿度极高时,水汽可能被误检为颗粒物,导致读数虚高。你可以尝试建立一个简单的补偿公式:浓度_修正 = 浓度_原始 / (1 + α * (RH - RH0)),其中α是一个经验系数,RH是当前湿度,RH0是参考湿度(如50%)。这需要你收集大量对比数据来拟合,对于非科研级应用,可以暂时记录这一现象,在解读高湿度下数据时保持谨慎。
3. 传感器预热与读数时机:传感器通电后,内部元件(特别是IRED)需要一段时间达到热稳定,输出才会稳定。建议在设备上电后,等待至少1-2分钟再开始记录有效数据。此外,避免在设备刚完成大功耗操作(如Wi-Fi发射)时立即读取ADC,因为电源可能略有波动。可以在读取传感器前加入一小段延时,或多次读取取平均。
4. 理解传感器的局限性:GP2Y1010AU0F这类低成本传感器,其设计目标是检测像香烟烟雾、室内扬尘这类相对较大的颗粒物(对PM2.5和PM10有响应,但对更小的PM1.0灵敏度很低)。它无法区分颗粒物的化学成分(是灰尘、烟尘还是水雾)。它的读数是一个“光学等效浓度”,与基于重量法的国标检测仪数据存在系统差异。因此,它最适合用于相对比较(比如这个房间和那个房间哪个更脏)、趋势监测(污染是上升还是下降)和事件检测(比如有人抽烟时浓度飙升),而不是获取绝对意义上的、法律认可的精确浓度值。认识到这一点,你就能更合理地利用它提供的数据。
最后,分享一个我个人的小技巧:为了直观判断传感器是否工作正常,我会在设备启动时,用嘴对着传感器轻轻吹一口气(注意不要带出口水)。一个功能正常的传感器,读数会在几秒内急剧上升,然后缓慢下降。这个“吹气测试”能快速验证整个数据链路的通畅性,从硬件驱动到软件计算再到显示或上传,非常实用。
