ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

基于Wio Terminal与PMS5003的物联网空气质量监测站DIY实战

基于Wio Terminal与PMS5003的物联网空气质量监测站DIY实战 1. 项目概述为什么选择Wio Terminal做空气质量监测最近几年空气质量成了大家日常关注的一个焦点无论是家里有老人小孩还是自己长期在室内工作都想知道自己呼吸的空气到底怎么样。市面上的空气质量检测仪不少但要么功能单一要么数据封闭要么价格不菲。作为一个喜欢动手折腾的硬件爱好者我一直在寻找一个既能实时查看、又能深度定制、还能把玩学习的方案。最终我把目光锁定在了Wio Terminal和PM2.5传感器的组合上。Wio Terminal 这块板子很有意思它集成了显示屏、按键、丰富的传感器接口和无线模块开箱即用几乎是为这种“显示交互联网”的物联网项目量身定做的。而PM传感器则是获取空气中悬浮颗粒物浓度的核心。这个项目的目标很明确搭建一个成本可控、数据可视、并且能让我完全掌控的实时空气质量监测站。它不仅仅是一个读数工具。通过Wio Terminal的屏幕我可以实时看到PM1.0、PM2.5、PM10的浓度数值和变化曲线通过内置的Wi-Fi我可以将数据上传到云端进行长期记录和分析甚至可以通过按键切换显示模式或者设置报警阈值。整个过程从硬件连接、软件编程到数据可视化涉及嵌入式开发、传感器通信和物联网应用是一个综合性很强的实战项目无论你是想了解环境监测还是学习物联网开发都能从中获得不少干货。2. 核心硬件选型与设计思路拆解做一个空气质量监测设备核心无外乎三部分负责计算和控制的主控、负责“感知”空气的传感器、以及负责“表达”信息的人机交互界面。我们的方案选择就是围绕这三点展开的。2.1 主控单元为什么是Wio Terminal市面上常见的主控板很多比如Arduino Uno、ESP32开发板等。选择Wio Terminal主要是因为它极大地简化了系统集成复杂度属于“高集成度方案”的典型代表。首先它自带一块2.4英寸的LCD彩色显示屏分辨率320x240。这意味着我们不需要再额外购买和连接显示屏省去了接线和驱动编写的麻烦。对于实时监测项目本地可视化是刚需自带屏幕的优势巨大。其次它集成了ESP32无线芯片支持Wi-Fi和蓝牙。这使得设备能够轻松接入网络实现数据上报、远程查看等功能让一个本地设备瞬间升级为物联网节点。如果使用普通的Arduino我们通常需要额外搭配一个ESP-01s之类的Wi-Fi模块接线和编程都会复杂一些。再者Wio Terminal提供了友好的用户交互硬件三个侧边按键、一个五向摇杆、甚至还有一个蜂鸣器。我们可以用这些硬件来实现模式切换、菜单操作、报警静音等功能交互体验直接拉满。最后其核心主控是ATSAMD51这是一颗基于Arm Cortex-M4F的微控制器性能足以流畅运行我们需要的传感器数据采集、屏幕刷新和网络通信任务。板载的Grove生态系统接口使得连接像PM传感器这样的外设变得异常简单只需一根四芯线即可完成电源、地和数据线的连接。注意虽然Wio Terminal功能强大但其ATSAMD51的Arduino核心库对内存的管理与常见的AVR或ESP32略有不同在编写涉及大量字符串处理或数组的程序时需要更留意内存使用情况避免堆栈溢出。2.2 感知核心PM传感器的选择与原理空气质量监测的核心指标之一是颗粒物浓度尤其是PM2.5直径小于等于2.5微米的颗粒物。常见的PM传感器主要分为两类基于激光散射原理和基于红外原理。前者精度高、响应快但价格也相对较高后者成本低但精度和响应速度一般。对于个人DIY项目我推荐使用攀藤PlantowerPMS5003系列传感器。它是一个非常成熟且广泛使用的激光散射式传感器。它通过内置风扇吸入空气用激光照射颗粒物通过检测散射光来推算不同粒径颗粒物的数量最终输出PM1.0、PM2.5、PM10的质量浓度值单位微克/立方米μg/m³。PMS5003通过UART串口通信输出的是结构化的数据帧解析起来非常方便。其精度在业内口碑很好足以满足家庭环境监测的需求。更重要的是它有标准的Grove接口版本可以直接插在Wio Terminal的任意一个Grove端口上实现“即插即用”。这里有一个关键点传感器需要预热和稳定。刚上电时风扇启动激光器稳定内部在进行自校准这段时间约30秒读出的数据是不准确的。在程序初始化时必须预留足够的预热时间并丢弃这段时间的数据。2.3 系统架构与数据流设计整个系统的运行逻辑可以清晰地用数据流来描述数据采集层Wio Terminal通过其硬件串口例如Serial1持续读取PMS5003传感器发送的数据包。程序需要按照传感器手册中定义的帧格式进行校验和解包提取出有效的PM值。数据处理与显示层解析出的PM值首先在本地进行处理。一方面驱动屏幕绘制实时数值、绘制历史趋势曲线、并根据预设的空气质量指数AQI标准转换颜色如优-绿色、良-黄色、污染-红色。另一方面处理用户通过按键或摇杆输入的指令切换显示页面或设置参数。数据上传层Wio Terminal内部的ESP32 Wi-Fi模块启动连接到家庭路由器。然后通过HTTP POST或MQTT协议将当前的PM数据、设备ID、时间戳等信息周期性地例如每60秒发送到指定的云服务平台或私有服务器。云端与远程层数据在云端被接收、存储并可通过Web仪表盘或手机App进行远程查看和历史数据分析。这一步实现了从“本地监测”到“物联网监测”的跨越。这个架构的优势在于层次分明各司其职。Wio Terminal作为边缘设备负责实时性要求最高的采集和显示云端负责海量数据的存储和复杂分析。即使网络暂时中断本地监测功能依然完好保证了核心功能的可靠性。3. 硬件连接与软件环境搭建理论清晰了接下来就是动手环节。硬件连接极其简单这也是Grove生态的魅力所在。3.1 硬件连接步骤准备部件Wio Terminal一台攀藤PMS5003传感器Grove接口版一个USB Type-C数据线一根。连接传感器将PMS5003传感器的Grove连接线插入Wio Terminal侧面的任意一个Grove端口例如标有UART的那个。这里需要注意PMS5003是串口设备虽然Wio Terminal的Grove端口大多支持多种协议I2C、模拟、数字但只有硬件串口对应的那个端口才能直接使用。通常端口UART对应的是Serial1。供电使用USB线将Wio Terminal连接到电脑或一个5V/2A的USB充电器上。PMS5003的电力将由Wio Terminal通过Grove接口提供。务必确保电源有足够的能力因为激光传感器和风扇在启动瞬间电流可能较大。连接完成后硬件部分就准备好了。你可能会注意到PMS5003传感器上有一个小风扇开始转动并听到轻微的激光器启动声这是正常现象。3.2 软件开发环境配置我们使用Arduino IDE进行开发因为它对Wio Terminal和ESP32的支持都很好库生态丰富。安装Arduino IDE从Arduino官网下载并安装最新版本的IDE。添加Wio Terminal板支持打开Arduino IDE进入“文件”-“首选项”在“附加开发板管理器网址”中填入https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json然后打开“工具”-“开发板”-“开发板管理器”搜索“Wio Terminal”找到并安装“Seeed SAMD Boards”。安装必要的库TFT_eSPI这是驱动Wio Terminal屏幕的核心图形库。安装后需要根据Wio Terminal的引脚定义进行配置。通常Seeed已经提供了预配置好的文件。PubSubClient如果需要使用MQTT协议上传数据这个库是必备的。ArduinoJson用于处理JSON格式的数据无论是设备配置还是与云端通信都非常有用。选择开发板和端口在“工具”菜单下选择开发板为“Seeed Wio Terminal”并选择正确的串口端口。实操心得在首次使用Wio Terminal时可能会遇到电脑无法识别端口的情况。此时需要安装Wio Terminal的USB驱动CDC驱动通常在Seeed的Wiki页面可以找到。安装驱动后设备管理器里会显示为一个串行端口。4. 核心代码解析与实现一切就绪我们来编写核心程序。程序主要分为四个模块传感器驱动、屏幕显示、网络连接、主循环调度。4.1 PMS5003传感器数据读取与解析PMS5003的输出是固定的32字节数据帧。我们需要编写一个可靠的解析函数。// 定义PMS5003数据结构体 struct PMS_DATA { uint16_t framelen; uint16_t pm10_standard, pm25_standard, pm100_standard; uint16_t pm10_env, pm25_env, pm100_env; uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um; uint16_t unused; uint16_t checksum; }; PMS_DATA data; bool readPMSdata(Stream *s) { if (!s-available()) { return false; } // 寻找数据帧头0x42, 0x4d if (s-peek() ! 0x42) { s-read(); return false; } if (s-available() 32) { // 确保有足够的数据 return false; } uint8_t buffer[32]; uint16_t sum 0; s-readBytes(buffer, 32); // 验证帧头 if (buffer[0] ! 0x42 || buffer[1] ! 0x4d) { return false; } // 计算校验和前30字节的和 for (uint8_t i 0; i 30; i) { sum buffer[i]; } uint16_t frameChecksum (buffer[30] 8) buffer[31]; if (sum ! frameChecksum) { return false; // 校验失败丢弃该帧 } // 将数据填充到结构体 data.framelen (buffer[2] 8) buffer[3]; data.pm10_standard (buffer[4] 8) buffer[5]; data.pm25_standard (buffer[6] 8) buffer[7]; data.pm100_standard (buffer[8] 8) buffer[9]; // ... 填充其他字段 return true; }在主循环中我们以非阻塞的方式调用这个函数void loop() { if (readPMSdata(Serial1)) { // Serial1是连接传感器的串口 // 成功读取到一帧有效数据 currentPM25 data.pm25_standard; // 使用标准环境下PM2.5浓度 // ... 更新显示和数据上传逻辑 } // ... 其他任务 }注意事项串口读取一定要做好帧同步和校验。PMS5003会持续发送数据如果程序因为其他任务阻塞可能导致串口缓冲区积累多帧数据造成解析错乱。上述代码通过peek()和校验和双重保障能有效提高数据可靠性。另外pm25_standard和pm25_env分别对应标准环境和大气环境下的浓度通常使用standard值进行空气质量评价。4.2 屏幕UI设计与实现利用TFT_eSPI库我们可以绘制丰富的界面。一个典型的界面可以包含大字体实时数值显示居中显示当前的PM2.5数值字体颜色根据AQI等级变化。趋势曲线图在屏幕下方绘制一个简单的折线图显示最近几十个时间点的PM2.5变化趋势。多参数显示在角落用小字体显示PM1.0和PM10的数值。状态栏顶部显示Wi-Fi连接状态、当前时间、电池电量如果外接了电池等信息。#include TFT_eSPI.h TFT_eSPI tft TFT_eSPI(); void setupScreen() { tft.init(); tft.setRotation(3); // 根据你的摆放方向调整 tft.fillScreen(TFT_BLACK); } void updateDisplay(float pm25) { tft.fillRect(50, 50, 220, 80, TFT_BLACK); // 清除之前数值区域 // 根据PM2.5值决定颜色 uint16_t color TFT_GREEN; if (pm25 75) color TFT_YELLOW; if (pm25 115) color TFT_RED; tft.setTextColor(color, TFT_BLACK); tft.setTextDatum(MC_DATUM); // 居中对齐 tft.drawString(String(pm25, 1), tft.width()/2, tft.height()/2 - 20, 7); // 大字体显示 // 绘制趋势图简化示例将数值映射到坐标 static int xPos 0; int yPos map(constrain(pm25, 0, 300), 0, 300, tft.height()-30, 30); tft.drawPixel(xPos, yPos, TFT_WHITE); xPos; if (xPos tft.width()) { tft.drawFastHLine(0, tft.height()-30, tft.width(), TFT_DARKGREY); // 清空趋势图区域 xPos 0; } }实操心得频繁刷新全屏会导致屏幕闪烁。优化策略是只刷新需要改变的区域比如数值区域和趋势图的点。fillRect清除旧内容再绘制新内容是局部刷新的常用方法。另外TFT_eSPI库功能强大但稍显复杂初次使用建议从官方示例开始理解其坐标系统和绘图函数。4.3 网络连接与数据上传Wio Terminal的Wi-Fi功能通过其内部的ESP32协处理器实现通信方式为AT指令。幸运的是Seeed提供了封装好的rpcWiFi库让我们可以像使用普通ESP32一样操作Wi-Fi。#include rpcWiFi.h #include HTTPClient.h // 用于HTTP上传 // 或 #include PubSubClient.h // 用于MQTT上传 const char* ssid 你的Wi-Fi名称; const char* password 你的Wi-Fi密码; const char* serverUrl http://你的服务器地址/api/data; // HTTP示例 void connectToWiFi() { WiFi.begin(ssid, password); tft.print(Connecting to WiFi); while (WiFi.status() ! WL_CONNECTED) { delay(500); tft.print(.); } tft.println(\nConnected! IP: ); tft.println(WiFi.localIP()); } void uploadData(float pm25, float pm10, float pm100) { if (WiFi.status() WL_CONNECTED) { HTTPClient http; http.begin(serverUrl); http.addHeader(Content-Type, application/json); // 构建JSON数据 String jsonPayload {\device_id\:\wio_terminal_01\,; jsonPayload \pm25\: String(pm25, 1) ,; jsonPayload \pm10\: String(pm10, 1) ,; jsonPayload \pm100\: String(pm100, 1) }; int httpResponseCode http.POST(jsonPayload); if (httpResponseCode 0) { // 上传成功 } else { // 上传失败记录错误 } http.end(); } }在主循环中可以设置一个定时器每60秒调用一次uploadData函数避免过于频繁的请求给服务器造成压力。HTTP vs MQTT对于简单的数据上报HTTP POST足够用。但如果设备数量多或者需要服务器向设备下发指令如远程修改采样频率MQTT是更专业的物联网协议它更轻量支持双向通信。PubSubClient库可以帮你实现MQTT客户端。4.4 主程序逻辑与多任务处理我们需要将数据采集、显示刷新、网络上传、用户按键检测等多个任务协调好。由于没有使用实时操作系统RTOS我们采用基于状态机和非阻塞延时的时间片轮询方式。unsigned long lastSensorReadTime 0; const long sensorReadInterval 1000; // 1秒读一次传感器 unsigned long lastUploadTime 0; const long uploadInterval 60000; // 60秒上传一次 unsigned long lastDisplayUpdateTime 0; const long displayUpdateInterval 500; // 0.5秒刷新一次显示 void loop() { unsigned long currentMillis millis(); // 任务1定时读取传感器 if (currentMillis - lastSensorReadTime sensorReadInterval) { lastSensorReadTime currentMillis; if (readPMSdata(Serial1)) { // 更新当前数据变量 } } // 任务2定时刷新显示 if (currentMillis - lastDisplayUpdateTime displayUpdateInterval) { lastDisplayUpdateTime currentMillis; updateDisplay(currentPM25); } // 任务3定时上传数据 if (currentMillis - lastUploadTime uploadInterval) { lastUploadTime currentMillis; uploadData(currentPM25, currentPM10, currentPM100); } // 任务4检测用户按键非阻塞 checkButtonPress(); }这种模式确保了每个任务都能在预期的时间间隔内得到执行不会因为某个耗时任务如网络请求而阻塞其他关键任务如传感器读取保证了系统的实时性和响应性。5. 进阶功能与优化思路基础功能实现后我们可以考虑增加一些提升体验和可靠性的功能。5.1 本地数据缓存与断网续传网络不稳定是物联网设备的常态。我们可以为Wio Terminal增加一个SD卡模块或者利用其有限的内部存储在发送失败时将数据暂时缓存起来。#include Seeed_FS.h #include SD/Seeed_SD.h File dataFile; void saveDataToSD(float pm25) { dataFile SD.open(datalog.txt, FILE_APPEND); if (dataFile) { dataFile.print(millis()); dataFile.print(,); dataFile.println(pm25); dataFile.close(); } } void uploadCachedData() { // 在网络恢复后读取SD卡中的文件尝试重新上传历史数据 }当网络恢复时程序可以检查SD卡中是否有未上传的数据并进行补传。这保证了数据的完整性不会因为短暂的网络波动而丢失记录。5.2 空气质量指数AQI计算与可视化直接显示PM2.5浓度μg/m³对大众来说不够直观。我们可以根据国家或国际标准例如中国的HJ 633-2012标准或美国的AQI标准将PM2.5浓度转换为0-500的AQI指数和对应的等级优、良、轻度污染等。int calculateAQI_CN(float pm25) { // 中国标准PM2.5 24小时平均浓度限值 float bpHi[7] {0, 35, 75, 115, 150, 250, 500}; float bpLo[7] {0, 0, 36, 76, 116, 151, 251}; int iAQIHi[7] {0, 50, 100, 150, 200, 300, 500}; int iAQILo[7] {0, 0, 51, 101, 151, 201, 301}; int i; for (i 0; i 7; i) { if (pm25 bpLo[i] pm25 bpHi[i]) { break; } } if (i 7) return 500; // 超出范围 float aqi ((iAQIHi[i] - iAQILo[i]) / (bpHi[i] - bpLo[i])) * (pm25 - bpLo[i]) iAQILo[i]; return (int)aqi; } String getAQILevel(int aqi) { if (aqi 50) return 优; else if (aqi 100) return 良; else if (aqi 150) return 轻度污染; else if (aqi 200) return 中度污染; else if (aqi 300) return 重度污染; else return 严重污染; }在屏幕上不仅显示数字还可以用getAQILevel返回的文本和对应的颜色绿、黄、橙、红、紫、褐来渲染整个背景或关键区域视觉警示效果更佳。5.3 低功耗设计与电池供电如果想做成一个完全便携、无需一直插电的设备就需要考虑功耗。Wio Terminal的屏幕和ESP32 Wi-Fi模块是耗电大户。屏幕优化在不需查看时可以调低屏幕亮度甚至完全关闭背光。可以设置一个按键或通过人体感应传感器来唤醒屏幕。传感器采样策略PMS5003传感器本身功耗不低。可以改为间歇性工作模式例如每5分钟启动传感器工作30秒进行采样其余时间让其进入休眠。这需要控制其ENABLE引脚。Wi-Fi策略仅在需要上传数据时连接Wi-Fi上传完成后立即断开。可以将上传间隔设置得更长比如10分钟一次。MCU休眠让ATSAMD51主控在空闲时进入低功耗睡眠模式通过定时器中断唤醒。这需要更深入的编程涉及配置实时时钟RTC和中断处理。通过这些优化配合一个容量合适的锂电池如18650电池充放电保护板可以让设备持续工作数天甚至数周。6. 常见问题与排查技巧实录在实际制作和调试过程中你几乎一定会遇到下面这些问题。我把它们和解决方法整理出来希望能帮你少走弯路。6.1 传感器读数异常全为零、数值固定或巨大现象屏幕上显示的PM值始终为0或者是一个不变的固定值或者是一个不合理的巨大数值如65535。排查步骤检查硬件连接确认Grove线已插紧并且插在了Wio Terminal的UART端口上通常是左侧或底部标有UART/Serial1的接口。检查电源确保USB电源能提供足够电流建议5V/2A。电量不足的充电宝或电脑USB口可能无法驱动传感器稳定工作表现为风扇转动无力或激光器不启动。尝试更换电源。检查串口配置在代码中确认你使用正确的串口对象读取数据。连接Grove UART口的一般是Serial1波特率通常是9600或9600。确保初始化正确Serial1.begin(9600);。检查数据解析在readPMSdata函数中增加调试输出将接收到的原始字节以十六进制形式打印出来SerialUSB.println(buffer[i], HEX);。对照PMS5003数据手册检查帧头0x42, 0x4d是否正确校验和计算是否匹配。最常见的错误是校验和计算不对导致有效数据帧被丢弃。预热等待传感器上电后需要30秒左右的稳定时间。在setup()函数中读取传感器数据前先添加delay(30000);并在这期间丢弃所有读取到的数据。6.2 Wi-Fi无法连接或频繁断开现象设备启动后长时间卡在“Connecting to WiFi...”或者连接成功后不久就断开。排查步骤检查凭证再三检查代码中的ssid和password是否正确注意大小写。信号强度将设备靠近路由器排除信号弱的问题。Wio Terminal的天线性能一般隔墙后信号衰减可能较大。路由器设置有些路由器可能设置了MAC地址过滤或仅允许特定设备连接。检查路由器后台设置。尝试将手机热点作为Wi-Fi源测试设备能否连接以排除路由器兼容性问题。库与固件确保使用了最新版本的rpcWiFi库。早期版本的库可能存在稳定性问题。也可以尝试在连接Wi-Fi的代码中加入重试逻辑连接失败后等待几秒再重试。电源干扰劣质USB线或电源适配器可能引入噪声影响Wi-Fi模块正常工作。换用质量好的电源和短线试试。6.3 屏幕显示异常花屏、不显示、闪烁现象屏幕白屏、花屏、显示内容错乱或者刷新时严重闪烁。排查步骤库配置TFT_eSPI库需要正确的引脚配置。对于Wio TerminalSeeed通常提供了现成的配置文件。检查Arduino安装目录下的libraries/TFT_eSPI/User_Setups/文件夹是否有Setup206_Wio_Terminal.h之类的文件并确保在User_Setup_Select.h中取消了该文件的注释。内存不足在屏幕上绘制大量图形或使用大字体时可能会耗尽内存。使用SerialUSB.println(freeMemory());来打印剩余内存。优化策略包括使用SPIFFS加载图片而非存储在内存中减少同时使用的字体数量避免在循环中动态创建大的字符串或对象。刷新优化全屏刷新tft.fillScreen会导致闪烁。坚持使用局部刷新fillRect清除旧区域再绘制。确保你的updateDisplay函数只重绘变化的部分。初始化顺序确保在setup()中先初始化屏幕tft.init()再进行其他耗时的操作如连接Wi-Fi。否则屏幕驱动可能初始化不正常。6.4 数据上传服务器失败现象设备Wi-Fi已连接但HTTP POST请求总是返回错误代码如-1、-4。排查步骤网络可达性首先在电脑上使用浏览器或Postman等工具测试你的服务器API地址是否可以正常访问。确保URL正确没有防火墙阻挡。HTTPS问题如果你的服务器使用HTTPSArduino的HTTPClient可能需要处理证书。对于测试可以暂时改用HTTP或者使用WiFiClientSecure并设置忽略证书验证不推荐用于生产环境。服务器响应在代码中打印出httpResponseCode和服务器返回的内容http.getString()。常见的错误如400是请求格式错误检查JSON格式401是未授权检查API密钥或Token404是地址错误500是服务器内部错误。内存泄漏确保每次HTTP请求后都调用http.end()来释放资源。否则多次请求后可能导致内存耗尽。连接超时网络状况差时可以适当增加超时时间http.setTimeout(10000); // 10秒超时。6.5 设备运行不稳定偶尔死机现象设备运行一段时间后屏幕卡住按键无响应仿佛程序“死”了。排查步骤看门狗定时器这是解决死机问题的利器。Arduino for SAMD核心内置了看门狗WDT。在setup()开头启用它watchdog.enable(8000); // 8秒超时。在loop()中定期喂狗watchdog.reset();。如果程序跑飞或陷入死循环超过8秒未喂狗看门狗会自动重启整个系统。堆栈溢出这是嵌入式系统常见的崩溃原因。避免在函数内定义过大的局部数组如char buffer[1024]尽量使用全局变量或动态分配并注意释放。使用SerialUSB.println(ESP.getFreeHeap());对于ESP32部分或专门的内存分析工具来监控内存使用。中断冲突如果你使用了外部中断确保中断服务程序ISR尽可能短小不要在里面做复杂的操作或调用可能阻塞的函数如delay,Serial.print。电源稳定性用万用表测量一下Wio Terminal的5V输入引脚在传感器风扇启动的瞬间电压是否有大幅跌落。如果跌落严重说明电源带载能力不足必须更换。电压不稳是导致MCU意外复位的常见原因。这个项目从硬件连接到软件调试再到功能扩展几乎涵盖了物联网设备开发的完整流程。我最深的体会是硬件项目成功的关键往往不在于最复杂的代码而在于对细节的把握一个接触不良的接口、一个错误的波特率、一个没考虑到的电源峰值都可能导致整个系统行为异常。耐心地、系统地排查并善用Serial.print进行调试是解决问题的唯一捷径。当你看到屏幕上跳动的数字终于真实地反映出窗外空气的变化并将这些数据成功发送到自己的服务器上时那种成就感是单纯购买一个成品设备无法比拟的。
返回列表