STM32F107裸机LWIP移植实战:从PHY初始化到80Mbps吞吐调优
简介这份资源是面向嵌入式开发者的STM32F107平台LWIP协议栈移植范例适合已掌握C语言与STM32基础、希望打通以太网通信的工程师和学生。内容围绕轻量级TCP/IP协议栈在Cortex-M3微控制器上的整合展开涉及协议栈架构、以太网MAC驱动、RTOS集成、参数配置编译及启动调试等关键环节并附带评估板手册与移植指南文档。压缩包共294个文件以133个.h头文件与114个.c源文件为主体另有12个汇编文件、工程配置与链接脚本、说明文档及PDF手册整体约6.88MB结构完整便于对照阅读。目前已有266人学习下载。通过该范例读者可理解网络接口层与协议处理层的衔接方式掌握内存池与连接数等配置项的调整思路并借助调试日志排查网络堆栈问题为后续开发HTTP服务器、数据上传等物联网应用提供可复用的移植参考。1. 从零跑通 STM32F107 的 LWIP这份移植范例到底省掉了哪些事手上有一块 STM32F107 的开发板网口 PHY 是常见的 DP83848想跑 TCP/IP 协议栈结果翻遍官方例程发现要么是 F4/F7 的要么是带操作系统的裸机版 LWIP 的移植说明写得云里雾里。这个场景我遇到过不止一次。这份 STM32F107 LWIP 移植范例解决的就是“从零把 LWIP 挂到 F107 上跑起来”这件事——它把 MAC 层驱动、PHY 初始化、内存池配置、协议栈初始化、裸机主循环轮询这套链路完整串了一遍适合刚接触 LWIP 移植的嵌入式工程师也适合手上有 F107 板子想快速验证网络功能的开发者。LWIP 本身是轻量级 IP 协议栈RAM 占用可以压到几十 KB在 F107 这种 72MHz 主频、64KB SRAM 的芯片上跑裸机版完全够用。但“够用”和“跑通”之间隔着一堆细节PHY 地址对不对、RMII 还是 MII、时钟怎么配、内存对齐有没有踩到坑。这份范例的价值就在于把这些细节都落到了具体代码上而不是只给你一个 lwipopts.h 让你自己猜。2. 移植前必须搞清楚的硬件与软件底子2.1 F107 的以太网外设和 PHY 接口选型STM32F107 自带一个 10/100M 以太网 MAC 控制器支持 MII 和 RMII 两种接口模式。选哪种不是拍脑袋决定的得看板子上 PHY 芯片的接法和引脚数量。RMII 只需要 7 根信号线REF_CLK、CRS_DV、RXD0、RXD1、TX_EN、TXD0、TXD1MII 要 16 根。F107 的引脚本来就紧张所以大多数板子走 RMII。但 RMII 有个硬性要求REF_CLK 必须是 50MHz而且这个时钟要么由外部晶振直接提供给 PHY 和 MAC要么由 MCO 引脚输出。我见过有人用 25MHz 晶振接 PHY然后在代码里忘了配 PLL3 输出 50MHz 给 MAC结果就是 PHY 能自协商成功但 ping 不通——这个坑后面会细说。PHY 地址是另一个容易翻车的地方。DP83848 的 PHY 地址由 PHYAD[4:0] 引脚的上拉/下拉决定常见的是 0x01 或 0x00。如果你在代码里写死了 0x01但板子上实际是 0x00那 MAC 初始化阶段读 PHY 寄存器就会一直返回 0xFFFF。这份范例里 PHY 地址是宏定义出来的改起来方便但前提是你得知道自己的板子到底接的是哪个地址。查原理图看 PHYAD 引脚别猜。2.2 LWIP 版本选择和 lwipopts.h 的关键配置LWIP 有 1.4.1 和 2.x 两个大版本分支API 差异不小。1.4.1 的 raw API 和 2.x 的 netconn/socket API 在回调机制上有区别。这份范例用的是 1.4.1 风格因为裸机环境下 raw API 最直接没有操作系统抽象层回调函数直接在中断或主循环里被调用。如果你打算后面上 FreeRTOS那建议直接切到 2.x但那是另一条路。lwipopts.h 是移植的核心配置文件几个参数必须根据 F107 的实际资源来调参数典型值说明MEM_SIZE16*1024协议栈堆内存F107 有 64KB SRAM留 16KB 给 LWIP 比较稳妥PBUF_POOL_SIZE8pbuf 池数量太少会导致接收丢包TCP_SND_BUF4*1024TCP 发送缓冲区影响吞吐TCP_WND4*1024TCP 窗口大小和 SND_BUF 配合ETH_RXBUFNB4以太网接收缓冲区数量ETH_TXBUFNB4以太网发送缓冲区数量CHECKSUM_BY_HARDWARE1F107 的 MAC 支持硬件校验和开了省 CPU这些值不是越大越好。MEM_SIZE 给到 32KB 以上链接时可能就报 RAM 不够了。PBUF_POOL_SIZE 给到 16 以上每个 pbuf 默认 1528 字节算一下就知道 SRAM 扛不住。我一般先用上面的典型值跑通再根据实际吞吐需求微调。2.3 工程目录结构和文件依赖关系这份范例的目录结构大致是这样的eth/下放 MAC 驱动和 PHY 操作lwip/是协议栈源码arch/放 lwipopts.h 和 sys_arch.c裸机下 sys_arch 基本是空实现main.c里做初始化和主循环。关键文件是ethernetif.c它实现了 LWIP 的 netif 接口和底层收发包的桥接。很多人移植失败就卡在这个文件上——low_level_output 里怎么把 pbuf 链拆成 MAC 能发的帧low_level_input 里怎么把 DMA 收到的帧组装成 pbuf这两个函数的实现直接决定能不能通。3. 从 MAC 初始化到 ping 通移植步骤拆解3.1 时钟和 GPIO 配置RMII 模式下的 50MHz 从哪来F107 的以太网 MAC 需要两个时钟一个是 MAC 自身的 25MHz 或 50MHz 参考时钟另一个是 PHY 的 50MHz REF_CLKRMII 模式下。常见做法是用外部 25MHz 晶振接 PHYPHY 内部 PLL 倍频出 50MHz 从 REF_CLK 引脚输出给 MAC。但有些板子省了这颗晶振直接用 STM32 的 MCO 输出 50MHz 给 PHY 和 MAC。两种方案代码配置不同。如果是 MCO 方案需要配 PLL3// 使能 PLL3输出 50MHz 给以太网 RCC_PLL3Config(RCC_PLL3Mul_10); // 假设 HSE 为 25MHz25/5*1050MHz RCC_PLL3Cmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLL3RDY) RESET); RCC_MCOConfig(RCC_MCO_PLL3CLK);这段代码的逻辑是PLL3 的输入先分频再倍频最终输出 50MHz 到 MCO 引脚。参数 RCC_PLL3Mul_10 表示 10 倍频前提是输入分频后是 5MHz。如果你的 HSE 不是 25MHz这个倍频系数要重算。配完时钟后GPIO 要设成 AF_PP 模式但注意 RMII 的 REF_CLK 引脚PA1要设成输入浮空因为时钟是从外部进来的。3.2 PHY 初始化和自协商别跳过这个步骤PHY 上电后需要初始化最关键的是启动自协商并等待完成。很多人直接跳过自协商手动设 100M 全双工结果遇到某些交换机或对端设备就通不了。自协商代码不复杂// 启动自协商 ETH_WritePHYRegister(PHY_ADDRESS, PHY_BCR, PHY_AutoNegotiation); // 等待自协商完成 do { timeout; ETH_ReadPHYRegister(PHY_ADDRESS, PHY_BSR, phyreg); } while (!(phyreg PHY_AutoNego_Complete) (timeout PHY_READ_TO)); // 读协商结果 ETH_ReadPHYRegister(PHY_ADDRESS, PHY_SR, phyreg); // phyreg 的 bit2 和 bit3 表示速度和双工模式PHY_BCR 是基本控制寄存器写 PHY_AutoNegotiation通常是 0x1000启动自协商。PHY_BSR 是基本状态寄存器bit5 是自协商完成标志。PHY_SR 是状态寄存器不同 PHY 芯片的位定义不同DP83848 的 bit2 是链路状态bit3 是速度bit4 是双工。这段代码里 timeout 是防死循环的PHY 没接好或者地址不对时不会卡死在这里。3.3 ethernetif.c 里的收发包实现这是整个移植里最核心的部分。LWIP 通过 netif 结构体里的 input 和 output 回调来收发数据。low_level_output 负责把 pbuf 链里的数据拷贝到 MAC 发送缓冲区static err_t low_level_output(struct netif *netif, struct pbuf *p) { struct pbuf *q; u8 *buffer (u8 *)ETH_GetCurrentTxBuffer(); u32 framelen 0; // 把 pbuf 链里的数据逐段拷贝到连续缓冲区 for(q p; q ! NULL; q q-next) { memcpy(buffer[framelen], q-payload, q-len); framelen q-len; } // 启动 MAC 发送 ETH_TransmitFrame(framelen); return ERR_OK; }逻辑说明LWIP 的 pbuf 可能是链式结构一个完整的以太网帧可能分散在多个 pbuf 里。MAC 的 DMA 需要连续内存所以这里做了一次拷贝。参数 framelen 是最终帧长度传给 ETH_TransmitFrame 后 MAC 会自动加前导码和 CRC。注意 buffer 的获取方式不同驱动库函数名不一样标准外设库是 ETH_GetCurrentTxBufferHAL 库是别的。接收方向low_level_input 把 DMA 收到的帧组装成 pbufstatic struct pbuf * low_level_input(struct netif *netif) { struct pbuf *p, *q; u32 len ETH_GetRxFrameLength(); u8 *buffer (u8 *)ETH_GetCurrentRxBuffer(); // 分配 pbuf 链 p pbuf_alloc(PBUF_RAW, len, PBUF_POOL); if(p NULL) return NULL; // 把数据拷贝到 pbuf 链 for(q p; q ! NULL; q q-next) { memcpy(q-payload, buffer, q-len); buffer q-len; } // 释放 MAC 接收缓冲区 ETH_ReleaseRxFrame(); return p; }这里用 PBUF_POOL 类型分配因为接收路径在中断上下文里不能用 PBUF_RAM会调用内存分配函数可能阻塞。拷贝完成后必须释放 MAC 的接收缓冲区否则后续帧收不进来。3.4 主循环里的协议栈处理裸机环境下没有操作系统LWIP 的定时器和协议处理都放在主循环里while(1) { // 处理接收到的以太网帧 ethernetif_input(gnetif); // 处理 LWIP 定时器ARP 超时、TCP 重传等 sys_check_timeouts(); // 你的应用代码 // ... }ethernetif_input 内部会调用 low_level_input 收帧然后交给 netif-input 即 ethernet_input 处理。sys_check_timeouts 是 LWIP 的软件定时器心跳必须周期性调用否则 ARP 表项不会老化TCP 重传也不会触发。调用周期建议 1ms 到 10ms 之间太慢会影响 TCP 性能。4. 避坑指南PHY 不通、ping 丢包、内存溢出怎么查4.1 PHY 寄存器读出来全是 0xFFFF现象初始化时读 PHY_BSR 一直返回 0xFFFF自协商永远完不成。原因PHY 地址不对或者 RMII 的 REF_CLK 没进来。0xFFFF 通常意味着 MDIO 总线上没有设备响应。解决先用示波器量 REF_CLK 引脚有没有 50MHz 波形。如果没有检查 MCO 配置或外部晶振。如果有波形但地址不对写个扫描程序从 0x00 到 0x1F 逐个读 PHY_BSR看哪个地址返回的不是 0xFFFF。我一般会把这个扫描做成上电自检的一部分省得每次换板子都要改代码。4.2 ping 通但丢包严重现象能 ping 通但丢包率 10% 以上或者 ping 大包直接不通。原因PBUF_POOL_SIZE 太小接收缓冲区不够用。或者 ETH_RXBUFNB 设得太少DMA 来不及处理。解决先把 PBUF_POOL_SIZE 从 8 加到 16ETH_RXBUFNB 从 4 加到 8看丢包是否改善。如果 SRAM 不够可以减小 MEM_SIZE 来腾空间。另一个可能是中断优先级问题——以太网中断被其他高优先级中断打断太久导致接收溢出。检查一下有没有其他中断服务函数执行时间过长。4.3 链接时报 RAM 溢出现象编译链接阶段报 “region RAM overflowed”。原因LWIP 的内存池加上你的应用变量超过了 64KB。解决先看 map 文件确认是哪个段占得最多。通常 MEM_SIZE 和 PBUF_POOL_SIZE 是大头。PBUF_POOL_SIZE 每减 1 能省约 1.5KB。如果还是不够考虑把 lwipopts.h 里的 MEM_LIBC_MALLOC 打开让 LWIP 用标准库的 malloc但这样内存碎片风险会高一些。F107 的 64KB SRAM 跑裸机 LWIP 加一个简单的 TCP 服务器是够的但如果还要跑文件系统或大量缓冲区就得精打细算了。4.4 发送大包时卡死现象发送超过 500 字节的 TCP 数据时程序卡死看门狗复位。原因low_level_output 里没有处理 pbuf 链的 tot_len 和 len 关系或者 ETH_TransmitFrame 的帧长度参数传错了。解决在 low_level_output 里加断言确认 framelen 等于 p-tot_len。如果不等说明 pbuf 链遍历有问题。另外检查 ETH_TXBUFNB 是否够用发送缓冲区耗尽的处理逻辑有没有死等。我习惯在发送函数里加超时计数超过一定次数就返回 ERR_MEM让上层协议栈自己重试而不是死循环。5. 进阶调优把吞吐从 2Mbps 拉到 80Mbps 的几个手段跑通 ping 只是第一步真正用起来还得看吞吐。裸机 LWIP 在 F107 上默认配置跑 TCP 吞吐大概 2-5Mbps优化后能到 60-80Mbps。差距在哪几个关键点。第一把 CHECKSUM_BY_HARDWARE 打开。F107 的 MAC 支持 IP/TCP/UDP 硬件校验和开了之后 CPU 不用算校验和省下来的时间全给数据拷贝。这个在 lwipopts.h 里改一个宏就行但注意有些 PHY 或 MAC 配置下硬件校验和会出错开了之后如果发现包全被丢弃就关掉。第二增大 TCP_WND 和 TCP_SND_BUF。默认 4KB 窗口在局域网里太小了RTT 稍微大一点吞吐就上不去。F107 的 SRAM 允许的话把这两个都设到 8KB 甚至 16KB。但注意 TCP_SND_BUF 增大后MEM_SIZE 也要相应增大否则 pbuf 分配会失败。第三优化 low_level_output 的拷贝逻辑。如果 MAC 支持 scatter-gather DMA可以跳过 memcpy直接把 pbuf 链的地址告诉 DMA。但 F107 的 MAC 不支持所以只能拷贝。拷贝的时候用 memcpy 比逐字节循环快但要注意对齐。如果 pbuf 的 payload 地址不是 4 字节对齐memcpy 在某些编译器下会退化成字节拷贝速度差很多。可以在 pbuf_alloc 时指定 PBUF_POOL 的对齐方式或者手动做对齐拷贝。第四把 ethernetif_input 从主循环挪到中断里。主循环轮询的方式如果应用代码执行时间长收包就不及时。改成中断触发收包能显著降低延迟。但注意中断里不能调用 pbuf_alloc 的 PBUF_RAM 类型只能用 PBUF_POOL。而且中断处理时间要尽量短收完帧就退出协议栈处理还是放主循环。验证吞吐的方法很简单在 PC 上用 iperf 或者自己写个 TCP 发送端板子做接收端统计每秒收到的字节数。我一般会先跑 iperf 默认参数然后逐步调大 TCP_WND 和 SND_BUF看吞吐曲线什么时候到拐点。拐点之后再加参数收益就很小了反而浪费 RAM。从那以后我每次移植 LWIP都会先把 PHY 地址扫描和时钟测量这两步做成固定流程不管板子换多少次先确认这两个底层条件满足再往下调代码。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

AI-First 前端脚手架演进:从零构建集成 Claude Code 的组件自动化生成套件

AI-First 前端脚手架演进:从零构建集成 Claude Code 的组件自动化生成套件

在前端工程化体系的发展历程中,脚手架工具(Scaffolding CLI) 始终是团队规范落地的先锋官。从早期的 Yeoman、vue-cli,到如今轻快的 create-vite 以及各大厂自研的企业级脚手架,它们的核心逻辑十年如一日地停留在同一个…

2026/10/13 20:06:04 阅读更多 →
Claude Code 接入 DeepSeek:从零搭建本地协议网关

Claude Code 接入 DeepSeek:从零搭建本地协议网关

这个组合我盯了很久。一边是终端里的编程助手,交互方式干净利落;另一边是代码能力不错、API 价格也友好的模型服务。把两者接起来之后,我日常写小工具、查报错、改配置的效率明显上来了,而且整个链路完全掌握在自己手里&#xff0…

2026/10/13 20:06:04 阅读更多 →
Vapor 模式下自定义指令的编译重塑:原生 DOM 生命周期钩子绑定实操

Vapor 模式下自定义指令的编译重塑:原生 DOM 生命周期钩子绑定实操

在 Vue 的组件化生态中,自定义指令(Custom Directives) 始终是直接操作底层 DOM 元素、封装无侵入行为逻辑的最佳利器。无论是在表单加载时自动聚焦输入框的 v-focus、在模态弹窗外侧点击自动关闭的 v-click-outside、还是在电商瀑布流中按需…

