USBCAN函数库开发实战:初始化配置与收发报文避坑指南
简介一份面向工业自动化、汽车电子与物联网开发者的周立功USBCAN接口卡函数库提供完整动态链接库及配套头文件、导入库用于快速实现CAN总线通信功能。压缩包共14个文件含11个DLL动态库、1个头文件、1个LIB导入库及1个INI配置文件整体约324KB可支撑USBCAN、CAN232、PCI/PC104等多种接口卡的调用。函数库覆盖设备初始化与关闭、数据帧收发、错误查询、波特率配置、接收滤波及多通道切换等核心API并附有基础配置说明便于开发者理解dll调用流程。目前已有1367人学习下载适合具备一定C/C基础、需要在Windows环境下集成CAN通信的工程技术人员作为参考。1. USBCAN接口卡函数库调试台上救急的那个DLL到底是什么把USBCAN智能接口卡插到电脑上装上驱动打开厂商自带的上位机能收到总线报文能发测试帧。但项目一旦要把CAN通信集成进自己的软件里上位机就成了摆设——程序不能靠人工点按钮去收发报文。这时候要面对的就是USBCAN接口卡函数库整套硬件真正干活的部分打开设备、配置波特率、收发报文、读取错误码全部通过它暴露的API来完成。本文适合两类人一类是第一次在项目里集成USBCAN智能接口卡的开发者另一类是已经会用上位机调试、想摆脱上位机束缚的现场工程师。下面按调用流程拆解从初始化、配置、收发到踩坑排查争取一次讲透。2. 认识函数库的调用链路从设备句柄到总线报文2.1 函数库的职责边界DLL管信号不管业务USBCAN接口卡函数库本质上是一层驱动封装下接USB设备上接业务代码。它管的事情很具体设备枚举、打开、关闭、CAN通道初始化、波特率配置、报文收发、缓冲区管理、错误码返回。它不管的事情也很清楚协议解析。你拿到手的永远是原始数据帧CANopen、J1939、自定义应用层协议的解析都得自己写或者借助协议栈库来完成。这个边界决定了项目的整体架构。在一个某车辆网关项目中架构被明确拆成了三层最底层是API调用层只负责和函数库交互中间是协议解析层处理扩展帧ID和字节序最上层才是业务逻辑。很多团队的教训是试图让底层直接解析业务协议结果函数库升级或者换硬件型号时到处都要改。选型上的理由也很直接为什么用官方函数库而不是自己通过USB驱动直接通信因为厂商已经在DLL里处理了USB批量传输协议、固件命令字、缓存管理、中断处理等底层细节。自己拼USB协议不光工作量大而且异常拔插、缓冲溢出这些场景很难处理到位。除非是纯学习目的不建议绕过函数库。#include 厂商提供的头文件.h #include cstdio // 打开USBCAN设备 // dwDeviceType设备类型号每个型号都有固定常量定义在厂商头文件里 // dwDeviceIndex设备索引接多个同型号设备时从0开始递增 // dwReserved保留参数目前固定填0 DWORD dwDeviceType 厂商头文件中的设备类型常量; DWORD dwDeviceIndex 0; DWORD dwRet VCI_OpenDevice(dwDeviceType, dwDeviceIndex, 0); if (dwRet ! STATUS_OK) { printf(打开设备失败错误码%d\n, dwRet); return -1; }这段代码里最容易被忽略的是返回值。STATUS_OK这个常量在头文件里的值是1不是0。很多新手习惯性地认为返回0才是成功第一次跑就翻车。VCI_OpenDevice返回非0表示成功返回0表示失败失败原因要结合设备管理器和驱动状态去排查。设备索引从0开始不是从1开始这也是个容易踩的点。2.2 调用模型句柄式API和它的三个阶段函数库采用的是句柄式调用模型这和串口API的设计思路很像。VCI_OpenDevice成功后设备就和一个内部句柄绑定后续的VCI_InitCan、VCI_StartCan、VCI_Transmit、VCI_Receive都通过设备类型和设备索引来定位设备不需要额外传入句柄变量。整个生命周期分三个阶段初始化阶段打开设备、配置CAN参数、启动通道运行阶段循环收发报文收尾阶段停止收发线程、关闭设备。三个阶段顺序不能乱尤其是VCI_StartCan必须在VCI_InitCan之后调用否则收发函数会返回错误或者直接失败。阶段调用顺序关键函数常见误用初始化1VCI_OpenDevice打开失败后继续初始化初始化2VCI_InitCan漏掉StartCan启动失败初始化3VCI_StartCan不检查返回值运行循环VCI_Transmit / VCI_Receive缓冲区满时忽略返回值收尾最后VCI_CloseDevice收发线程未退出就关闭上表列出的常见误用会在后面的章节逐个展开。这里先说一个原则每调用一次函数就检查一次返回值。这个习惯能省掉大量现场排查时间。某次排查一个偶发收发异常查到最后就是某处调用没检查返回值导致错误状态被掩盖后面所有调用都在错误模式下空转。收尾阶段还需要注意VCI_CloseDevice会释放内核缓冲区未读取的报文直接丢失。如果业务上有重要数据比如关键诊断报文必须在关闭设备之前保证缓冲区已被读取并完成落盘。3. 初始化与配置把CAN口跑起来的六个关键参数3.1 填写VCI_INIT_CONFIG波特率、滤波和工作模式初始化配置结构体VCI_INIT_CONFIG是整个函数库调用里信息量最大的一个结构体六个字段分别是验收码AccCode、验收掩码AccMask、滤波器保留模式、定时参数Timing0/Timing1、工作模式Mode。现场最常见的现象是两端都设成500kbps但报文就是收不到最后发现Timing0/Timing1的组合写错了。VCI_INIT_CONFIG的配置需要对应厂商文档里的波特率参数表。以常见的500kbps为例一组典型值是Timing00x00Timing10x1C但不同型号的固件对这两个寄存器的换算可能有差异。常见做法是先用官方上位机工具选择波特率并应用成功观察工具的寄存器读数再把这个数值填到代码里。VCI_INIT_CONFIG config; memset(config, 0, sizeof(config)); config.AccCode 0x00000000; // 验收码与掩码配合做ID过滤 config.AccMask 0xFFFFFFFF; // 滤波掩码全F表示接收所有ID config.ResetTime 0; // 复位时间平时填0 config.Timing0 0x00; // 波特率相关对应低速/高速分频 config.Timing1 0x1C; // 波特率相关500kbps时常见组合 config.Mode 0; // 0正常模式1只听模式 DWORD dwRet VCI_InitCan(dwDeviceType, dwDeviceIndex, 0, config); if (dwRet ! STATUS_OK) { printf(初始化CAN通道失败错误码%d\n, dwRet); return -1; } dwRet VCI_StartCan(dwDeviceType, dwDeviceIndex, 0); if (dwRet ! STATUS_OK) { printf(启动CAN通道失败\n); return -1; }滤波是个容易被误解的参数。AccMask是验收掩码1表示这一位不关心0表示这一位必须匹配AccCode中的值。把AccMask设成0xFFFFFFFF表示全部不关心等于收下总线上所有的帧也就是不过滤。如果只想接收特定范围的ID比如某设备只关心ID 0x100到0x1FF的标准帧就需要把掩码和验收码按对齐规则换算。这里需要特别留意验收码和掩码的位对齐不是直接把CAN ID放进去而是按ID的高位对应寄存器的高位来映射不同厂商固件有细节差异最好在本机验证后固定下来。工作模式Mode1是只听模式不会在总线上产生应答信号也不会发送错误帧。这个模式用于总线侦测很实用但在正常通信中不要设置。某次调试时一位同事为了偷看总线数据把Mode设成1结果对方节点发完数据后因为等不到ACK一直重发总线上刷屏排查了半天才发现是模式问题。3.2 启动与关闭的顺序细节StartCan之后的通道才是活的VCI_StartCan在VCI_InitCan之后调用且必须两兄弟成对出现。VCI_InitCan负责把配置写入设备寄存器VCI_StartCan才真正把CAN控制器置入工作状态。漏掉StartCan的表现很隐蔽VCI_InitCan返回成功VCI_Transmit也说不报错但总线上就是测不到任何电平变化。关闭顺序同样有讲究。合理顺序是先置位接收线程的退出标志等待线程结束再调用VCI_CloseDevice。顺序反过来会出现偶发崩溃因为线程还阻塞在VCI_Receive里设备句柄就已经被释放了等到数据回来时线程访问到非法环境。// 收尾清理等接收线程退出后再关闭设备 bRunning false; // 通知接收线程退出 WaitForSingleObject(hThread, 2000); // 等待线程结束 VCI_CloseDevice(dwDeviceType, dwDeviceIndex); // 最后关闭设备这段代码里WaitForSingleObject的超时时间要留够。VCI_Receive的阻塞行为在现场各有差异有的版本设置了100ms超时有的版本会一直等待直到有数据或设备出错。两秒钟的等待通常足够。如果线程还没退出不要强制关闭先把超时时间加大或者用标志位让线程在下次循环退出。关于设备被其他进程占用的问题常见坑是程序崩溃后进程残留。Windows下再次调用VCI_OpenDevice前先打开任务管理器确认没有残留进程。厂商自带的上位机也会占用设备开发和调试时最好避免同时打开。4. 收发报文与滤波别让程序卡在“收不到数据”4.1 发送一帧数据帧结构体与缓冲区写满时的表现发送报文的核心是VCI_CAN_OBJ这个结构体它包含ID、发送类型、帧类型、数据长度和数据内容。这里的ID在标准帧时直接使用原始ID值扩展帧时也直接填29位ID不需要手动位移拼接底层会处理。但这个结论只对大部分厂商固件成立换设备型号时最好先发一帧标准ID和扩展ID验证一下。VCI_CAN_OBJ frame; memset(frame, 0, sizeof(frame)); frame.ID 0x123; // 标准帧ID扩展帧时填29位ID frame.SendType 0; // 0正常发送会重发1单次发送不重发 frame.RemoteFlag 0; // 0数据帧1远程帧请求帧 frame.ExternFlag 0; // 0标准帧11位ID1扩展帧29位ID frame.DataLen 8; // 数据长度CAN最大8字节 memcpy(frame.Data, dataBuffer, 8); // 待发送的8字节数据 DWORD dwRet VCI_Transmit(dwDeviceType, dwDeviceIndex, 0, frame, 1); if (dwRet 0) { // 发送失败或缓冲区满这种情况很常见 printf(发送失败缓冲区可能已满\n); }最坑的地方在这里VCI_Transmit返回0不代表数据没上总线也不代表总线报错绝大多数情况是发送缓冲区满了。DLL内部有一个发送队列队列满后新的帧进不去返回0。正确的处理是延迟重试而不是直接当错误处理。考虑一个场景总线上某个节点故障不发ACK正常发送模式下的帧会一直重发缓冲区很快被填满。SendType字段的含义也要说清楚。SendType0是正常发送模式如果总线仲裁失败或者节点不响应ACK控制器会自动重发保证报文最终被送达代价是占用总线时间。SendType1是单次发送模式发一次不管结果适合测试环境下避免重发风暴。正式通信里绝大多数情况用0。远程帧RemoteFlag1时数据字段没有实际意义只是为了请求对方节点上报数据。常见错误是远程帧里也填了数据长度和内容实际上对方节点看的是ID不是数据区。4.2 接收报文先查数量再批量读取的通用节奏接收报文有五种常见写法轮询VCI_GetReceiveNum加VCI_Receive、直接VCI_Receive阻塞读取、用事件回调、用定时器扫描、以及混合方式。在实际工程里最常见的是第一种先查询缓冲区里有多少帧再批量读取。// 接收线程的主循环 while (bRunning) { // 查询当前缓冲区中待读取的帧数 DWORD dwNum VCI_GetReceiveNum(dwDeviceType, dwDeviceIndex, 0); if (dwNum 0) { // 批量读取一次最多取32帧 VCI_CAN_OBJ frames[32]; DWORD dwRead VCI_Receive(dwDeviceType, dwDeviceIndex, 0, frames, 32); for (DWORD i 0; i dwRead; i) { // 处理一帧按ID分发、解析数据、更新状态 ProcessCanFrame(frames[i]); } } else { // 没有数据时让出CPU避免忙轮询 Sleep(1); } }VCI_Receive的返回值是实际读取的帧数它可能小于len参数因为缓冲区里没有那么多帧。读取之后这些帧会从内核缓冲区移除。如果缓冲区满了呢多数固件的行为是覆盖最旧的帧保留最新帧。所以读取不及时的话丢的是旧帧。这个行为和某些人的直觉相反很多人以为丢的是新帧结果排查时全往数据延迟方向查。总线负载率高时覆盖旧帧其实是好事至少说明程序能拿到尽可能新的数据。关于阻塞等待的问题某些版本的DLL里VCI_Receive在缓冲区为空时会阻塞一段时间再返回而不是立刻返回0。这个行为会直接影响接收线程的响应时间。如果程序对实时性要求高常见做法是先用VCI_GetReceiveNum查询只有数量大于0时才调用VCI_Receive。5. 避坑指南四次现场排查换来的经验清单5.1 设备打不开驱动装好了但VCI_OpenDevice返回0现象设备管理器里能看到USBCAN设备驱动状态正常但程序里VCI_OpenDevice一直返回0。原因多半是32位和64位DLL用错了。DLL有x86和x64两个版本进程位数必须与DLL匹配。另一个常见原因是厂商上位机还开着设备被占用VCI_OpenDevice拿不到句柄。还有一种情况是权限不足设备插在USB Hub上供电不稳设备处于半连接状态。解决先关掉所有厂商工具和残留进程检查任务管理器然后看工程生成的平台是x86还是x64换成对应DLL版本最后检查USB口是不是直连主板供电不稳时换一个口再试。这一套下来能解决八成打开失败的问题。5.2 波特率对不上两端都设成500kbps但就是收不到现象自测时用标准帧能发能收接到对方ECU后报文全丢对方也收不到。原因对方的波特率实际是500kbps但容差很小或者两端的采样点位置不同。Timing0/Timing1的组合在不同固件里代表的采样点可能不一样500kbps只是平均值采样点偏移会导致对方节点判定位错误。解决用示波器勾住CAN_H和CAN_L抓一段波形测量一个数据位的时间宽度确认到底是500k还是499.9k。也可以直接改采样点把TS1/TS2的比例调一下让采样点落在位时间的70%左右。更省事的做法是把手头的USBCAN设备和对方设备接在同一个总线分析仪上看总线分析仪识别出的波特率是多少。这个坑往往是物理层的不是函数库的问题但程序行为表现成API层收发失败。5.3 接收丢帧线程回调里做了耗时操作现象总线负载率只有30%程序偶尔丢帧丢帧数量不固定。原因接收线程在处理一帧数据时做了打印日志、格式化字符串、写数据库等操作处理一帧耗时超过帧间隔。DLL缓冲区一般只有几百帧深度处理不过来就开始覆盖旧帧。解决接收线程只做三件事把帧拷贝进自己的环形缓冲、更新计数器、发送信号给消费者线程。日志和落盘操作全部移到消费者线程。如果仍然丢加大接收线程优先级或者减少VCI_Receive的单次读取数量比如一次读8帧而不是32帧让单次占用时间更短。5.4 关闭时偶发崩溃错误地址指向DLL内部现象程序退出时不是每次都崩但十次有五六次崩报错地址落在VCI_CloseDevice或VCI_Receive内部。原因收发线程还在阻塞或正在调用VCI_Receive主线程先执行了VCI_CloseDeviceDLL内部资源被释放线程再回来访问就会触发访问冲突。解决严格的关闭顺序置位退出标志、等待线程函数返回、再调用VCI_CloseDevice。如果线程阻塞在VCI_Receive里出不来较稳妥的做法是给VCI_Receive设置超时版本或者让线程阻塞时用WaitForSingleObject监测超时强制移除。这个问题的根源是资源竞争不是函数库bug顺序对了就能消除。6. 进阶回调、多设备与异常恢复的三个习惯很多应用场景需要同时管理两个CAN通道比如一个通道采集传感器数据另一个通道发送控制指令。常见做法是每通道一个接收线程线程函数里传入设备类型、设备索引和通道号然后各自独立收发。这里要注意的是设备类型和索引相同但通道号不同的多个线程它们操作的是同一个设备句柄DLL内部对通道的收发是独立缓冲区的线程间不需要额外加锁来保护收发调用本身。另一个复杂场景是设备意外拔插。USBCAN设备拔出时正在进行的VCI_OpenDevice和VCI_Receive调用会返回错误。正确的恢复姿势是检测到设备级返回错误后执行CloseDevice清理状态延迟几百毫秒后重新OpenDevice然后重新InitCan和StartCan。这里有一个真实教训某次现场测试时操作员无意中碰了一下USB线设备掉线程序只做了错误弹窗没有自动恢复结果整个调试窗口期都在等人重启程序。从那以后我每次部署前都强制走一遍设备拔插重连测试确认程序能在设备掉线后自己恢复才敢交给现场。关于回调模式函数库通常不提供注册回调函数的原生接口大部分实现只能用VCI_Receive轮询。如果追求更低延迟可以把接收线程的调度周期压到更短配合时间戳互斥锁读取设备时钟。多设备管理上用统一句柄数组管理多个设备类型和索引启动时批量枚举拔插时自动重连对上层应用透明。这套做法在多个项目里帮助提升稳定性希望帮到你。本文还有配套的精品资源点击获取

相关新闻

输电线路电力金具数据集:从数据体检到YOLO训练全流程

输电线路电力金具数据集:从数据体检到YOLO训练全流程

简介:面向输电线路智能巡检与深度学习目标检测研究需求的电力金具图像数据集,覆盖悬垂线夹、耐张线夹、接续管、防振锤、间隔棒等典型金具类别,适合算法工程师和科研人员开展模型训练与验证。压缩包共5023个文件,包含2511张jpg图像…

2026/10/16 9:38:44 阅读更多 →
ViT猫狗分类实战:从Patch Embedding到注意力可视化

ViT猫狗分类实战:从Patch Embedding到注意力可视化

简介:面向Transformer与计算机视觉初学者,这份实践项目以经典的“猫狗大战”分类任务为载体,完整演示如何用ViT模型搭建图像分类流程。项目不局限于猫狗场景,只需替换数据集并调整类别数参数即可迁移至其他图像分类任务&#xff0…

2026/10/16 9:38:44 阅读更多 →
Multiboot启动实验:从GRUB引导到C语言内核的完整流程

Multiboot启动实验:从GRUB引导到C语言内核的完整流程

简介:面向刚开始接触操作系统实验的学生,这份资源以QEMU与Multiboot协议为切入点,完整演示如何在汇编中编写multiboot header:直接写VGA显存输出“helloworld”,借助串口输出“HELLOWORLD”,并将Ubuntu与主…

2026/10/16 9:37:43 阅读更多 →

最新新闻

大麦网自动抢票脚本快速上手:Automatic_ticket_purchase 新手完整指南

大麦网自动抢票脚本快速上手:Automatic_ticket_purchase 新手完整指南

大麦网自动抢票脚本快速上手:Automatic_ticket_purchase 新手完整指南 【免费下载链接】Automatic_ticket_purchase 大麦网抢票脚本 项目地址: https://gitcode.com/GitHub_Trending/au/Automatic_ticket_purchase Automatic_ticket_purchase 是一个开源的大…

2026/10/18 1:33:47 阅读更多 →
Python课件高效学习指南:73页PPT拆解与实战路径

Python课件高效学习指南:73页PPT拆解与实战路径

简介:这份Python学习课件以73张PPT的形式系统梳理了Python入门阶段的核心知识,面向零基础初学者、高校学生以及需要快速回顾语法要点的开发者,帮助读者在较短时间内建立对Python语言的整体认知。压缩包内共1个PPT文件,大小约13.02…

2026/10/18 1:33:47 阅读更多 →
163MusicLyrics:免费的跨平台歌词提取工具,一键获取网易云与QQ音乐歌词

163MusicLyrics:免费的跨平台歌词提取工具,一键获取网易云与QQ音乐歌词

163MusicLyrics:免费的跨平台歌词提取工具,一键获取网易云与QQ音乐歌词 【免费下载链接】163MusicLyrics 云音乐歌词获取处理工具【网易云、QQ音乐】 项目地址: https://gitcode.com/GitHub_Trending/16/163MusicLyrics 163MusicLyrics 是一款免费…

2026/10/18 1:33:47 阅读更多 →
Maven 3.6.3 稳定版安装配置与避坑指南

Maven 3.6.3 稳定版安装配置与避坑指南

简介:Apache Maven 3.6.3是Java生态中主流的项目构建与依赖管理工具,基于POM模型实现标准化生命周期。面向需要离线搭建Maven环境、或希望快速获得可复用依赖库的Java开发者,适用于内网开发、持续集成及教学实验等场景。压缩包共2000个文件&a…

2026/10/18 1:33:47 阅读更多 →
用C++从零实现一个OllyDbg风格调试器:事件循环、断点与反汇编

用C++从零实现一个OllyDbg风格调试器:事件循环、断点与反汇编

简介:这是一款基于Windows 10平台、以C/C语言实现的调试器源码工程,整体仿照OllyDebug设计,适合软件开发人员、逆向工程师及安全研究人员学习调试器底层机制与实战技巧。工程覆盖创建与附加进程、汇编代码查看与修改、内存/栈/寄存器操作、模…

2026/10/18 1:33:47 阅读更多 →
tiny11builder:1 条命令瘦身 Win11 镜像

tiny11builder:1 条命令瘦身 Win11 镜像

tiny11builder:1 条命令瘦身 Win11 镜像 【免费下载链接】tiny11builder WinUI app for building smaller, customized Windows 11 images. 项目地址: https://gitcode.com/GitHub_Trending/ti/tiny11builder 如果你的机器上有官方 Windows 11 ISO&#xff0…

2026/10/18 1:32:47 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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/18 0:55:02 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →