拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于RP2040与W5100S的嵌入式以太网人体检测系统设计与实践

1. 项目缘起为什么选择在RP2040上做以太网人体检测最近在捣鼓一些边缘AI的小玩意儿发现一个挺有意思的现象很多朋友一提到在微控制器上跑AI模型第一反应就是上ESP32或者更高端的树莓派然后通过Wi-Fi联网。这当然没问题但对于一些特定的工业或嵌入式场景比如固定位置的安防监控、产线工位状态监测或者对网络稳定性和延迟有更高要求的应用有线以太网其实是个更“稳”的选择。正好手头有几片RP2040和W5100S以太网模块我就琢磨着能不能用这套“复古”但可靠的组合实现一个实时的人体检测器毕竟RP2040的双核M0和264KB的SRAM跑个小型的TensorFlow Lite for Microcontrollers模型理论上是有戏的。这个项目的核心目标很明确打造一个低成本、低功耗、基于有线网络的“智能感知节点”。它不依赖云端强大的算力而是在设备端Edge完成最基本的人体识别任务然后仅仅通过以太网将结构化的检测结果比如“检测到人”、“坐标位置”、“置信度”发送出去。这样做的好处太多了响应速度快本地推理、网络稳定可靠有线连接、数据隐私性好原始图像不上传、整体系统成本低。对于智能家居的入侵检测、办公室的节能照明控制、或者小型仓库的区域安防这种方案都很有吸引力。2. 核心硬件选型RP2040与W5100S的“黄金搭档”为什么是RP2040和W5100S这可不是随便抓两个芯片就来用的。每一件硬件选型的背后都是一系列需求和约束平衡的结果。2.1 主控RP2040性价比与灵活性的平衡Raspberry Pi Pico的核心RP2040是一颗非常独特的微控制器。它的双Arm Cortex-M0内核主频最高133MHz对于微控制器级别的AI推理来说这个算力是够用的尤其是运行经过深度优化和裁剪的TFLite Micro模型。更关键的是它拥有264KB的片上SRAM。在MCU上跑AI内存往往是比算力更紧俏的资源。模型权重、中间激活值activations都需要放在RAM里264KB的容量让我们有机会部署一个轻量级的目标检测模型比如基于MobileNetV1或V2骨干网络的人体检测模型。RP2040的另一个优势是其可编程I/OPIO。虽然在这个项目中我们主要用SPI与W5100S通信但PIO的存在为未来扩展其他传感器或执行器提供了极大的灵活性。比如你可以用PIO来驱动一个自定义协议的显示屏或者高效地读取一组数字传感器而几乎不占用CPU资源。2.2 网络芯片W5100S稳定可靠的以太网接口网络部分我放弃了更常见的ENC28J60而选择了Wiznet的W5100S。这是一个硬件集成的TCP/IP协议栈芯片。它的最大优点就是“省心”。芯片内部集成了TCP、UDP、ICMP、IPv4等协议的处理硬件这意味着主控MCURP2040不需要耗费大量的CPU周期去处理繁琐的网络协议包解析与封装只需要通过SPI接口读写数据即可。对于资源紧张的RP2040来说这相当于把网络通信这个“重活”外包了自己可以更专注于AI推理这个核心任务。W5100S支持10/100Mbps自适应的以太网内置32KB的收发缓冲区并且有4个独立的硬件Socket。在我们的项目里我们可以用一个Socket工作在UDP模式持续向指定的服务器IP和端口发送检测结果或者用另一个Socket工作在TCP服务器模式等待上位机的连接和查询。这种硬件级的协议支持带来了极高的通信稳定性和极低的CPU开销特别适合需要7x24小时不间断运行的嵌入式设备。注意W5100S需要外接一个网络变压器Magnetics和RJ45接口才能连接到以太网。市面上有集成了这两者的模块购买时请认准“带网络变压器”的版本否则无法直接插网线。2.3 视觉传感器OV2640与它的挑战人体检测需要图像输入。最直接的方案是使用摄像头模块比如常见的OV2640。它输出JPEG或RGB565格式的图像通过DVP并行接口或SCCB类似I2C进行配置。RP2040有足够的IO口来对接DVP接口但这里会遇到一个巨大的挑战图像缓冲。OV2640输出一帧QVGA320x240的RGB565图像需要3202402 153,600字节即约150KB的数据。这已经超过了RP2040单片SRAM的一半。如果直接存一整帧图像留给AI模型运行的内存就所剩无几了很可能导致程序崩溃。因此在实际实现中我们通常采用一种“流式处理”或“降低分辨率”的策略硬件缩放与裁剪充分利用OV2640内部的DSP通过SCCB总线将其输出分辨率设置为模型所需的输入尺寸例如96x96或160x160并设置为输出灰度图YUV格式的Y分量或低位深的RGB格式。这能直接从源头减少数据量。双核协作与PIO一个RP2040内核Core0专门负责通过PIO高速读取DVP接口的数据并实时进行简单的预处理比如裁剪、均值降采样然后存入一个循环缓冲区。另一个内核Core1则从缓冲区中取出够一帧模型输入的数据例如96x96的灰度图约9KB立刻进行推理。这需要精细的线程同步和内存管理。使用更轻量的传感器如果检测目标对颜色不敏感可以考虑使用纯灰度传感器或者寻找输出数据格式更简单的模块。在本次原型中为了简化问题我选择了第三种思路的变种使用现成的数字输出型人体感应模块如HC-SR501作为触发信号然后用一个低分辨率的模拟摄像头或甚至一组红外对管来提供粗略的“图像”信息。但这严格来说已经不是视觉检测了。为了忠于“Person detector”的标题下面的讨论我们还是基于OV2640这类真实摄像头并直面内存挑战来展开方案设计。3. 软件架构TFLM模型部署与网络通信的融合整个项目的软件栈可以分成相对独立的三层硬件驱动层、AI推理层、网络应用层。清晰的层次划分能让开发和调试更顺利。3.1 TensorFlow Lite Micro的移植与裁剪TFLM是整个项目的AI引擎。它的移植是第一步也是考验耐心的一步。第一步获取TFLM库。我们不直接使用完整的TensorFlow仓库而是从其GitHub仓库中提取tensorflow/lite/micro这个目录。这个目录包含了所有核心文件、参考实现reference kernels以及一些示例。我们需要的就是这个精简的结构。第二步适配RP2040平台。TFLM设计了一套“移植层”Porting Layer我们需要为RP2040实现几个关键函数内存管理实现tflite::MicroAllocator所需的接口或者更简单点直接提供一个静态的数组作为“Tensor Arena”。这个Arena的大小至关重要它需要容纳模型权重、所有中间张量tensors以及TFLM运行时本身所需的内存。对于一个96x96x1输入的人体检测模型这个Arena通常需要120KB到180KB。我们必须从RP2040那宝贵的264KB SRAM中划出这么一大块。// 在内存中开辟Tensor Arena constexpr int kTensorArenaSize 150 * 1024; // 150KB alignas(16) static uint8_t tensor_arena[kTensorArenaSize];调试输出实现MicroPrintf函数将其重定向到RP2040的UART这样我们就能通过串口看到TFLM的调试信息和错误日志。时间函数实现一个返回毫秒或微秒计数的函数用于基准测试。第三步模型选择与转换。我们不可能在RP2040上跑YOLO。合适的选择是使用TensorFlow Hub或Model Zoo中的轻量级图像分类模型如MobileNetV1 0.25深度乘数、96x96输入但将其重新训练为“人”与“非人”的二分类模型。这只能判断“图里有没有人”没有位置信息。使用面向移动端的单发检测器SSD架构搭配MobileNet骨干网并大幅减少先验框anchor boxes的数量和输出维度裁剪出一个超微型的目标检测模型。这能得到粗略的边界框。模型训练好后需要转换为TFLite格式.tflite然后使用xxd或类似的工具将其转换为C语言数组嵌入到固件中。# 将.tflite模型转换为C数组 xxd -i person_detect_model.tflite model_data.cc生成的model_data.cc文件里就包含了像unsigned char person_detect_model_tflite[]这样的数组定义。第四步编写推理循环。在RP2040的主循环中我们需要从摄像头获取一帧图像数据并预处理成模型需要的格式例如96x96的灰度图数据归一化到[-1, 1]或[0, 1]区间。将预处理后的数据拷贝到TFLM输入张量的数据指针中。调用TfLiteMicroInterpreter::Invoke()执行推理。从输出张量中解析结果。对于二分类可能是一个sigmoid激活后的概率值对于微型检测器可能是一组[y_min, x_min, y_max, x_max, confidence, class]。3.2 W5100S的驱动与Socket管理W5100S通过SPI与RP2040通信。我们需要实现其底层寄存器读写函数然后基于官方库或自行封装Socket API。初始化流程至关重要硬件SPI初始化配置RP2040的SPI接口为主机模式时钟频率建议在10-20MHz之间。连接好片选CS、时钟SCK、主机输出从机输入MOSI、主机输入从机输出MISO四根线。W5100S复位通过一个GPIO控制W5100S的复位引脚进行一次硬件复位确保芯片处于已知状态。配置网络参数通过SPI写入W5100S的模式寄存器MR、网关地址GAR、子网掩码SUBR、物理地址SHAR即MAC地址、源IP地址SIPR。这些必须在开启任何Socket之前设置好。// 伪代码示例设置IP地址 w5100s_write_reg(SIPR0, 192); // IP地址第一部分 w5100s_write_reg(SIPR1, 168); w5100s_write_reg(SIPR2, 1); w5100s_write_reg(SIPR3, 100); // 设备IP设为192.168.1.100配置Socket选择一个Socket例如Socket 0设置其模式寄存器Sn_MR为UDP或TCP并设置目标端口号Sn_PORT。如果是TCP客户端还需要设置目标IP地址Sn_DIPR和端口Sn_DPORT。打开Socket发送Socket打开命令Sn_CR OPEN。数据收发W5100S的数据收发通过其内部的发送/接收缓冲区进行。我们需要操作两个指针写指针和读指针。发送将数据写入Socket的发送缓冲区然后更新发送写指针Sn_TX_WR最后发送发送命令Sn_CR SEND。接收轮询或中断检查接收数据大小Sn_RX_RSR。如果有数据从接收读指针Sn_RX_RD处读取数据然后更新读指针。一个常见的优化是将网络数据收发的任务放在一个固定的核心如Core0上而将AI推理放在另一个核心Core1上。两者通过队列例如RP2040 SDK中的queue_t传递检测结果。这样即使网络偶尔有延迟也不会阻塞推理的进行。3.3 应用层协议设计简单高效的JSON over UDP为了将检测结果发送到服务器或上位机我们需要定义一个简单的应用层协议。追求极简和实时性我推荐使用UDP协议并采用JSON格式封装数据。虽然JSON解析在MCU上有点负担但对于发送端我们的设备来说仅仅是将几个变量格式化成字符串开销是可接受的。接收端通常是PC或服务器解析JSON则非常方便。一个典型的数据包可能看起来像这样{ dev_id: rp2040_node_01, timestamp: 1678881234567, detection: { present: true, confidence: 0.92, bbox: [0.2, 0.3, 0.7, 0.8] // [ymin, xmin, ymax, xmax] 归一化坐标 } }在RP2040上我们可以使用一个轻量级的库如jsmn或手写简单的格式化函数来生成这个JSON字符串。然后通过W5100S的UDP Socket发送到指定的服务器IP和端口。提示UDP是不可靠的但在局域网内丢包率极低。如果项目要求绝对可靠可以改用TCP但需要处理连接建立、维护和断线重连的逻辑复杂度会上升。一个折中方案是在应用层实现简单的序列号和确认重传机制。4. 系统集成与优化实战让整个系统跑起来把各个模块的代码堆在一起只是开始让它们稳定、高效地协同工作才是真正的挑战。4.1 内存的精细规划与分配RP2040的264KB SRAM是共享内存必须精打细算。我们需要一个清晰的内存布局图Tensor Arena分配150KB。这是最大的一块用于TFLM运行时和模型张量。图像缓冲区分配约20KB。用于存放从摄像头读取并预处理后的模型输入图像例如96x96的灰度图约9KB再加上一些行缓冲或临时空间。网络数据缓冲区W5100S的Socket缓冲区是芯片内部的不占用RP2040的SRAM。但我们需要在RP2040端为待发送的JSON数据包准备一个缓冲区2KB通常足够。栈空间每个核心的栈需要预留足够空间例如各4KB用于函数调用和局部变量。全局变量与堆剩下的内存留给全局变量、动态分配如果使用和FreeRTOS内核对象如果使用RTOS。可以使用链接脚本.ld文件来精确控制这些区域的地址但更简单的方法是直接在代码中定义大型数组并确保它们被分配到正确的内存段比如.bss。务必使用sizeof()来检查实际占用并留出至少10%的余量以防溢出。4.2 双核任务划分与同步利用RP2040的双核特性是提升性能的关键Core 0 (主核)初始化所有硬件SPI、UART、PIO等。负责摄像头数据的读取。如果使用PIO驱动DVP这部分工作可以高效且不占用太多CPU。负责图像预处理缩放、裁剪、归一化。这部分计算量不大可以在Core0完成。将预处理后的图像数据放入一个“帧缓冲区”队列。同时Core0也负责网络通信的主循环检查发送缓冲区如果有待发送的检测结果就通过W5100S发送出去。Core 1 (从核)从“帧缓冲区”队列中获取图像数据。执行TFLM模型推理。这是一个相对耗时的过程可能会阻塞几十到几百毫秒。解析推理结果生成结构化的检测数据如JSON字符串。将检测结果放入一个“结果队列”。两个核心通过队列进行通信。RP2040的SDK提供了queue_t数据结构它是线程安全的通过互斥锁或原子操作实现。确保队列有足够的深度例如帧缓冲区队列深度为2结果队列深度为10以避免生产者和消费者速度不匹配导致的数据丢失或阻塞。4.3 性能瓶颈分析与调优系统集成后要用逻辑分析仪、串口打印时间戳等方式来测量性能瓶颈。瓶颈1图像采集与预处理。如果使用软件模拟DVP读取会非常慢。解决方案必须使用PIO可编程I/O来硬件实现DVP时序这能将读取一帧的时间从毫秒级降低到微秒级。瓶颈2模型推理时间。这是最主要的耗时环节。解决方案模型量化使用TFLite的整数量化Integer Quantization将模型从FP32转换为INT8。这通常能带来2-4倍的推理速度提升并显著减少模型体积和内存占用。使用CMSIS-NN库如果TFLM为RP2040的Cortex-M内核移植了CMSIS-NN内核优化确保在编译时启用它。这能利用ARM架构的特殊指令加速卷积等操作。降低输入分辨率将模型输入从96x96降到64x64能平方级地减少计算量。瓶颈3内存带宽。频繁地在大块内存如图像缓冲区、Tensor Arena间拷贝数据也会消耗时间。解决方案尽量让数据“原地”处理。例如让摄像头PIO程序直接将数据写入Tensor Arena中输入张量对应的区域避免一次额外的memcpy。4.4 电源管理与稳定性考量这是一个常被忽视但至关重要的点。我们的设备可能需要长期通电。RP2040的功耗在133MHz全速运行下RP2040的功耗不低。如果检测频率要求不高比如每秒1次可以在两次推理之间让RP2040进入休眠模式sleep_ms()或者降低核心频率。W5100S的功耗W5100S在工作时也有一定功耗。如果网络通信不是持续的可以考虑在空闲时关闭其PHY物理层或进入低功耗模式。摄像头的功耗OV2640的功耗较大。可以通过GPIO控制其电源引脚在不进行检测时彻底断电。看门狗务必启用RP2040的内部看门狗WDT并定期喂狗。防止程序跑飞导致设备死机这对于无人值守的设备是必须的。网络断线重连如果使用TCP必须实现心跳机制和断线重连逻辑。对于UDP虽然无需连接但最好也能定期发送一个“存活”包让服务器知道设备在线。5. 开发与调试中的“坑”与应对策略这个项目从原型到稳定我踩过不少坑这里分享几个最有代表性的。5.1 Tensor Arena不足导致的诡异崩溃现象模型推理有时成功有时会硬故障HardFault错误地址看起来是随机的。排查首先检查栈是否溢出增加了栈大小后问题依旧。然后使用TFLM的MicroPrintf输出调试信息发现每次崩溃都发生在Interpreter::Invoke()函数内部的不同位置。根因Tensor Arena的大小分配不足。TFLM在运行时会动态分配各种中间张量如果Arena空间耗尽它会尝试分配失败但某些内核可能没有妥善处理分配失败的情况导致访问了非法内存地址。特别是当输入图像尺寸或模型结构稍有变化时对中间张量的需求也会变化。解决首先通过计算模型所有参数和估算中间激活大小给出一个理论值。然后在实际代码中在初始化解释器后调用interpreter-arena_used_bytes()打印出实际使用的Arena大小。将这个值乘以1.5到2的安全系数作为最终的Arena大小。这是最稳妥的方法。5.2 W5100S Socket无法接收数据现象设备能发送UDP包但无法接收服务器发来的配置指令。排查检查Socket模式寄存器Sn_MR确认设置为UDP模式0x02。检查本地端口Sn_PORT是否设置正确。检查物理连接和服务器端防火墙。使用网络调试工具抓包确认服务器发出的包确实到达了设备所在的IP和端口。根因忘记在接收数据后更新Socket的接收读指针Sn_RX_RD。W5100S硬件根据这个指针来判断哪些数据已被主机读取。如果不更新硬件会认为缓冲区始终是满的从而丢弃后续所有新数据。解决在每次读取完接收缓冲区中的数据后必须按照数据手册的说明正确地更新Sn_RX_RD寄存器。通常是将当前Sn_RX_RD的值加上读取到的数据长度然后写回。uint16_t data_len receive_data_from_socket(buf); uint16_t current_rx_rd w5100s_read_reg(Sn_RX_RD0, Sn_RX_RD1); current_rx_rd data_len; w5100s_write_reg(Sn_RX_RD0, (current_rx_rd 8) 0xFF); w5100s_write_reg(Sn_RX_RD1, current_rx_rd 0xFF); // 最后发送RECV命令 w5100s_write_reg(Sn_CR, RECV);5.3 双核数据竞争导致的内存损坏现象系统运行一段时间后图像数据或检测结果出现乱码甚至程序崩溃。排查问题间歇性出现与推理任务和网络任务的负载有关。根因Core0和Core1共享了某些内存区域比如图像缓冲区但没有做好同步。例如Core0正在写入新的一帧图像而Core1刚好开始读取这帧图像进行推理导致读到的数据是半新半旧的。解决对于所有在两个核心间共享的全局变量或缓冲区必须使用同步原语。最简单的就是使用RP2040 SDK提供的queue_add_blocking和queue_remove_blocking函数它们内部已经处理了同步。如果必须使用共享内存则需要使用互斥锁mutex或者将共享数据设计成只读例如Core0生产完一整帧数据后通过一个原子标志位通知Core1Core1读取时该缓冲区变为只读。5.4 模型精度与场景过拟合现象在实验室环境下测试效果很好但部署到实际场景光线变化、不同衣着、不同姿态后漏检和误检率飙升。根因训练模型使用的数据集如公开的COCO或VOC与你的实际场景差异太大。或者自己收集的数据集过于单一导致模型过拟合。解决数据采集尽可能在实际部署环境中从你的摄像头角度采集各种光照条件白天、夜晚、逆光、各种人物姿态站立、行走、蹲下、不同穿着冬装、夏装下的正负样本图片。数据增强在训练时使用强力的数据增强如随机亮度对比度调整、随机裁剪、模拟运动模糊等以提升模型的鲁棒性。模型微调不要从头训练。选择一个在ImageNet或COCO上预训练好的轻量级模型如MobileNet用你采集的小规模场景数据对其进行微调Fine-tuning。这比从头训练收敛更快效果更好。后处理优化调整推理结果的后处理阈值。例如提高判定为“人”的置信度阈值如从0.5提高到0.7可以显著减少误检但可能会增加漏检。需要根据实际场景的容忍度来权衡。这个基于RP2040和W5100S的以太网人体检测器项目就像在微控制器上搭建一座功能完整的“智能哨所”。它挑战了我们对MCU能力的传统认知将AI推理、实时图像处理和稳定网络通信融合在一个火柴盒大小的设备里。整个过程充满了对内存的锱铢必较、对性能的毫秒必争以及对稳定性的反复打磨。最终当你看到设备上的LED随着检测到人而闪烁并在网络另一端收到清晰的JSON数据包时那种将复杂算法嵌入到极致简约硬件中的成就感是使用现成开发板所无法比拟的。它不仅仅是一个项目更是一次对嵌入式AI系统边界的深入探索。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门