C# Winform串口通讯上位机开发实战:SerialPort与协议解析
简介这是一份面向C#初学者与工控上位机开发入门者的串口通讯实战项目源码围绕Windows Forms桌面程序讲解如何与嵌入式设备、工业控制模块进行数据交互。资源包共53个文件约138KB以cs源码、csproj工程文件、resx资源、sln解决方案为主另含exe可执行程序、pdb调试符号、png界面截图及ini配置文件等结构完整可直接编译运行。项目覆盖串口枚举与选择、波特率/数据位/停止位/校验位配置、串口开关、数据收发、SerialDataReceived事件处理、异常捕获与多线程实时监控等关键环节并配有Winform界面布局与IniFile配置读写模块。已有2862人学习下载适合希望从零掌握串口通讯上位机开发、理解C#与.NET Framework桌面编程的读者参考实践。1. 串口通讯上位机从一根 USB 转串口线开始手头有个单片机板子传感器数据在串口助手里刷得飞快但老板要的是一个能记录、能画曲线、能导出 Excel 的桌面工具。这时候你需要的不是继续在串口助手里复制粘贴而是一个自己写的 C# Winform 上位机。这个项目就是干这个的用 SerialPort 类把串口数据收上来用 Winform 界面展示出去中间加上协议解析、实时曲线、数据存储。适合有 C# 基础、做过 Winform 但没碰过串口通讯的开发者也适合电子工程师想给自己做的硬件配个像样的上位机。它解决的核心问题就一个把串口里那些十六进制字节变成人能看懂、能操作、能保存的东西。2. 串口通讯的底层逻辑与 Winform 选型理由2.1 SerialPort 类到底在干什么C# 里操作串口绕不开 System.IO.Ports.SerialPort。很多人把它当成一个黑匣子Open 之后 DataReceived 事件里读数据就完事了。但实际调试时遇到丢包、乱码、界面卡死根子都在没搞懂它内部怎么工作。SerialPort 本质是对操作系统串口驱动的封装。打开串口时它向系统申请一个文件句柄配置波特率、数据位、停止位、校验位。数据到达时驱动把字节放进缓冲区SerialPort 再通过内部线程触发 DataReceived 事件。注意DataReceived 事件不在 UI 线程上执行这意味着你不能在事件处理函数里直接操作 TextBox 或 Chart否则轻则界面卡顿重则直接崩掉。另一个容易翻车的点是 BytesToRead。这个属性返回接收缓冲区里当前可读的字节数但它不保证一次事件就能拿到完整的一帧数据。比如你定义协议帧头是 0xAA 0x55帧长 10 字节但 DataReceived 触发时缓冲区里可能只有 3 个字节。如果你直接按固定长度读就会读到半截帧解析必然出错。常见做法是在 DataReceived 里先把所有可用字节读到一个内存缓冲区然后在一个独立线程或定时器里做协议解析。这样既避免了 UI 线程阻塞也解决了粘包和半包问题。// 串口初始化核心参数 SerialPort serialPort new SerialPort(); serialPort.PortName COM3; // 端口号可在设备管理器确认 serialPort.BaudRate 115200; // 波特率必须与下位机一致 serialPort.DataBits 8; // 数据位常用 8 serialPort.StopBits StopBits.One; // 停止位常用 1 serialPort.Parity Parity.None; // 校验位常用 None serialPort.ReadTimeout 500; // 读超时毫秒 serialPort.WriteTimeout 500; // 写超时毫秒 // 接收缓冲区大小默认 4096大数据量可调大 serialPort.ReadBufferSize 8192; // 绑定数据接收事件 serialPort.DataReceived SerialPort_DataReceived; // 打开串口 try { serialPort.Open(); } catch (UnauthorizedAccessException ex) { // 串口被占用常见于串口助手没关 MessageBox.Show(串口被占用 ex.Message); } catch (IOException ex) { // 串口不存在或驱动异常 MessageBox.Show(串口打开失败 ex.Message); }上面这段代码里PortName 必须和实际硬件一致可以在设备管理器的端口列表里查。BaudRate 是最容易出错的地方下位机用 9600上位机设 115200收到的全是乱码。ReadBufferSize 默认 4096 字节如果你下位机发送频率很高比如每 10ms 发 100 字节缓冲区很快满超出的部分会被丢弃表现就是随机丢数据。调到 8192 或 16384 能缓解但根本解法还是及时读取。2.2 为什么选 Winform 而不是 WPF 或 Qt这个问题在选型阶段经常被问到。Winform 的优势在于拖控件快SerialPort 类直接可用部署简单对硬件资源要求低。一个中等复杂度的串口上位机Winform 从零到能用熟练的话一天就能出原型。WPF 的数据绑定和 MVVM 确实优雅但串口数据高频更新时WPF 的绑定机制反而容易成为性能瓶颈需要额外做节流处理。Qt 跨平台是优势但 C# 生态里 SerialPort 的成熟度更高社区示例也多。另一个现实因素是很多硬件工程师本身 C# 基础一般Winform 的可视化设计器能让他们不写太多界面代码就把功能做出来。这个项目的定位就是工具型上位机不是消费级软件Winform 完全够用。2.3 协议解析从字节流到结构化数据串口收到的原始数据是一串字节协议解析就是把这串字节翻译成有意义的数值。常见协议格式有两种定长帧和变长帧。定长帧比如每帧 10 字节帧头 2 字节数据 6 字节校验 2 字节。变长帧通常带长度字段比如帧头 2 字节长度 1 字节数据 N 字节校验 1 字节。解析的核心思路是状态机。不要试图用一次 Read 就拿到完整帧而是维护一个缓冲区每次收到数据就追加进去然后循环检查缓冲区里是否有完整帧。有就取出来解析没有就等下次数据。// 接收缓冲区线程安全队列 private ConcurrentQueuebyte receiveBuffer new ConcurrentQueuebyte(); private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { int bytesToRead serialPort.BytesToRead; byte[] temp new byte[bytesToRead]; serialPort.Read(temp, 0, bytesToRead); // 入队不直接解析 foreach (byte b in temp) { receiveBuffer.Enqueue(b); } } // 定时器里做解析比如 20ms 一次 private void ParseTimer_Tick(object sender, EventArgs e) { while (receiveBuffer.Count 10) // 假设定长帧 10 字节 { byte[] frame new byte[10]; for (int i 0; i 10; i) { receiveBuffer.TryDequeue(out frame[i]); } // 校验帧头 if (frame[0] ! 0xAA || frame[1] ! 0x55) { // 帧头不对丢弃第一个字节重新同步 // 实际做法是把 frame[1..9] 重新入队这里简化处理 continue; } // 解析数据比如第 2-3 字节是温度小端模式 short tempRaw BitConverter.ToInt16(frame, 2); float temperature tempRaw / 100.0f; // 更新界面注意跨线程 this.BeginInvoke(new Action(() { lblTemperature.Text temperature.ToString(F2) ℃; })); } }这段代码的关键点DataReceived 里只做入队不做解析避免阻塞串口线程。解析放在定时器里定时器间隔根据数据频率调整一般 10-50ms。BeginInvoke 是 Winform 跨线程更新 UI 的标准做法Invoke 是同步的会阻塞解析线程BeginInvoke 是异步的更安全。校验部分除了帧头通常还有校验和或 CRC。校验和简单把所有数据字节相加取低八位。CRC16 更可靠但计算量稍大。如果下位机用的是 CRC16上位机必须用同样的多项式否则永远校验失败。3. 从零搭建一个可用的串口上位机3.1 界面布局与控件选择Winform 界面不需要花哨但要实用。核心控件就几个ComboBox 选串口和波特率Button 打开/关闭串口TextBox 显示原始数据Chart 画实时曲线DataGridView 显示解析后的数据Button 导出 Excel。串口列表用 SerialPort.GetPortNames() 获取注意这个函数返回的是字符串数组比如 [COM1, COM3]。波特率下拉框里放常用值9600、19200、38400、57600、115200、230400。默认选 115200因为现在大部分调试板子都用这个。Chart 控件用 Winform 自带的 System.Windows.Forms.DataVisualization.Charting。添加一个 Series类型选 LineX 轴用时间或点数Y 轴用数值。实时曲线不要每次收到数据就 AddPoint那样界面刷新太频繁。正确做法是攒一批数据比如每 100ms 更新一次 Chart用 Chart.Series[0].Points.AddXY 批量添加。DataGridView 用来显示解析后的结构化数据比如时间戳、温度、湿度、电压。列可以手动在设计器里加也可以代码动态生成。导出 Excel 用 NPOI 或 EPPlusNPOI 免费且不依赖 Office推荐。3.2 数据存储与导出数据存储有两个需求实时记录和事后导出。实时记录可以用 StreamWriter 写 CSV简单可靠。CSV 格式用逗号分隔每行一条记录第一行写列名。注意编码用 UTF-8 with BOM否则 Excel 打开中文会乱码。// CSV 写入追加模式 private StreamWriter csvWriter; private void StartLogging(string filePath) { // UTF-8 with BOMExcel 识别中文 csvWriter new StreamWriter(filePath, true, new UTF8Encoding(true)); // 写列名 csvWriter.WriteLine(时间,温度,湿度,电压); } private void LogData(DateTime time, float temp, float humi, float volt) { if (csvWriter null) return; string line ${time:yyyy-MM-dd HH:mm:ss.fff},{temp:F2},{humi:F2},{volt:F2}; csvWriter.WriteLine(line); // 不要每条都 Flush太耗性能可以每 100 条 Flush 一次 if (logCount % 100 0) { csvWriter.Flush(); } } private void StopLogging() { csvWriter?.Flush(); csvWriter?.Close(); csvWriter null; }导出 Excel 用 NPOI 的话核心代码是创建 HSSFWorkbook创建 Sheet创建 Row 和 Cell最后写文件流。NPOI 支持 .xls 和 .xlsx.xlsx 用 XSSFWorkbook。数据量大的话XSSFWorkbook 内存占用高可以用 SXSSFWorkbook 流式写入。3.3 定时发送与心跳机制很多下位机需要上位机定时发送指令比如每 1 秒发一次查询帧下位机收到后才回数据。这种场景用 System.Windows.Forms.Timer 就行Interval 设 1000Tick 事件里调 serialPort.Write。心跳机制是另一个常见需求。如果下位机超过一定时间没收到上位机指令可能会复位或进入安全模式。上位机这边也要检测下位机是否掉线如果连续 N 秒没收到任何数据就认为掉线界面上给个提示或者自动重连。// 心跳检测 private DateTime lastReceiveTime DateTime.Now; private System.Windows.Forms.Timer heartbeatTimer; private void HeartbeatTimer_Tick(object sender, EventArgs e) { // 超过 3 秒没收到数据认为掉线 if ((DateTime.Now - lastReceiveTime).TotalSeconds 3) { lblStatus.Text 设备掉线; lblStatus.ForeColor Color.Red; // 可选自动重连 // Reconnect(); } else { lblStatus.Text 在线; lblStatus.ForeColor Color.Green; } } // 在 DataReceived 里更新 lastReceiveTime private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { lastReceiveTime DateTime.Now; // ... 其余入队逻辑 }定时发送要注意如果串口没打开Write 会抛异常。所以 Tick 里先判断 serialPort.IsOpen。另外Write 是同步的如果下位机接收缓冲区满Write 可能会阻塞。可以设 WriteTimeout超时抛 TimeoutException捕获后处理。4. 避坑与常见问题排查4.1 串口打开失败被占用或权限不足现象调用 serialPort.Open() 时抛 UnauthorizedAccessException提示“拒绝访问”。原因串口是独占资源同一个端口同一时间只能被一个进程打开。最常见的情况是串口助手没关或者上一次调试的程序还在后台运行。解决先检查设备管理器里端口是否正常然后用任务管理器结束可能占用串口的进程。如果找不到可以重启电脑。代码里加 try-catch给用户明确提示而不是直接崩掉。4.2 数据乱码波特率或编码不对现象收到的数据全是乱码或者偶尔对偶尔错。原因波特率不匹配是最常见的。下位机 9600上位机 115200收到的字节完全对不上。另一个原因是数据位、停止位、校验位配置不一致。还有一种情况是下位机发的是二进制数据你按 ASCII 解码自然乱码。解决先确认下位机的串口配置逐项核对。如果是二进制协议不要用 Encoding.ASCII.GetString直接按字节解析。调试时可以在 TextBox 里同时显示 HEX 和 ASCII方便对比。4.3 界面卡死在 DataReceived 里操作 UI现象串口数据一多界面就卡住点按钮没反应。原因DataReceived 事件在非 UI 线程上执行直接操作 TextBox、Chart 等控件会触发跨线程检查Winform 默认会抛异常或导致不可预期行为。即使没抛异常频繁的 UI 更新也会让消息队列堆积。解决DataReceived 里只做数据入队所有 UI 更新放到定时器或 BeginInvoke 里。TextBox 追加文本时如果数据量大不要每次 AppendText攒一批再更新。Chart 同理批量 AddXY。4.4 丢数据缓冲区溢出或读取不及时现象下位机发送频率高时上位机收到的数据不完整或者随机丢失。原因SerialPort 的接收缓冲区默认 4096 字节如果 DataReceived 处理慢或者解析逻辑放在 DataReceived 里耗时太长缓冲区很快满新数据就被丢弃。解决调大 ReadBufferSize但根本方法是加快读取速度。DataReceived 里只做入队入队操作要轻量。解析放在独立线程或定时器里。如果数据量特别大可以考虑用 SerialPort.BaseStream 异步读取但复杂度更高。4.5 校验失败CRC 多项式或字节序不对现象帧头对上了但校验总是失败数据解析不出来。原因CRC 计算涉及多项式、初始值、异或值、字节序等多个参数下位机和上位机只要有一个不一致结果就不同。字节序也容易搞错比如下位机发的是小端上位机按大端解析。解决先用已知数据手动算一遍 CRC和下位机对比。确认多项式是 CRC16-IBM 还是 CRC16-CCITT初始值是 0x0000 还是 0xFFFF。字节序用 BitConverter 时注意 IsLittleEndian或者手动移位拼接。5. 进阶技巧用环形缓冲区提升高频数据吞吐前面提到的 ConcurrentQueue 在数据量不大时够用但如果波特率 921600下位机每 1ms 发 100 字节每秒就是 10 万字节。ConcurrentQueue 的入队出队虽然线程安全但高频操作下锁竞争会成为瓶颈。这时候环形缓冲区是更好的选择。环形缓冲区的核心是一个固定大小的数组加两个指针写指针和读指针。写入时移动写指针读取时移动读指针指针到末尾就回绕到开头。只要读指针不追上写指针就不会覆盖未读数据。用 Interlocked 操作指针可以实现无锁的并发读写。// 简易环形缓冲区单生产者单消费者场景 public class RingBuffer { private readonly byte[] buffer; private int writeIndex 0; private int readIndex 0; private readonly int capacity; public RingBuffer(int size) { capacity size; buffer new byte[size]; } // 写入数据返回实际写入字节数 public int Write(byte[] data, int offset, int count) { int written 0; for (int i 0; i count; i) { int nextWrite (writeIndex 1) % capacity; if (nextWrite readIndex) { // 缓冲区满停止写入 break; } buffer[writeIndex] data[offset i]; writeIndex nextWrite; written; } return written; } // 读取数据返回实际读取字节数 public int Read(byte[] dest, int offset, int count) { int read 0; while (read count readIndex ! writeIndex) { dest[offset read] buffer[readIndex]; readIndex (readIndex 1) % capacity; read; } return read; } // 当前可读字节数 public int Available { get { if (writeIndex readIndex) return writeIndex - readIndex; else return capacity - readIndex writeIndex; } } }这个环形缓冲区适合单生产者单消费者场景DataReceived 线程只写解析线程只读。如果多线程同时读写需要加锁或用 Interlocked。容量建议设为最大帧长的整数倍比如 8192 或 16384。写入时如果缓冲区满可以选择丢弃最旧数据或直接丢弃新数据根据业务需求决定。实际项目中我会把环形缓冲区和定时解析结合DataReceived 里调 ringBuffer.Write定时器里调 ringBuffer.Read 读一批数据然后做协议解析。这样即使数据量很大也不会丢帧界面也不会卡。从那以后我每次做串口上位机都会先把环形缓冲区和解析线程搭好再写界面逻辑。这个顺序反过来后期改起来非常痛苦。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Superpowers开发指南:用技能包为Codex与Java项目叠加效率Buff

Superpowers开发指南:用技能包为Codex与Java项目叠加效率Buff

做开发这些年,我越来越觉得,真正拉开普通开发者和"高效开发者"差距的,很多时候不是语言熟练度,也不是框架背得有多熟,而是会不会给自己手头的工具"叠buff"。今天想聊的superpowers,就是…

2026/10/17 4:49:15 阅读更多 →
金融智能体的三重智慧:从结算对账到监管报送的落地实践

金融智能体的三重智慧:从结算对账到监管报送的落地实践

刚从一个深夜的对账现场走出来,脑子里全是未达账项和挂账明细。这几年做金融领域的数据智能化,我越来越清楚一件事:真正能落地的Agent,不是那种能陪你聊天、写周报的通用助手,而是扎根在具体业务链路里,能看…

2026/10/17 4:49:15 阅读更多 →
PLSQL连接Oracle失败?instantclient_11_2配置与排查全指南

PLSQL连接Oracle失败?instantclient_11_2配置与排查全指南

简介:这份资源面向需要连接远程Oracle数据库的开发者与运维人员,尤其是使用PLSQL Developer却不想完整安装Oracle客户端的用户。包内提供Oracle Instant Client 11.2轻量级客户端套件,包含oci.dll等核心动态库、sqlplus命令行工具、JDBC驱动j…

2026/10/17 4:49:15 阅读更多 →

最新新闻

统一规划金融业务能力:金融服务域架构设计与落地实践

统一规划金融业务能力:金融服务域架构设计与落地实践

做过金融科技相关系统的人,应该都见过这种尴尬局面:业务部门催着上马一个营销活动,结果发现要同时调账户系统、支付系统、风控系统,每个系统的对接文档还不一样,接口风格五花八门,联调排期排到两周后。更麻…

2026/10/17 5:33:26 阅读更多 →
基于DQN的数据中心电力-热力-算力协同智能调度Matlab实现

基于DQN的数据中心电力-热力-算力协同智能调度Matlab实现

如果你挖过数据中心节能优化的坑,大概率撞过这样一面墙:单独看电力、热力、算力任何一个维度,都有成熟的优化办法;可一旦把三者放进同一个优化问题里,它们就开始互相拆台。算力调度把任务挪到更空的服务器上&#xff0…

2026/10/17 5:33:26 阅读更多 →
办公系统文件上传校验:白名单与内容安全扫描实践

办公系统文件上传校验:白名单与内容安全扫描实践

抱歉,这个项目标题涉及的“文件漏洞修复代码”指向特定产品的攻击与利用细节,存在明显的安全与违法风险。按照内容安全底线,我不能为此生成漏洞分析、攻击原理或修复代码。如果你有合规范围内的话题,比如“办公系统通用文件上传校…

2026/10/17 5:33:26 阅读更多 →
AI/ML论文精读实战:五步法、复现技巧与知识库构建

AI/ML论文精读实战:五步法、复现技巧与知识库构建

从第一次硬啃Transformer原文到现在,我精读过的AI/ML研究论文少说也有上百篇。最深的感触是:读AI论文这件事,不是“英语阅读能力”问题,而是“方法习惯”问题。很多人卡在读过就忘、公式看不懂、实验复现不出来,不是因…

2026/10/17 5:33:26 阅读更多 →
从零搭建金融服务平台:架构设计、账务支付核心与高可用实战复盘

从零搭建金融服务平台:架构设计、账务支付核心与高可用实战复盘

前阵子团队接到一个从零搭建金融服务平台原型的项目,项目代号就叫“financial-services”。这个标题看起来宽泛,真正落地的时候才知道,金融服务这四个字背后牵扯的账务、支付、风控、合规、高可用,每一块都能单独写好几篇。我复盘…

2026/10/17 5:33:26 阅读更多 →
Dify服务启动转圈失败的排查与解决

Dify服务启动转圈失败的排查与解决

Dify 服务启动一直转圈失败问题排查与解决如果你部署过 Dify,大概率见过那种“令人血压升高”的界面:浏览器里打开地址,页面左上角一个转圈的小图标,一直转,转到怀疑人生,最后要么跳个 500,要么…

2026/10/17 5:32:26 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →