V4L2驱动开发核心:video_device结构体深度解析与注册流程

有人问过我好几次,能不能把 V4L2 驱动开发那套东西整理成系列笔记。拖了挺久,这次终于动笔。第一篇先从最底层的 video_device 说起。这个结构体容易被当成“注册一下就完事”的模板代码,实际上它内部有不少值得掰开讲清楚的设计。它是用户空间看到的 /dev/videoX 节点背后那层抽象,也是整个 V4L2 驱动模型的中枢结构,后面要讲的 v4l2_device、vb2_queue 都跟它脱不了关系。把 video_device 的分配、初始化、注册、销毁这条链路弄明白,后续读其它驱动源码会轻松很多。

这篇文章主要面向刚开始写 V4L2 驱动的开发者,也适合那些改过不少驱动但从没系统捋过框架的人。我会从整体层次讲到字段定义,再给出一个最小可用的注册流程,最后把实际项目中踩过的坑一并列出来。内容偏内核态开发,默认你有一些 Linux 驱动基础,对 file_operations、字符设备、kref 这类概念不陌生。

1. 先把 V4L2 的层次结构看清楚

1.1 从用户空间到底层的三层抽象

V4L2 框架设计的核心目的很直接:让用户空间能用一套统一的 open/read/write/ioctl 接口,去操作各种完全不同的视频设备,从 USB 摄像头到 CSI 接口的 ISP,再到视频编解码硬件。要做到这一点,内核里必须解决几个问题:设备节点怎么创建、文件操作怎么分发、buffer 怎么管理、设备能力怎么上报。video_device 就是针对“设备节点和文件操作分发”这层问题的答案。

你可以把整个 V4L2 驱动看成三层。最上层是字符设备层,负责在 /dev 下面生成 videoX 节点,并且把用户空间的 open/release/ioctl 调用转交给对应回调。中间一层是 V4L2 核心框架,它实现了 VIDIOC_QUERYCAP、VIDIOC_S_FMT 这类标准 ioctl 的公共逻辑,也维护着设备能力、优先级、控制处理等状态。最下层才是你的硬件驱动,负责操作 sensor、DMA、编解码器等具体硬件。

video_device 处在最上层和中间层之间。它把字符设备的通用机制和 V4L2 核心框架绑在一起,既承载了一个设备节点的完整生命周期,也保存着 V4L2 核心逻辑需要的回调、锁、设备指针。换句话说,如果没有 video_device,VFS 层只知道这是一个字符设备,根本不知道它跟视频采集有什么关系。

1.2 video_device 在驱动模型中的位置

在具体驱动代码中,一个物理设备往往对应一个 struct v4l2_device,而这个 v4l2_device 下面可能挂多个 video_device。以我做过的一个图像采集项目为例,硬件同时支持采集和输出,驱动里就创建了两个 video_device 节点,一个对应 capture 路径,一个对应 output 路径,共用同一个 v4l2_device 和同一个物理设备结构。

这种“一物理设备多节点”的组织方式很常见。video_device 里保留了指向 v4l2_device 的指针,同时 device_caps 字段会明确这个节点具体提供什么能力。用户空间打开 /dev/video0 时,VFS 通过 inode 里的 cdev 找到对应的 video_device,然后调用该 video_device 注册的 fops 回调。整个调用链大致是这样:open("/dev/video0") -> VFS -> cdev -> video_device->fops->open -> 你的驱动回调。

这里有一个值得注意的设计:V4L2 核心框架并不强制所有 ioctl 都由你的驱动实现,很多标准 ioctl 已经由 v4l2_ioctl_ops 帮你处理掉了。video_device 的 fops 里,通常会把 unlocked_ioctl 指向 v4l2_ioctl,这个函数会在框架内部根据 cmd 分发到 video_device->ioctl_ops 里对应的回调。搞清楚这条路径,你才能理解为什么注册 video_device 时要同时给 fops、ioctl_ops、v4l2_dev 这三个关键成员全部赋值。

2. video_device 结构体逐字段拆解

2.1 必须手动初始化的核心字段

video_device 定义在 include/media/v4l2-dev.h。这个结构体比较大,但真正要求驱动开发者手动初始化的字段并不算多,其余大多由框架内部维护。我按重要程度逐个说。

第一个是 fops,它是 struct file_operations 指针,指向这个设备节点的文件操作集。正常情况下,open、release、read、write、mmap、poll、unlocked_ioctl 这几个回调都需要提供。框架提供了一组辅助函数,比如 v4l2_open、v4l2_release、v4l2_mmap、v4l2_poll,你可以在 fops 里直接引用它们,也可以从驱动自己的 fops 里先做一层封装再调用。

第二个是 ioctl_ops,指向 struct v4l2_ioctl_ops。这个字段决定设备支持哪些标准控制命令。它包含几十个回调,分别对应 VIDIOC_QUERYCAP、VIDIOC_ENUM_FMT、VIDIOC_S_FMT、VIDIOC_REQBUFS、VIDIOC_QBUF、VIDIOC_DQBUF 等等。写驱动时,根据设备实际能力选择性实现,不支持的接口如果被用户空间调用,会返回 -ENOTTY。

第三个是 v4l2_dev,指向包含该 video_device 的 v4l2_device 实例。这个字段对 sysfs 的拓扑结构很关键,建议在调用注册函数之前就把它设置好。如果置为 NULL,注册时接口内部会报错或者行为异常。

第四个是 queue,指向 vb2_queue 实例。它把 video_device 和 videobuf2 框架连接起来,处理缓冲区分配、入队出队、DMA 映射等逻辑。如果你的驱动直接走 vb2 这套标准流程,这个字段必须指向已经初始化好的 vb2_queue;如果自己手动管理 buffer,可以不设置。

第五个是 device_caps,是设备能力标志位,比如 V4L2_CAP_VIDEO_CAPTURE、V4L2_CAP_STREAMING、V4L2_CAP_READWRITE。用户空间调用 VIDIOC_QUERYCAP 时,这个字段会和 v4l2_caps 一起影响返回值。

第六个是 lock,一个指向 struct mutex 的指针。V4L2 核心框架会在某些 ioctl 的公共路径中尝试获取这个锁,用来保护驱动内部状态。它可以和 vb2_queue 的锁是同一个锁,也可以分开,取决于你的驱动设计。

第七个是 name,设备名称字符串。这个名称会出现在设备节点的 sysfs 属性里,也会在某些日志中出现,不代表 /dev 下节点的文件名。

2.2 框架内部管理的隐藏字段

除驱动主动设置的字段外,video_device 里还有一批由框架内部维护的成员。例如 minor,次设备号;num,v4l2 设备编号;cdev,内嵌的字符设备结构;dev,指向 struct device 的指针,注册成功后会与 v4l2_device 的 dev 建立关联。

这些内部字段不建议驱动代码直接改动。尤其是 num,它决定 /dev/videoX 里的编号,虽然注册函数允许传入期望编号,但依赖自动分配更省事。框架内部会维护一张注册表,避免编号冲突。如果你在驱动里强行修改这些字段,很容易造成节点错乱,出了问题还特别难排查。

另外还有 dev-parent 相关的逻辑。video_device 注册成功后,框架会把 video_device 的 device 注册成 v4l2_device 的子设备。这样在 sysfs 下,你能看到一个清晰的层级:物理设备 -> v4l2_device -> video_device。用户空间的 udev 也能根据这个层级创建稳定的符号链接。

2.3 与 v4l2_device、vb2_queue 的协作关系

这三个结构体的关系可以用一句话概括:v4l2_device 表示物理设备整体,video_device 表示一个功能节点,vb2_queue 表示该节点的缓冲队列。实际项目里,同一个 v4l2_device 通常对应一个物理总线地址和设备树节点,而 v4l2_device 内部的多个 video_device 共用同一个底层硬件。

从结构体成员看,video_device 持有 v4l2_device 和 vb2_queue 的指针。而 v4l2_device 内部也有一个 video_device 列表,用于在注销时统一清理所有节点。这就产生了一个循环引用的感觉,但内核里并不存在内存管理上的循环依赖,因为生命周期的负责人是驱动代码,不是结构体之间的引用关系。

很多人刚接触 V4L2 时,会把 v4l2_device 和 video_device 混为一谈。其实看名字就能理解:v4l2_device 是设备层面的抽象,video_device 是节点层面的抽象。一个 v4l2_device 可以没有 video_device,比如它可能只提供 subdev 接口;反之 video_device 一定属于某个 v4l2_device。写驱动的时候,顺序应该是先注册 v4l2_device,再注册各个 video_device,注销时顺序相反。

3. 核心函数接口与源码级解读

3.1 分配与释放:video_device_alloc 和 video_device_release

最传统的分配方式是调用 video_device_alloc(),它内部通过 kzalloc 分配一个 video_device 结构体,并且把 release 回调初始化为 v4l2_device_release。kref 引用计数也会在这里初始化。对应的释放函数是 video_device_release(),但请注意,你不能在驱动里随意调用它,框架通常会在最后一个引用被释放时自动调用,这个引用计数机制和字符设备的打开次数、用户空间持有 fd 数量相关联。

说句实话,我早期写驱动基本都是 video_device_alloc 一把梭,后来发现直接内嵌到自己的私有结构体里更顺手。你可以定义一个 struct my_dev { struct video_device vdev; int hw_index; ... },然后通过 container_of 拿到自己的私有结构体。这样做的好处是减少一次独立分配,也让 video_device 跟私有数据的生命周期天然绑定,不用担心漏释放。缺点是初始化时要用 video_device_release 保证 release 回调不为空。

如果选择内嵌方式,需要在注册前设置 video_device 的 release 回调,把它指向一个自定义函数。这个函数会在设备最后一次引用被释放时被调用,通常你在里面完成 v4l2_device_unregister 后的一些收尾工作。

3.2 注册与注销:两个版本函数怎么选

注册函数有两代,老的是 video_register_device(struct video_device *vdev, int type, int nr),新的是 video_register_device_v2(struct video_device *vdev)。我现在写代码一律用 v2,原因很简单,老的注册函数要求你手动传 type 和 nr,type 还是 int 类型的 vfl_type,传错很隐蔽;v2 则直接从 video_device 结构体内部读取 vfl_type、vfl_dir、dev_parent 等字段,减少出错机会,而且内部实现更干净。

老函数内部其实也就是把 type 和 nr 转存到 vdev->vfl_type、vdev->num 之后调用了 v2 版本。所以如果你看到一份老驱动源码还在用 video_register_device,完全可以无痛换成 v2,只需要在初始化时把 vfl_type 设好。

注册过程大致做几件事:检查 v4l2_dev 是否需要设置 parent 设备、分配次设备号、注册字符设备、创建设备节点。注册成功后,/dev/videoX 就立即可用了。注销时调用 video_unregister_device(struct video_device *vdev),它会从框架注册表中移除节点,并释放字符设备相关资源。注意这个函数是异步语义的,它只移除用户空间可见节点,真正释放内存要等 kref 计数归零后触发 release 回调。

3.3 辅助接口:drvdata 与设备转换

video_set_drvdata(struct video_device *vdev, void *data) 和 video_get_drvdata(struct video_device *vdev) 是一对很常用的辅助接口。它们把私有数据指针存储到 video_device 内部某个字段里。很多驱动在 open 回调里拿不到自己的私有结构体,都会通过 video_get_drvdata(vdev) 来获取。这里我建议你把整个私有结构体指针存进去,而不是只存一个设备树节点指针,省得后面到处转换。

还有一个常用宏是 to_video_device(struct device *dev),它从 struct device 指针反推出 video_device。这在你编写设备模型相关回调时很有用,比如 dev_get_drvdata、或者注册到 sysfs 的属性回调里。类似地,video_device_to_dev(struct video_device *vdev) 返回它内部的 struct device 指针,需要传给某些通用设备 API 时用它。

另外还有一套优先级管理接口:v4l2_prio_init、v4l2_prio_open、v4l2_prio_close、v4l2_prio_change、v4l2_prio_check。它们用于控制多个文件描述符同时访问设备时的优先级。如果设备支持某些只能在独占模式下打开的功能,这些接口能帮你实现简单的仲裁。大部分简单驱动不会用到,但 v4l2_prio_open 在框架的 open 路径里其实会被调用。

4. 实操:从零实现一个 video_device 注册流程

4.1 驱动私有结构体设计与初始化

下面我用一个典型的内嵌式写法,展示一个最简 v4l2 设备节点的完整注册过程。这个例子不涉及实际采集硬件,重点演示 video_device 生命周期管理。

#include <media/v4l2-device.h> #include <media/v4l2-dev.h> #include <media/videobuf2-v4l2.h> struct demo_vdev { struct v4l2_device v4l2_dev; struct video_device vdev; struct vb2_queue queue; /* 其他私有成员 */ }; static void demo_video_device_release(struct video_device *vdev) { struct demo_vdev *demo = container_of(vdev, struct demo_vdev, vdev); /* 释放私有资源 */ kfree(demo); }

先定义私有结构体,把 v4l2_device、video_device、vb2_queue 都内嵌进去。release 回调里使用 container_of 找回整个结构体并释放。这个写法实际上只需要一次 kalloc,就同时分配了物理设备、节点、队列三块核心对象。

4.2 注册前的必要字段赋值

初始化的顺序建议如下,先 v4l2_device_register,再初始化 vb2_queue,最后初始化 video_device 并注册。之所以这样排,是因为 video_device 的 v4l2_dev 和 queue 字段都要指向已经就绪的实例。

v4l2_device_register(&parent_dev, &demo->v4l2_dev)

注意 v4l2_device_register 的第一个参数通常是你的平台设备指针,这样 sysfs 拓扑才能挂到正确的父设备下。第二个参数是 v4l2_device 实例。

接着初始化 vb2_queue,这里只列最关键的几个字段:

demo->queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; demo->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; demo->queue.ops = &demo_queue_ops; demo->queue.mem_ops = &vb2_dma_contig_memops; demo->queue.drv_priv = demo; demo->queue.lock = &demo->lock; vb2_queue_init(&demo->queue);

然后再设置 video_device 字段:

demo->vdev.v4l2_dev = &demo->v4l2_dev; demo->vdev.queue = &demo->queue; demo->vdev.fops = &demo_fops; demo->vdev.ioctl_ops = &demo_ioctl_ops; demo->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; demo->vdev.lock = &demo->lock; demo->vdev.release = demo_video_device_release; strscpy(demo->vdev.name, "demo-capture", sizeof(demo->vdev.name)); demo->vdev.vfl_type = VFL_TYPE_VIDEO; demo->vdev.vfl_dir = VFL_DIR_RX;

有几点要特别说明。vfl_dir 表示数据流方向,采集设备用 VFL_DIR_RX,输出设备用 VFL_DIR_TX,内存到内存设备用 VFL_DIR_M2M。这个字段在旧版内核里不存在,而是通过 vfl_type 的 sub_type 判断,移植老代码时要注意。

device_caps 里的 V4L2_CAP_VIDEO_CAPTURE 表示这个节点能采集视频,V4L2_CAP_STREAMING 表示支持 streaming I/O。如果你的驱动同时支持读写方式采集,还要加上 V4L2_CAP_READWRITE。不要无脑把所有能力都加上,用户空间工具会根据这个字段尝试探测功能,加错了会导致 v4l2-compliance 报错或者在应用程序里出现诡异行为。

4.3 核心注册流程与错误处理

字段都设置好之后,调用注册函数:

ret = video_register_device_v2(&demo->vdev); if (ret < 0) { v4l2_device_unregister(&demo->v4l2_dev); /* 返回错误 */ }

很多新手在注册失败后直接 return,结果把已经注册的 v4l2_device 留在系统里,下次 insmod 直接冲突。建议在任何失败路径上都做一个统一的清理 goto 逻辑,把之前申请的资源全部释放掉。我在实际项目中吃过一次亏,注册 video_device 失败但 v4l2_device 没注销,导致驱动重载时崩溃,从那以后再也没敢跳过清理步骤。

成功注册后,你可以立刻用如下命令验证:

ls -l /dev/video*

正常情况下会看到 /dev/video0,而且它的主设备号是 81,次设备号是框架自动分配的。同时 sysfs 里会出现 /sys/class/video4linux/video0/,目录下的 name 文件内容就是你在 vdev.name 里设置的字符串。

4.4 卸载流程的时序控制

卸载驱动时,注意时序:

video_unregister_device(&demo->vdev); v4l2_device_unregister(&demo->v4l2_dev);

先注销节点,再注销物理设备。video_unregister_device 调用后,即使内核中还有对这个 video_device 的引用,也不会再接受新的 open 操作。已经打开的 fd 仍然可以继续使用,等最后一个 fd close 后,release 回调被触发,然后释放整个 demo 结构体。

如果你用的是 video_device_alloc 而不是内嵌方式,释放时机则在 release 回调里调用 video_device_release。一定要保证 release 回调存在,否则内核在释放节点时找不到回调函数,直接产生一个异常,这个坑很经典。

5. 常见问题与实战排查技巧

5.1 注册失败且 dmesg 无明确报错

遇到 video_register_device_v2 返回负值但 dmesg 只有零散提示的情况,首先检查 v4l2_dev 字段是否已经赋值。这个字段在注册流程里会被用来设置设备的 parent,如果它为 NULL,注册函数内部可能出现空指针解引用或者直接返回 -EINVAL。

其次检查 vfl_type 是否合法。内核里 vfl_type 有 VFL_TYPE_VIDEO、VFL_TYPE_VBI、VFL_TYPE_RADIO、VFL_TYPE_SDR、VFL_TYPE_TOUCH、VFL_TYPE_SUBDEV 等。如果你设置成 VFL_TYPE_SUBDEV,注册出来的就不是 /dev/videoX,而是 /dev/v4l-subdevX,并且它走的是 subdev 的设备节点逻辑,和普通视频采集节点的用户空间接口完全不同。我第一次写 subdev 驱动时就踩过这个,花了大半天才反应过来。

还有一个常见因素是设备编号耗尽。系统里已注册的 video_device 数量达到上限,新注册就会失败。检查 /sys/class/video4linux/ 下已经有多少个 videoX 节点,如果很多,考虑给注册函数传入期望编号,或者手动在 sysfs 中去掉不用的节点。

5.2 打开节点后 ioctl 返回 -ENOTTY

这个错误几乎都是 ioctl_ops 里没有实现对应的回调。比如 VIDIOC_S_FMT 对应 vidioc_s_fmt_vid_cap,你没有实现它,框架就会返回 -ENOTTY。排查办法是把 ioctl_ops 中所有回调列出来,对照 v4l2-compliance 的输出,逐个补齐。

也遇到过一种特殊情况:ioctl_ops 实现了,但 fops 里的 unlocked_ioctl 没有指向 v4l2_ioctl。这种情况下 ioctl 请求根本不会走到 V4L2 框架,而是被你的自定义逻辑拦截或者直接返回 -ENOTTY。在 fops 初始化时务必写上 .unlocked_ioctl = v4l2_ioctl。

5.3 多个 video_device 节点的编号不符合预期

硬件同时有采集和输出功能时,框架分配编号并不保证 video0 一定是 capture、video1 一定是 output,它只保证不同节点编号不冲突。想要固定映射,有两个办法:一是注册前设置 vdev.num,但框架可能因为编号已被占用而返回错误;二是在用户空间通过 VIDIOC_QUERYCAP 检查 device_caps 来判断节点的功能,这也是正规做法。

驱动层面,如果你想让 udev 自动建立稳定的别名,可以在 video_device 注册后设置 dev 的 devt 或者创建自定义 attribute。多数项目不需要这么麻烦,用户空间通过 v4l2-ctl 的 --list-devices 一般能直接看到每个节点对应的驱动名称。

5.4 卸载模块时崩溃或卡死

这个现象最常见的原因是在 open 期间尝试卸载驱动,导致 release 回调被并发调用。检查 vdev.lock 是否设置。如果不设置锁,V4L2 框架中的公共路径会以无锁状态运行,而你的驱动又用自旋锁或者互斥锁保护私有数据,就会形成锁顺序不一致。

另一个原因是 container_of 用错了。如果你把 video_device 内嵌在某个子结构里,release 回调里 container_of 的类型参数必须写对,否则释放的是错误的偏移地址,轻则内存损坏,重则内核崩溃。这块建议写一个 debugfs 或打印日志验证一下偏移量。

还有一个容易被忽略的点:如果你在内核模块的 exit 里手动调用了 video_device_release,会和框架的 kref 释放机制打架,导致双重释放。确保只通过 video_unregister_device 注销节点,让 kref 计数自然归零后自动调用 release,不要手动提前释放。

5.5 调试工具与日志技巧

看 V4L2 驱动问题,我常用的套路是先跑 v4l2-compliance。这个工具能帮你把 ioctl 相关的兼容性、能力上报、格式协商等问题自动测一遍,比肉眼查代码快得多。跑的时候加上 -s 参数还能做流式测试,把 buffer 提交和 dequeue 的路径也覆盖到。测试结果里凡是 FAIL 的项,基本都能映射到 ioctl_ops 里某个没实现好的回调。

内核日志方面,可以在 video_register_device_v2 调用前后加 pr_info 打印关键返回值,也能在 fops 的 open/release 里打印进程 pid 和设备节点编号。V4L2 框架本身不提供太多动态调试输出,需要自己加。可以用 dev_dbg 配合 dynamic_debug,按模块动态打开日志,比随时留一堆 printk 干净。

如果碰到用户空间行为怪异的现象,比如某个 app 拿到了错误的格式或分辨率,优先查看 VIDIOC_QUERYCAP 返回的 device_caps,以及 VIDIOC_ENUM_FMT 返回的格式列表。很多时候不是 video_device 的问题,而是驱动对格式的描述跟实际硬件能力不一致。用户空间全是基于这些描述做决策的,描述错了,后面就全错。

6. 从 video_device 出发的扩展思路

写了这么多,最后还是想分享一个我自己的体会:video_device 虽然是 V4L2 驱动里最小的入口结构体,但把它理解透了对后续工作帮助特别大。因为它几乎把所有关键交互点都集中在一起了,fops 连接 VFS,ioctl_ops 连接控制逻辑,queue 连接数据流,v4l2_dev 连接物理设备,锁字段连接并发模型。你读高通、NXP 这些平台的 V4L2 驱动时,会发现它们本质都是围绕这几组关联在展开。

另外可以动手做个实验,在你的驱动里实现两个 video_device,一个走采集,一个走输出,然后分别用 v4l2-ctl 打开,你会发现两个节点的 file_operations 可以完全不一样。这个灵活性就是 video_device 独立存在的意义。后续这个系列如果继续的话,我会沿着这个思路往 v4l2_device 和 vb2_queue 两个方向深入,把数据流的完整路径串起来讲。

再补充一个小技巧,也是我踩过几次坑之后总结的:初始化 video_device 时,尽量用一个专门的 init 函数把所有字段集中在同一个地方赋值,不要分散在驱动的不同阶段里。因为这类结构体字段多、关联复杂,分散赋值很容易漏掉一两个关键点,而且出问题的时候排查范围会被扩大很多。哪怕代码看起来啰嗦一点,也值得为可调试性买单。

相关新闻

用Python批量生成PPT:内容与样式分离的自动化实践

用Python批量生成PPT:内容与样式分离的自动化实践

1. 为什么我要自己动手做一个 PPT 生成工具先说说我做这个项目的起因。我在一家做企业培训服务的公司待了快六年&#xff0c;岗位名义上是"课程研发"&#xff0c;实际上干的事情非常杂——写课件、做讲义、剪视频、搭题库&#xff0c;其中占用时间最多、也最让人抓狂…

2026/10/20 5:53:19 阅读更多 →
量化交易框架WonderTrader依赖库编译避坑指南

量化交易框架WonderTrader依赖库编译避坑指南

简介&#xff1a;这是为 Ubuntu 22.04 与 gcc 11.4 环境预编译的 WonderTrader 依赖库&#xff0c;面向需要在本地编译量化交易框架的 C 开发者。资源将官方默认依赖路径 /home/mydeps 改为通过环境变量 MyDependsGcc 指向自定义目录 /deps/wtcpp&#xff0c;并同步修改 CMakeL…

2026/10/20 5:53:19 阅读更多 →
LFM2.5-24B-A2B完全解读:24B MoE推理大模型为何只激活2B参数就能跑本地Agent

LFM2.5-24B-A2B完全解读:24B MoE推理大模型为何只激活2B参数就能跑本地Agent

大模型基础模型推理模型本地部署模型量化 【免费下载链接】LFM2.5-24B-A2B-GGUF 项目地址&#xff1a; https://ai.gitcode.com/hf_mirrors/unsloth/LFM2.5-24B-A2B-GGUF 点击查看 免费下载 LFM2.5-24B-A2B-GGUF 是 Liquid AI 开源的 24B MoE 推理大模型的 GGUF 量化仓库&…

2026/10/20 5:53:19 阅读更多 →

最新新闻

STM32 bxCAN实战:从物理层到过滤器配置与调试排障

STM32 bxCAN实战:从物理层到过滤器配置与调试排障

做嵌入式这几年&#xff0c;UART、I2C、SPI都是老朋友&#xff0c;但真正让我觉得“像个通信工程师”的&#xff0c;是开始啃STM32的CAN外设之后。原因很简单&#xff1a;在汽车、机器人和工业现场&#xff0c;CAN总线是默认的“老大哥”&#xff0c;而它在微控制器上的硬件实现…

2026/10/20 6:26:33 阅读更多 →
ReST-MCTS精读:用蒙特卡洛树搜索与过程奖励实现LLM自我训练

ReST-MCTS精读:用蒙特卡洛树搜索与过程奖励实现LLM自我训练

这篇是论文精读系列的第九期。选题的时候我特意挑了一个既讲方法、又讲工程的文章&#xff1a;ReST-MCTS&#xff0c;标题直译过来就是“基于过程奖励引导的树搜索做LLM自我训练”。一句话概括它的核心思想&#xff1a;让大模型不要只想着“快答”&#xff0c;而是像下棋一样&a…

2026/10/20 6:26:33 阅读更多 →
实测,ToClaw为什么是最优秀的OpenClaw龙虾:从 ToDesk 远程到 TaoToken 统一 Key 的配置复盘

实测,ToClaw为什么是最优秀的OpenClaw龙虾:从 ToDesk 远程到 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/20 6:26:33 阅读更多 →
diagram-design:从图形类型到布局配色的技术图设计指南

diagram-design:从图形类型到布局配色的技术图设计指南

1. 从"diagram-design"这个标题说起&#xff1a;它到底在解决什么问题第一次看到"diagram-design"这个标题&#xff0c;我脑子里冒出来的第一个念头是&#xff1a;这大概率不是一个单纯的"画图工具"&#xff0c;而是一套关于"如何把复杂信息…

2026/10/20 6:26:33 阅读更多 →
CTF Misc入门:DOCX隐写与ZIP伪加密的底层原理及010 Editor实操

CTF Misc入门:DOCX隐写与ZIP伪加密的底层原理及010 Editor实操

CTF新手村常客——Misc题型里的DOCX隐写和伪加密&#xff0c;对很多刚入坑的朋友来说&#xff0c;看WriteUp觉得挺简单&#xff0c;真上手时又容易卡壳。我最初玩PolarCTF这类靶场时也是这样&#xff0c;面对一个打不开的docx文件&#xff0c;第一反应是直接改后缀名丢进解压工…

2026/10/20 6:26:33 阅读更多 →
HTTP POST表单提交全解析:Content-Type、编码格式与实战避坑指南

HTTP POST表单提交全解析:Content-Type、编码格式与实战避坑指南

1. 从一个被忽略的细节说起&#xff1a;为什么表单提交远不止填个action很多人第一次接触Web开发&#xff0c;接触到的第一个"能跑起来"的功能&#xff0c;大概率就是一个HTML表单。写一个<form>标签&#xff0c;配上action和method"post"&#xff0…

2026/10/20 6:25:32 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/19 2:54:50 阅读更多 →
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/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练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/18 12:16:16 阅读更多 →