2026/10/13 20:06:04 阅读更多 →

最新新闻

生成式AI从算法创新到工程落地:关键技术与实操指南

生成式AI从算法创新到工程落地:关键技术与实操指南

1. 从一场大会的议程设计,看生成式AI落地的真实风向如果你最近在关注生成式AI的行业动态,大概率会注意到一个现象:各种技术大会的议程正在悄悄变脸。前两年大家还在热烈讨论“大模型参数规模”“预训练数据量”这类底层指标,而今年…

2026/10/13 22:57:14 阅读更多 →
Qt QTableView vs QTableWidget:高性能表格开发实战指南

Qt QTableView vs QTableWidget:高性能表格开发实战指南

1. 项目概述:为什么一个表格控件值得专门写一篇深度实践笔记?在Qt开发中,QTableWidget和QTableView这两个名字经常被新手混为一谈,甚至不少写了两三年界面的开发者,也只停留在“拖个控件、填几行数据、改改颜色”的层面…

2026/10/13 22:57:14 阅读更多 →
双MCU SPI主从通信实战:atmega8535与atmega8寄存器配置与避坑指南

双MCU SPI主从通信实战:atmega8535与atmega8寄存器配置与避坑指南

简介:这份资源提供ATmega8535作为SPI主机、ATmega8作为从机的完整通信程序,面向正在学习AVR单片机与同步串行总线的嵌入式开发者及电子类专业学生。内容围绕SPI四线制展开,涵盖SCK时钟、MOSI与MISO数据线以及SS片选信号的配置,并涉…

2026/10/13 22:57:14 阅读更多 →
裸机点LED:从启动代码到GPIO寄存器的ARM开发全流程

裸机点LED:从启动代码到GPIO寄存器的ARM开发全流程

如果只能选一个实验来证明自己“真正碰过开发板”,我会选裸机点LED。不是因为它简单,恰恰是因为它把嵌入式开发里最绕的几个概念一次性砸到你面前——程序怎么启动、CPU怎么取指、引脚怎么和寄存器对应、编译出来的二进制怎么烧进去。当年我在JZ2440上做…

2026/10/13 22:57:14 阅读更多 →
集团级蓝图顶层设计实战:从四层架构到落地避坑指南

集团级蓝图顶层设计实战:从四层架构到落地避坑指南

做集团级蓝图顶层设计这类项目,最怕听到的一句话是:“我们集团现在系统太多了,想理一理。” 这句话听起来简单,背后往往牵扯着几十个业务板块、几百套系统、上千个接口、几个亿的数字化预算。如果只把蓝图当成“画几十张漂亮的PPT…

2026/10/13 22:57:14 阅读更多 →
汽车行驶记录仪代码:CAN报文采集与断电保护存储实战

汽车行驶记录仪代码:CAN报文采集与断电保护存储实战

简介:这份资源是面向嵌入式开发与汽车电子方向学习者的汽车行驶记录仪(俗称“黑匣子”)STM32代码工程,适合具备一定C语言与单片机基础、希望深入理解车载数据采集与存储实现的中高级开发者。代码围绕加速度传感器接口、数据记录与…

2026/10/13 22:56:13 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →