通用型Makefile:多级目录自动收集与依赖追踪实战

做C/C++项目时间长了,几乎每个人都会攒一套自己的Makefile。早年我在一个跨平台工具项目里用的还是那种把所有文件路径一行行写死的Makefile,源码目录从两层涨到四层之后,每次新增一个模块都要回去改源文件列表、改编译规则、改clean,一不留神就漏文件,漏了还不一定报错,经常是链接时符号找不到才反应过来。后来我花了一个晚上,把所有重复维护的工作抽象成了一个支持多目录层级的通用型Makefile配置文件:它不关心你的源码放在哪个目录、嵌套了多少层,复制到项目根目录后只需要改两三个变量,就能完成编译、链接、清理,构建产物统一落到build目录里,源码随便怎么新增目录都不用再碰Makefile。这篇文章把配置原文和背后的设计思路一起拆开讲,适合刚接触make的初学者,也能给正在纠结怎么把构建脚本做通用的开发者当个参考。

1. 为什么我不再用递归Makefile,而选"单文件+自动收集"

1.1 目录层级一多,手写依赖路径就成了体力活

很多项目一开始都是单目录,src下面平铺十几个.c文件,Makefile里写死一个SRCS变量就完事。一旦项目往模块化发展,目录结构很快就会变成src/network/http、src/helper/thread、src/protocol/parser这种多级嵌套。这个时候最笨但也最常见的做法,是把每个文件的相对路径都敲进Makefile:

SRCS := src/main.c \ src/network/http/server.c \ src/network/http/client.c \ src/helper/thread/pool.c \ src/protocol/parser/lexer.c

路径一长,眼花的概率直线上升。更麻烦的是新增文件时,你必须记得去更新这个列表,忘一次就编译出一堆undefined reference。我见过有的项目为了省事,直接在SRCS里写src/*.c src/*/*.c src/*/*/*.c,看起来覆盖了三层,实际上每多加一层就要再补一个通配符,而且make的wildcard对不存在匹配时返回空字符串,某一层目录是空的反而会触发规则匹配异常。这种写法本质上是把"目录层级"这个事实硬编码进了构建脚本,跟手写路径没什么区别。

1.2 递归Makefile的诱惑与代价

还有一类方案是递归Makefile:每个子目录放一个自己的Makefile,顶层Makefile通过make -C逐个进入子目录调用。这样每个模块的编译规则在自己的目录里维护,看起来边界清晰,目录层级再多都不怕。

但递归Makefile有几个非常实际的坑。第一,跨目录依赖很难表达,比如network模块要依赖helper模块生成的静态库,顶层必须在network之前先编译helper,这个顺序靠手工维护,一旦依赖链复杂起来就是一场灾难。第二,并行编译时make -C subdir和顶层make -j组合在一起,经常会出现"目录还未创建就去编译"或者多个子任务同时操作一个共享目录的竞争问题,排查起来很费时间。第三,clean、install这种目标必须递归传递到每个子目录,少写一个子目录就多一份残留。后来我慢慢意识到,中小型项目里递归Makefile带来的模块隔离,远远抵不上它引入的编排复杂度。

1.3 通用型Makefile的取舍标准

我在设计这个通用型Makefile时定的标准很简单:一个文件承载全部构建逻辑,源码目录结构完全交给文件系统自动发现。目标只有四个:

  • 新增/删除源文件时,不改Makefile;
  • 新增/删除子目录时,不改Makefile;
  • 构建产物全部落在build目录里,不污染源码树;
  • 同一份配置文件,换一个项目只需要改项目名、编译器和源目录根路径。

这套取舍下来,最终方案就是"单Makefile + find自动收集源码 + 路径映射 + 自动依赖生成"。下面的内容全部围绕这四条展开。

2. 让make自动找到每一层目录的源文件:扫描与路径映射

2.1 用find还是wildcard:先想清楚你的目录规模

GNU Make内置的$(wildcard ...)函数在单层目录下非常好用,性能也比调用shell快:

