RV1126内核驱动入门:交叉编译到烧写的完整实战
1. 为什么零基础入门建议从RV1126内核驱动开始RV1126内核驱动编译与烧写听起来像是需要啃半年内核源码才能碰的东西但实际拆开看就是一套可以照着做的流程。我第一次接触这块芯片的时候连交叉编译工具链都没配明白照着网上的文章一步步试最后总算把第一个hello.ko跑起来。这篇文章就是想按我实测过的路径把这个过程完整讲清楚适合完全零基础、但有一点点C语言和Linux命令行常识的读者。先说为什么推荐用RV1126入门而不是直接在PC机上学。RV1126属于带NPU和ISP的视觉处理芯片广泛用在智能摄像头、边缘计算盒子这类产品上。它的SDK通常已经帮你整理好了内核源码、交叉编译工具链、文件系统和烧写脚本你不需要从零搭建整个系统只需要把注意力集中在内核驱动本身。这个特性对新手极其友好你遇到问题的时候能区分问题是出在环境配置还是驱动代码而不是所有环节都糊在一起。另外芯片架构是ARM Cortex-A732位内核版本基于Linux 4.x/5.x和大多数嵌入式教材、网上的驱动资料高度匹配。你在这套平台上学到的知识换到其他ARM Linux板子比如树莓派、各种国产开发板逻辑几乎完全一样。所以我认为这是性价比很高的入门路径。那到底需要准备什么我按最低配置列一下一台x86主机装Ubuntu 20.04或18.04 LTS最好有8GB以上内存磁盘空闲空间尽量留大一些内核编译经常需要20-30GB临时空间。开发板和相关电源、串口线、USB烧写线。串口是必须的因为看日志、观察模块加载输出都靠它。SDK源码包一般解压后里面包含kernel目录也就是内核源码树。一个能联网的终端用于安装依赖包和交叉编译工具链。这套东西准备好后续所有操作都在Linux终端里完成。如果你连终端都没用过建议先花半天熟悉一下cd、ls、make、export这些基础命令否则后面会手忙脚乱。整个流程可以压缩成四句话配置交叉编译环境、编写驱动源码、编译生成.ko模块或boot镜像、通过烧写工具部署到开发板验证。接下来我按实际操作的顺序一步步展开。2. 搭建交叉编译环境工具链版本与目录结构里的门道2.1 交叉编译工具链选择与安装交叉编译工具链是这门课的第一个大坑。你在PC上写代码用PC自带gcc编译出来的程序只能在PC上跑而开发板是ARM架构必须用能生成ARM指令的编译器。RV1126的SDK里通常会预置一份工具链路径一般在toolchain/目录下或者SDK初始化的时候已经帮你装好了。我的建议是第一优先使用SDK自带工具链不要自己另外下载因为内核编译对编译器版本非常敏感。如果SDK里没带也可以自己从发行版软件源安装。这里有个关键点RV1126是32位ARM平台所以要装arm-linux-gnueabihf工具链而不是aarch64-linux-gnu。很多新手看到厂商文档里有“armhf”字样却装成了aarch64结果编译内核时出现一堆无法识别的选项。可以这样确认arm-linux-gnueabihf-gcc --version如果命令能输出版本号说明工具链已经可用。否则需要手动安装sudo apt update sudo apt install gcc-arm-linux-gnueabihf这里要注意如果系统是Ubuntu 20.04及以上软件源里的交叉编译器版本可能偏新。对于Linux 4.19/Cortex-A7这个组合来说gcc 9/10问题不大但如果编译时报内联汇编错误或unknown option优先回退到SDK自带工具链。2.2 内核源码树结构认知解压SDK后你会看到一堆目录别慌。我们需要关注的只有kernel目录下面的部分典型结构kernel/ ├── arch/ │ └── arm/ │ ├── configs/ # 平台默认配置文件 │ ├── boot/ │ └── mach-xxx/ # 板级支持代码 ├── drivers/ │ └── xxx # 各类驱动源码 ├── include/ ├── scripts/ ├── Makefile └── Kconfigarch/arm/configs下通常有对应开发板的默认配置文件文件名带平台型号比如某型号的defconfig。编译内核的第一步就是把平台默认配置加载成当前配置。这步相当于告诉内核“你需要支持哪些功能和驱动”配置文件就是一张功能清单。drivers目录下面按类型分了很多子目录gpio、i2c、spi、net、video等等。我们接下来要添加的hello驱动既可以直接放在某个子目录里也可以单独放在内核源码树外的目录中用模块方式编译。新手建议从独立目录开始不影响内核原有代码。2.3 用一次完整编译验证环境是否正常环境配置完不能拍脑袋说没问题一定要做一次完整的原厂内核编译。这一步有两个目的一是验证工具链和源码能配合工作二是生成后面编译模块时需要的“内核编译产物”。因为外部模块编译时必须依赖内核源码树里已经生成好的头文件和符号表没有这些你会遇到autoconf.h缺失之类的报错。在kernel目录下执行export ARCHarm export CROSS_COMPILEarm-linux-gnueabihf- make xxx_defconfig # 用SDK文档里对应的配置名 make -j4 zImage-j4表示用4线程并行编译速度会快不少。这里如果报错先检查依赖库是否装全。常见缺少的包有sudo apt install libncurses5-dev libssl-dev bc flex bison首次编译内核可能需要20分钟到1小时视机器性能而定。编译成功后在arch/arm/boot/下能看到zImage文件这时候环境才算真正通了。我没见过谁能跳过这步直接编模块成功的所以这一小时别省。3. 第一个驱动模块从hello代码到ko文件的全过程3.1 驱动源码骨架编写驱动模块虽然挂在Linux内核里但本质上也是一段C程序只不过它的入口不是main()而是module_init和module_exit指定的函数。内核模块的加载入口主要负责初始化资源卸载入口负责释放。我们先写一个最简版本先不要管设备树、硬件寄存器这些复杂概念。新建一个目录这里假设路径是/home/用户名/hello_driver在里面创建hello.c#include linux/init.h #include linux/module.h #include linux/kernel.h static int __init hello_init(void) { printk(KERN_INFO Hello RV1126 kernel module\n); return 0; } static void __exit hello_exit(void) { printk(KERN_INFO Goodbye RV1126 kernel module\n); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE(GPL); MODULE_DESCRIPTION(A simple hello module);printk就是内核空间的打印函数作用类似用户空间的printf。这里用KERN_INFO表示普通信息级别后面在dmesg里能看到输出。MODULE_LICENSE(GPL)不是可有可无的没有它编译会报警告而且某些内核功能可能对非GPL模块拒绝加载。__init和__exit是两个宏作用是告诉内核这些函数只在初始化/退出时存在正常运行时可以释放节省内存。3.2 Makefile的组成与变量解析模块能编译出来靠的是Makefile里那些看起来绕的参数。我的做法是复制一份通用模板然后修改路径即可。下面这份是经过多次试验的obj-m : hello.o KERNEL_DIR : /path/to/kernel PWD : $(shell pwd) all: $(MAKE) -C $(KERNEL_DIR) M$(PWD) ARCHarm CROSS_COMPILEarm-linux-gnueabihf- modules clean: $(MAKE) -C $(KERNEL_DIR) M$(PWD) ARCHarm CROSS_COMPILEarm-linux-gnueabihf- cleanobj-m : hello.o表示我们希望生成一个名为hello的内核模块后面连接到hello.c。中间的-C选项是让make先切换到内核源码目录读那里的Makefile然后利用M$(PWD)回到当前目录编译模块。ARCHarm和CROSS_COMPILEarm-linux-gnueabihf-不能漏漏了就会用PC的gcc编x86模块拿上板子直接格式错误。这里有个容易被忽略的细节obj-m后面的目标名必须和源码名一致如果你把目标改成myhello.o就得再写一条myhello-objs : hello.o不如直接同名省事。3.3 编译并检查ko文件在hello_driver目录下执行make顺利的话目录下会生成hello.ko同时伴随一堆中间文件。可以用下列命令验证file hello.ko输出里如果有ARM或EABI字样说明架构正确。如果看到x86-64说明交叉编译没有生效回去检查Makefile里的CROSS_COMPILE。再用modinfo查看模块信息arm-linux-gnueabihf-modinfo hello.ko可以看到模块名、描述、许可证等。检查完后用make clean清掉中间文件只保留hello.c和Makefile方便后面打包。到这一步你已经跨过了“能生成板子架构模块文件”这道坎。4. 把驱动编进内核Kconfig与menuconfig的配置链路4.1 模块方式与内建方式的差异前面我们编译的是外部可加载模块module即.ko文件。它的好处是灵活可以随意insmod/rmmod坏了也不影响系统启动。但有些驱动必须在系统启动早期就存在比如存储控制、串口初始化这时候外部模块就来不及了必须编进zImage也就是内建built-in。模块和内建在Kconfig里对应两个选项m表示模块y表示内建n表示不编。明白这三态后面配置内核就心里有底了。设备驱动开发中最常用的套路是先配成模块调试功能稳定后再改成内建或者用init脚本加载。4.2 修改Kconfig添加自己的配置项为了让自己的驱动能出现在menuconfig配置界面里需要给它配一个Kconfig选项。这里我演示一种最小做法在drivers/Kconfig末尾追加内容不太推荐容易污染原厂配置更好的方式是建一个独立目录。不过零基础阶段我建议先在内核源码树外维护源码只使用内核已有的内核模块编译框架。如果你想体验“打开菜单开关驱动”可以在某个驱动子目录下创建一个Kconfig片段。比如在drivers/misc/目录下已有Kconfig中追加config HELLO_MODULE tristate Hello module support default n help This is a demo module for RV1126 driver learning.然后确认drivers/Kconfig里通过source引用了drivers/misc/Kconfig这样菜单上就会出现“Hello module support”。选M保存后退出再在hello_driver目录下执行make内核会根据配置自动编译这个模块。实际上如果你只想编译自己的模块不需要动Kconfig直接在外部Makefile里指定obj-m就足够了。但理解Kconfig的价值在于将来你想炮制一个驱动目录或者把自己的驱动集成进SDK就必须走这套流程。4.3 配置内核并编译生成boot.img在SDK场景下配置内核最常用命令是make menuconfig界面里用方向键浏览按空格切换[ ]、[M]、[*]三种状态。保存退出后.config文件就更新了。接下来要重新生成zImage和对应的设备树文件。设备树DTB描述了硬件连接关系内核启动时用它来识别串口、GPIO、I2C等资源。RV1126这种芯片通常需要把zImage和DTB打包成一个boot.img才能被U-Boot引导。打包方式每个SDK略有差别但原理一样SDK里会有一个mkbootimg类似的脚本输入是zImage和.dtb文件输出是boot.img。你可以先确认SDK文档里是否有现成的build boot.img脚本没有就找“打包boot”相关的Make目标。把boot.img烧写到boot分区后内核才会按新配置启动。5. 烧写到开发板分区表、打包流程与fastboot实操5.1 理解开发板的启动分区RV1126方案常见的内存布局是U-Boot放在起始地址接着是boot分区、rootfs分区等。烧写前必须先搞清楚开发板固件分了几块每块是干嘛的。虽然理论上你可以直接往裸flash里写整片镜像但那会抹掉引导程序非常容易变砖。先用串口连接开发板启动时按提示进入bootloader或升级模式。一个通用的做法是在系统起来后执行adb reboot bootloader或者按开发板上的物理按键触发升级模式。不同板子的进入方式不同但最终目的都是让U-Boot进入等待烧写的状态。下面是常见分区表具体分区名和大小以你的SDK文档为准分区名内容烧写必要性ubootU-Boot引导程序通常不动bootzImage DTB本文需要烧写rootfs根文件系统存放ko模块、应用程序misc升级标识等一般不手动烧写5.2 构建镜像并使用烧写工具我们在第4节已经得到了boot.img。现在把它烧进boot分区。以fastboot为例如果你的SDK用专用图形工具操作逻辑类似都要选择分区和镜像文件fastboot flash boot boot.img执行前先用fastboot devices确认能看到设备ID。如果提示no permissions大概率是udev权限没配用sudo fastboot即可。烧写完成后还要把hello.ko放到板子的文件系统里。有两种方式一是把ko放进rootfs镜像再整体烧写适合发布固件二是用adb push临时推送到板的/data目录适合调试阶段。新手强烈建议用第二种不用重刷整个文件系统。具体命令adb push hello.ko /data/然后重启开发板进入系统后切换目录用insmod加载cd /data insmod hello.ko没有报错说明加载成功再用lsmod可以看到hello模块出现在列表里。卸载时使用rmmod hello注意rmmod后面是模块名不是ko文件名。5.3 验证驱动是否真正工作加载完毕后第一件事是看内核日志。串口下执行dmesg | tail能搜到Hello RV1126 kernel module输出说明模块的init函数确实被调用了。卸载后再执行dmesg | tail应该多出Goodbye那行。我见过不少新人在这里卡住原因是板子的串口波特率或控制台配置不对导致根本看不到内核日志。建议把串口工具波特率设为1500000或115200具体以SDK文档为准。如果dmesg没有输出先检查模块是否加载成功再检查打印级别后面会说。6. 踩坑记录交叉编译版本、模块加载失败与日志排查6.1 编译阶段的常见报错与对策我整理了一张排查表按频率排序报错现象原因解决思路gcc: error: unrecognized command line option工具链版本或架构不匹配切换为SDK自带工具链确认CROSS_COMPILE前缀kernel source tree not foundMakefile中KERNEL_DIR路径不对用绝对路径指向内核源码目录确认该目录下有Makefileinclude/generated/autoconf.h: No such file内核没做过配置回到kernel目录执行make xxx_defconfigNo rule to make target modules当前内核源码树不完整或版本过老重新解压SDK原版内核源码不要用git子模块缺失的仓库工具链版本不匹配是最隐蔽的。我遇到过编译器版本太新导致asm内联汇编语法不兼容的情况报错位置根本不在你的源码里而在内核头文件里。这时候不要去改内核源码先换工具链。SDK预置工具链是最好的避坑方式。6.2 insmod失败的几种典型原因模块编译出来了也推到板子上了一insmod却报错。常见的错误消息有insmod: cant open /data/hello.ko文件不在或路径写错。检查目录和文件名。module: invalid module format最经典说明模块编译时的内核配置和当前运行的内核不一致。这种情况多半是你改了内核配置后用新的boot.img启动但模块是用旧配置编译的或者反过来。解决方法是确保编译模块时使用的内核源码树和板子里运行的内核是同一份配置、同一份源码。Unknown symbol模块引用了其他模块导出的函数但那个模块没加载。需要先加载依赖模块。missing MODULE_LICENSE()编译时用的是旧内核规范新内核要求显示声明许可证回到hello.c加上MODULE_LICENSE(GPL)。还有一种情况值得单独说你用了modprobe而不是insmod。modprobe会先查找依赖并自动加载但它需要depmod生成的模块依赖文件。在嵌入式文件系统里如果你没有运行过depmod -amodprobe hello就会报not found。这是正常的不代表模块有问题直接用insmod即可。6.3 printk日志级别与dmesg调试技巧驱动里最常用的调试手段就是printk但它有级别。数字越小优先级越高dmesg默认只显示低于当前console_loglevel的日志。如果看不到自己的打印可以先用dmesg -c清空日志再加载模块减少干扰。观察输出时优先使用dmesg | grep -i hello为了更直观也可以在代码里临时提高级别printk(KERN_ALERT Hello RV1126 kernel module\n);级别高的信息通常不会被过滤。但正式交付代码时要把调试信息恢复到KERN_INFO或KERN_DEBUG否则会被同事骂刷屏。另外一个实用技巧是给模块传递参数。比如在hello.c里声明一个变量static char *who world; module_param(who, charp, 0644); MODULE_PARM_DESC(who, who to greet);加载时用insmod hello.ko whoRV1126就能在不改代码的情况下改变行为。这个做法在调试真实设备时非常有用你可以用它控制调试级别或者指定设备节点。最后分享一个小习惯我在调试RV1126驱动时会同时开三个终端一个看串口输出一个用dmesg -w跟踪内核日志一个用来敲编译命令。每次改代码后按顺序执行make、adb push、insmod、dmesg四步形成肌肉记忆。等你跑通第一个hello模块后面去啃GPIO、I2C这些真实驱动时流程还是这套只是硬件操作逻辑变复杂而已。

相关新闻

二次判别分析QDA:非线性分类边界背后的原理与Python实战

二次判别分析QDA:非线性分类边界背后的原理与Python实战

1. 需要一个比线性边界更灵活的模型时做分类任务的朋友应该都遇到过这种情况:拿逻辑回归或者线性判别分析(LDA)跑了一版模型,测试集准确率卡在某个数值怎么都上不去。画一下决策边界发现,两类数据之间那条分界线压根不…

2026/10/17 17:55:36 阅读更多 →
Unity UI动效实战:用协程与 AnimationCurve 打造锻造结算流程

Unity UI动效实战:用协程与 AnimationCurve 打造锻造结算流程

很多第一次做锻造、开箱、抽卡这类“结果结算”系统的开发者,往往会有同样一个困惑:为什么别人做的锻造结算面板弹出时,能带出那种“瞬间上头的爽感”,而自己做的时候,最多就是让面板淡入、放个图标、显示几行文字&…

2026/10/17 17:55:36 阅读更多 →
高质量数据集建设实施路径:从质量基准到清洗标注全流程指南

高质量数据集建设实施路径:从质量基准到清洗标注全流程指南

简介:围绕人工智能高质量数据集建设,这份DOCX文档系统梳理了从理论到落地的全链路实施路径,适合数据工程师、算法研究者、数据管理负责人及政企数字化规划人员阅读。文档首先交代研究背景、目的及方法,随后厘清数据集定义、分类与…

2026/10/17 17:55:36 阅读更多 →

最新新闻

OpenClaw深度解析:从聊天到干活的AI Agent如何重塑自动化流程

OpenClaw深度解析:从聊天到干活的AI Agent如何重塑自动化流程

这两年“AI Agent”这个词快被说烂了,但真正能落地干活的并不多。市面上大多数产品还停留在“聊天机器人”的阶段——你问它答,最多帮你查个天气、写段文案,本质上还是一个更聪明的搜索引擎。但最近圈子里有一款叫 OpenClaw 的 Agent 讨论度很…

2026/10/17 18:50:12 阅读更多 →
GEE可视化与随机点缓冲区建立:投影与单位避坑指南

GEE可视化与随机点缓冲区建立:投影与单位避坑指南

说实话,第一次看到“Google Earth Engine(GEE)——可视化并缩放至指定级别和随机点缓冲区的建立”这个标题,我脑子里立刻跳出来一个非常具体的场景:某个区域调查项目里,我要在遥感影像上看清楚样点布设的可…

2026/10/17 18:50:12 阅读更多 →
头歌MySQL实训答案:从照抄到真正掌握SQL的完整指南

头歌MySQL实训答案:从照抄到真正掌握SQL的完整指南

简介:头歌MySQL数据库实训答案有目录.pdf是一份面向头歌平台MySQL数据库实训任务的通关答案汇总,专为正在刷题、补作业或复习数据库基础操作的高校学生与自学者设计,能够帮助读者快速理清实训关卡要求并完成验证。这份PDF以单个文件形式打包&…

2026/10/17 18:50:12 阅读更多 →
随机森林分类实战:从决策树原理到调参与避坑指南

随机森林分类实战:从决策树原理到调参与避坑指南

简介:这是一份面向机器学习学习者的随机森林分类算法系统讲解资料,以PDF格式梳理集成学习(Bagging)框架下随机森林的核心思想,包括有放回自助采样、随机特征子集选择、多棵决策树投票集成等机制,并对比其优…

2026/10/17 18:50:12 阅读更多 →
PyCharm配置Python环境:解释器、虚拟环境与PATH实战指南

PyCharm配置Python环境:解释器、虚拟环境与PATH实战指南

简介:本资源是一份面向Python初学者与PyCharm新用户的实操型配置指南,聚焦解决“如何在PyCharm中正确配置Python解释器及第三方库”这一关键入门问题,适用于课程实训、自学搭建开发环境或项目初始化阶段。文档以清晰步骤拆解核心流程&#xf…

2026/10/17 18:50:12 阅读更多 →
云计算ETF领涨背后:算力需求、资金轮动与指数机制的深度拆解

云计算ETF领涨背后:算力需求、资金轮动与指数机制的深度拆解

最近一段时间,只要盯盘超过十分钟,很难不注意到云计算ETF的动静。盘面上,跟踪云计算主题指数的场内基金连续放量,价格一步一个台阶往上抬,在多数行业板块里显得特别扎眼。我也把这类行情的走势、成交额和资金数据来回看…

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

日新闻

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/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 阅读更多 →