SRCS := $(wildcard src/*.c)

但它有一个硬伤:默认不递归。src/*.c只匹配src第一层,src/*/*.c只匹配到第二层,你永远不知道项目未来会涨到几层。虽然可以写$(wildcard src/**/*.c),但make的通配符匹配对**的支持并不像shell和Python那样可靠,不同make版本表现还不一样,放在"通用型"配置里就是定时炸弹。

我最终选择用shell命令收集:

SRCS := $(shell find $(SRC_DIRS) -name '*.c')

find天然递归,不管嵌套多少层,一条命令全部捞出来。配合GNU Make的:=赋值,SRCS只在Makefile解析阶段执行一次shell调用,后续不会再重复执行,性能开销完全可以接受。

两类方案的对比如下:

方案目录层级支持依赖shell跨平台风险适用场景
wildcard默认单层,多层需逐级写否低目录结构固定的简单项目
find递归任意层级,自动递归是高多级目录、频繁增删文件的项目

2.2 路径映射逻辑:src/a/b.c如何变成build/src/a/b.o

拿到SRCS只是第一步,编译时Makefile必须知道".o文件放到哪里"。我的做法是让build目录完整镜像源码目录结构:

SRCS := $(shell find $(SRC_DIRS) -name '*.c') OBJS := $(patsubst %.c, $(BUILD_DIR)/%.o, $(SRCS))

这里patsubst做的事情是把src/a/b.c转成build/src/a/b.o。关键是相对路径的中间部分原样保留。举个例子:

  • src/network/http/server.c→build/src/network/http/server.o
  • src/helper/thread/pool.c→build/src/helper/thread/pool.o

为什么要保留src前缀?因为两个不同子目录里的同名文件太常见了,比如src/foo/util.c和src/bar/util.c。如果只取文件名,两个都会变成build/util.o,后编译的会直接覆盖先编译的,而且make还会因为两个规则指向同一个目标文件而报错。保留相对路径后,两个目标文件分别是build/src/foo/util.o和build/src/bar/util.o,互不干扰。这一个设计直接解决了多目录项目里最长见的同名冲突问题。

2.3 一行-MMD让依赖文件自动生成并随源码更新

只编译源文件还不够,头文件依赖才是大工程。手动维护server.o: server.c server.h ../helper/thread/util.h这种依赖迟早会漏,漏一个头文件就可能导致改完头文件make说"Nothing to be done"。GCC自带机制可以自动解决这个问题:

CFLAGS += -MMD -MP

-MMD让GCC在编译源文件时顺带生成一个.d文件,内容就是该源文件实际include了哪些头文件的依赖关系;-MP会给每一个头文件生成一个空的"假目标",这样即使某个头文件被删除,make的依赖检查也不会因为"找不到目标文件"而报错。

生成的.d文件名自动和.o绑定,所以DEPS可以很容易推导:

DEPS := $(OBJS:.o=.d)

最后用-include $(DEPS)把所有依赖文件引入Makefile:

-include $(DEPS)

前面的-号表示依赖文件不存在时不报错。首次编译时没有.d文件,make正常忽略;编译结束后.d文件生成,第二次编译开始,任何一个头文件更新,依赖它的源文件都会自动重编。这一步做完,才算是真正把Makefile从"文件清单维护者"升级成了"依赖关系跟踪器"。

3. 可以直接抄走的Makefile配置:基础版与扩展版

3.1 基础版:纯C项目的完整配置全文

下面这份是经过多个项目验证的基础版,直接保存到项目根目录,名字就叫Makefile:

# ========================================================== # 通用型Makefile —— 支持任意多级源码目录 # 适用:GNU Make 3.81+,Linux/macOS 默认环境 # ========================================================== PROJECT := demo BUILD_DIR := build SRC_DIRS := src INCLUDE_DIRS := include CC := gcc CFLAGS := -Wall -Wextra -O2 -g LDFLAGS := LIBS := # 1. 递归收集所有 .c 文件,并映射到 build 目录下同名 .o SRCS := $(shell find $(SRC_DIRS) -name '*.c') OBJS := $(patsubst %.c, $(BUILD_DIR)/%.o, $(SRCS)) DEPS := $(OBJS:.o=.d) # 2. 头文件搜索路径 CPPFLAGS := $(addprefix -I, $(INCLUDE_DIRS)) TARGET := $(BUILD_DIR)/$(PROJECT) .PHONY: all clean info all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) # 3. 模式规则:源码 -> 目标文件,并自动生成 .d 依赖文件 $(BUILD_DIR)/%.o: %.c @mkdir -p $(dir $@) $(CC) $(CPPFLAGS) $(CFLAGS) -MMD -MP -c $< -o $@ # 4. 自动包含已生成的依赖文件 -include $(DEPS) info: @echo "SRCS=$(SRCS)" @echo "OBJS=$(OBJS)" clean: rm -rf $(BUILD_DIR)

使用方式就三条命令:

make # 编译 make info # 查看自动收集到的源文件列表 make clean # 清理构建产物

3.2 关键变量逐一解释

很多初学者抄Makefile只抄个形,不知道每个变量是干什么的,出了问题不会改。这里逐个说清楚:

  • PROJECT:生成的可执行文件名。改成你的项目名就行。
  • BUILD_DIR:所有中间产物和最终的二进制文件都放在这里。统一放一个目录的意义在于,clean只需要删掉一个文件夹,源码树绝对干净。
  • SRC_DIRS:源码根目录,支持多个。比如SRC_DIRS := src tests,find命令会自动在多个根目录里都找一遍。
  • INCLUDE_DIRS:头文件搜索路径。$(addprefix -I, include)会把include转成-Iinclude,多个路径就用空格隔开,addprefix会依次给每个路径加上-I前缀。
  • CFLAGS:编译选项。-Wall -Wextra开警告,-O2开优化,-g生成调试信息。按项目需要自己调整。
  • LDFLAGS和LIBS:链接选项和链接库,链接命令写成$(CC) ... $^ -o $@ $(LIBS),需要-lm就把LIBS改成-lm。
  • CPPFLAGS:专门放预处理器参数,通常就是头文件搜索路径,和CFLAGS分开是为了语义清晰。

3.3 扩展版:C++项目、多源目录与库输出

C++项目只需要做三个改动。第一,编译器换成g++,编译选项换成CXXFLAGS标准:

CXX := g++ CXXFLAGS := -std=c++17 -Wall -Wextra -O2 -g

第二,find时同时匹配多种C++后缀,规则也需要为.cpp/.cc/.cxx各写一条模式规则:

SRCS := $(shell find $(SRC_DIRS) \( -name '*.cpp' -o -name '*.cc' -o -name '*.cxx' \)) $(BUILD_DIR)/%.o: %.cpp @mkdir -p $(dir $@) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -MMD -MP -c $< -o $@ $(BUILD_DIR)/%.o: %.cc @mkdir -p $(dir $@) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -MMD -MP -c $< -o $@ $(BUILD_DIR)/%.o: %.cxx @mkdir -p $(dir $@) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -MMD -MP -c $< -o $@

第三,如果要输出静态库而不是可执行文件,把TARGET规则改成ar打包:

TARGET := $(BUILD_DIR)/lib$(PROJECT).a $(TARGET): $(OBJS) ar rcs $@ $^

动态链接库则用g++的-shared:

$(TARGET): $(OBJS) $(CXX) -shared $(CFLAGS) $^ -o $@

我这里提供的扩展版逻辑足够覆盖绝大多数中小型项目,别人问我要"通用Makefile",我基本都是直接把这一套发过去。

4. 从"能跑"到"稳定跑":实操踩坑与规避

4.1 mkdir -p与dir $@的搭配,解决目录不存在问题

第一次用这个Makefile的人,十有八九会遇到这个报错:

gcc: error: build/src/foo/bar.o: No such file or directory

原因很简单:build/src/foo这个目录还不存在,gcc没办法直接在里面创建文件。解决方式不是手动先mkdir一次,而是把目录创建交给make自己。模式规则里这一行是关键:

@mkdir -p $(dir $@)

$(dir $@)的作用是从目标路径里提取目录部分,比如目标是build/src/network/http/server.o,$(dir $@)就是build/src/network/http/。mkdir -p递归创建这个目录,不存在就创建,已存在就静默跳过。这样每次编译都自动保证目录就绪,不需要任何手动初始化。

4.2 find误收文件与空目录引发的问题

如果源码目录里混入了第三方代码、生成器产物、或者带.c后缀的备份文件,find会把它们全部收进SRCS。最典型的是某个子目录跑过protobuf生成器,生成了xxx.pb.c,第二次全量编译时这些文件也被编译进去了,轻则产生多余警告,重则链接时符号冲突。

遇到这种情况,优先考虑把中间产物放在源码目录之外,比如protobuf生成的代码单独放一个gen_src目录,SRC_DIRS里明确写src和gen_src。如果目录必须保留在源码树里,就在收集完SRCS后用filter-out过滤:

EXCLUDE_DIRS := src/3rd src/generated SRCS := $(filter-out $(addsuffix /%, $(EXCLUDE_DIRS)), $(shell find $(SRC_DIRS) -name '*.c'))

$(addsuffix /%, src/3rd)会把src/3rd变成src/3rd/%,filter-out把所有匹配这个前缀的路径全部删除。这个方案完全用make内置函数实现,可读性好,也比find的-prune参数好记。

4.3 clean的边界:别把整个家目录删了

rm -rf $(BUILD_DIR)写起来很爽,但有一个潜在风险:如果哪天BUILD_DIR被误设成.或者空字符串,clean会把源码目录甚至更多东西删掉。我在这方面吃过惊吓,所以clean目标里加了一个安全校验:

clean: @test "$(BUILD_DIR)" != "" && test "$(BUILD_DIR)" != "." && rm -rf $(BUILD_DIR)

test命令会在$(BUILD_DIR)为空或为.时返回非零退出码,make会直接报错停止,不会执行删除。更保守的做法是要求BUILD_DIR必须包含build字样,但test加非空和非.判断已经能覆盖绝大多数误操作场景。

4.4 make -j并发构建的隐藏坑

单Makefile的并行构建比递归Makefile安全得多,因为每个.o目标路径唯一,不会出现两个子make写同一份产物的问题。不过第一次全量编译时,如果你用的是make -j8,偶发的问题来自.d依赖文件还没生成,make对头文件依赖一无所知,只能靠模式规则推导源码依赖。这种状态下的构建结果通常还是对的,毕竟每个.c和.o都建立了模式规则关联,但存在一个小概率事件:干净构建和增量构建的依赖解析不一致,导致某次改动头文件后,依赖它的文件没有被重编。

我的建议很实际:第一次构建先make -j1或直接make,生成.d文件之后再随便加-j参数增量编译。另外,$(shell find ...)在每次make解析时都会执行一次,文件多时会有开销,用:=赋值后只在启动时执行一次,不会反复扫描,这个细节对大体量项目有明显感知差异。

4.5 路径带空格与Windows命令差异

这个Makefile有两个平台相关的前提,必须提前说明。

第一,源码路径里不能有空格。make把空格当作分隔符,find输出C:\Program Files\...这种路径时,make会把路径拆成多个词,编译命令会因为找不到文件而报错。通用型配置文件默认世界是"没有空格路径"的,如果项目不得不放在带空格的目录下,需要做额外的转义处理,复杂度会高很多,我的建议是换目录名,别让构建脚本承担这种本不该承担的兼容逻辑。

第二,Winodws下用GNU Make时,find命令依赖shell环境。原生cmd.exe里没有find的Linux语义,要使用Git Bash或MinGW的shell环境跑make才行。如果项目必须全程Windows MSVC工具链,那这套Makefile就不适用,应该考虑CMake。通用配置的"通用"是有限定范围的,这个边界在一开始就要跟团队讲清楚。

5. 进阶改造:让通用Makefile适配真实项目的几种玩法

5.1 排除目录或文件、按需收集

前面4.2已经给出了按目录排除的写法。如果再想按文件名排除,比如不编译所有*_test.c,用filter-out也很顺手:

SRCS := $(filter-out %_test.c, $(SRCS))

这个能力特别适合在项目里同时放单元测试源码和正式源码的场景。用一个SRC_TEST_DIRS := test变量,配合filter-out,把测试文件排除在正式产物之外,而在跑测试时单独引入,一套Makefile两种用途,非常灵活。

5.2 debug/release双模式切换:一份Makefile两套产物

我用BUILD_TYPE变量来做构建类型切换:

BUILD_TYPE ?= release ifeq ($(BUILD_TYPE), debug) BUILD_DIR := build/debug CFLAGS := -O0 -g3 -DDEBUG else BUILD_DIR := build/release CFLAGS := -O2 -DNDEBUG endif

命令切换:

make BUILD_TYPE=debug make BUILD_TYPE=release

这里有几个细节值得注意。BUILD_DIR必须放在OBJS和DEPS推导之前,否则路径映射还是旧值;CFLAGS使用=而不是+=,避免和Makefile后面的默认CFLAGS叠加,造成-O2 -O0这种冲突;?=表示如果没有在命令行或环境变量里指定BUILD_TYPE,才使用release。默认构建release、需要调试时显式加BUILD_TYPE,这个工作流足够简单,也足够明确。

5.3 链接后的自定义动作:从size到打包脚本

链接完成之后经常要做一些收尾工作:查看镜像体积、复制发布包、生成符号表。我习惯在TARGET规则体里追加自定义命令,比如:

$(TARGET): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) @mkdir -p dist @cp $@ dist/$(PROJECT) @size $@

mkdir -p dist保证dist目录存在,cp把二进制同步到发布目录,size打印每个段的大小。不要小看这几行,在做嵌入式交叉编译的时候,每次看size输出,能直接判断某个新功能加进来后内存涨了多少,比等跑起来再发现内存不够要高效得多。

进一步,想在编译后生成给编辑器用的compile_commands.json,可以在Makefile里增加一个phony目标:

.PHONY: compile_commands compile_commands: bear -- make -B

bear会包装make的整个编译过程,截获每个编译命令生成JSON文件,配合现代编辑器插件的代码跳转和静态分析,体验比手写compile flags舒服得多。

5.4 增量编译与缓存的心得

这套Makefile的增量编译完全依赖.d依赖文件和文件时间戳。实际使用中一个常见现象是:切换git分支后,明明只改了一个文件,make却重新编译了一大半。原因是git切换分支会重置文件时间戳,make判断"头文件比源文件新"时,会连坐触发依赖它的所有源文件重编。这是make工作模式的固有行为,不是配置错了,遇到这种情况让make编一次,后面增量编译就正常了。

如果项目编译耗时明显,建议直接引入ccache。基础版Makefile的编译器路径改成:

CC := ccache gcc

ccache会缓存编译结果,头文件没变的情况下二次编译几乎零耗时。不过要注意一点:第一次切换ccache后建议手动make clean一遍,否则旧的build目录里没有ccache元数据,第一次编译无法命中缓存。把这个习惯写进团队的快速上手指南里,能省下很多人的等待时间。

做通用配置的心态是"配置让机器做该做的事,人只做决策"。这套Makefile我把绝大部分重复工作都交给了find和-MMD机制,剩下的只是回答"编译什么、优化等级多少、要不要调试信息"这几个问题。遇到新的源码目录结构时,先跑一次make info确认SRCS和OBJS收集正确,再交给全套编译流程处理。做一个能自动收集源码的构建模板,你收获的不只是不用手写路径,更是对项目目录扩张的无感处理——目录加了一层又一层,make每次都能把新文件找出来,这种"不碰配置就能继续工作"的踏实感,用过之后就回不去了。

相关新闻

STM32启动流程全解析:从复位向量到main函数前的完整路径

STM32启动流程全解析:从复位向量到main函数前的完整路径

朋友们平时点灯、调外设都很顺&#xff0c;但有没有想过这样一个问题&#xff1a;你在Keil里点一下下载&#xff0c;程序跑到main之前&#xff0c;芯片到底做了什么&#xff1f;很多新手甚至部分老手&#xff0c;遇到程序不跑、变量莫名清零、一进中断就HardFault这类问题&…

2026/10/20 23:02:12 阅读更多 →
diagram-design:从草图到可维护工程资产的图表设计化生产方法论

diagram-design:从草图到可维护工程资产的图表设计化生产方法论

1. 从一张草图到一套系统&#xff1a;diagram-design 到底在解决什么问题 第一次听到 “diagram-design” 这个词&#xff0c;很多人会下意识觉得它只是“画图”的另一种说法。但真正在项目里被图表折磨过的人知道&#xff0c;画图本身从来不是最痛的部分。最痛的是&#xff1a…

2026/10/20 23:02:12 阅读更多 →
RNN文本分类实战:从数据预处理到BiGRU模型调参全记录

RNN文本分类实战:从数据预处理到BiGRU模型调参全记录

前阵子接手一个文本分类需求&#xff1a;给一批用户评论打标签&#xff0c;正向、负向、中性三选一。同事的第一反应是直接上预训练模型&#xff0c;说效果肯定好。我的想法不太一样——先别急着烧显卡&#xff0c;RNN文本分类这个经典方案&#xff0c;在数据量只有几万条、以短…

2026/10/20 23:02:12 阅读更多 →

最新新闻

AF_XDP 实战指南:Ubuntu 24.04 上基于 eBPF 的高性能包处理

AF_XDP 实战指南:Ubuntu 24.04 上基于 eBPF 的高性能包处理

1. 项目概述&#xff1a;AF_XDP 到底解决什么问题碰上 AF_XDP 这个技术点的人&#xff0c;多半是已经在 Linux 上做高性能包处理&#xff0c;或者被 DPDK 的运维成本折腾过之后&#xff0c;想在内核生态里找个折中方案。AF_XDP 不是一个框架&#xff0c;而是 Linux 内核提供的地…

2026/10/20 23:41:33 阅读更多 →
用Unity打造桌面精灵:透明窗口、鼠标穿透与动画实战

用Unity打造桌面精灵:透明窗口、鼠标穿透与动画实战

简介&#xff1a;这是一份基于Unity引擎开发桌面精灵的完整工程资源&#xff0c;面向希望实现类似QQ宠物透明浮动桌面形象的Unity开发者。资源围绕窗口无边界设置、透明材质渲染、UI布局、Animator动画控制以及鼠标交互事件等关键技术点组织&#xff0c;适合有一定Unity基础、想…

2026/10/20 23:41:33 阅读更多 →
GLSL变量与类型系统详解:从标量到矩阵的Shader避坑指南

GLSL变量与类型系统详解:从标量到矩阵的Shader避坑指南

如果你写过一段时间的着色器&#xff0c;大概率遇到过这种时刻&#xff1a;shader编译失败&#xff0c;错误信息指向某个变量&#xff0c;你盯着那一行看了很久&#xff0c;愣是没发现哪里有问题。我印象最深的一次是在某个延迟渲染demo里&#xff0c;片元着色器把法线数据当成…

2026/10/20 23:41:33 阅读更多 →
C语言是什么?从底层原理到上手路径的全面解析

C语言是什么?从底层原理到上手路径的全面解析

编程这行干久了&#xff0c;总会被外行朋友或刚入行的新人问到同一个问题&#xff1a;“C语言到底是什么&#xff1f;” 每次被问到&#xff0c;我都会愣一下&#xff0c;因为这个问题看似简单&#xff0c;真要一两句话说清楚还挺难。它不像Java能“写安卓APP”&#xff0c;也不…

2026/10/20 23:41:33 阅读更多 →
好写作AI如何用游戏化设计破解写作恐惧,养成写作习惯

好写作AI如何用游戏化设计破解写作恐惧,养成写作习惯

1. 好写作AI到底在做什么&#xff1a;把“写作”变成“练级”1.1 它解决了什么问题&#xff1a;写作恐惧不是懒&#xff0c;是“防御”我见过太多人说自己“不会写”“写不出来”&#xff0c;包括我自己也曾是这样。面对一个空白文档&#xff0c;光标一开一合&#xff0c;脑子里…

2026/10/20 23:41:33 阅读更多 →
华三AC WX5560H AP信道优化实战:解决信号满格却卡顿

华三AC WX5560H AP信道优化实战:解决信号满格却卡顿

前阵子在某园区处理无线投诉时&#xff0c;我碰到一个很典型的场景&#xff1a;AP 装了一堆、信号显示也是满格&#xff0c;但视频会议就是动不动卡成 PPT。当时很多人第一反应是带宽不够&#xff0c;可跑到华三 AC WX5560H 上一看&#xff0c;控制器负载很低&#xff0c;出口带…

2026/10/20 23:40: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/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

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

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

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

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

如果你的信息流最近也天天刷到“机器人大脑”这个词&#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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →