用perf与ALSA精准定位Linux音频设备爆音和xrun问题

1. 场景还原:一次典型的音频爆音排查

1.1 问题现象

前一阵子接手一个跑着Linux系统的音频播放设备项目,硬件平台用了一颗常见的嵌入式SoC,音频走的是I2S接口外接编解码芯片。设备本身功能不复杂,就是实时播放音频流,但测试反馈播放过程中会周期性出现“咔哒”声,负载稍微上来一点,爆音就变得很密集,严重的时候甚至出现十几毫秒的声音断续。

这种问题在音频设备里很常见,但定位起来并不轻松。单纯看应用层代码很难看出毛病,因为播放线程没报错,缓冲区的数据也一直在喂,可声音就是不对。我当时的目标很明确:找到爆音出现的真实原因,是CPU来不及处理,还是中断被抢占,还是DMA传输本身出了问题。

排查工具我最后落在perf和snd这两条线上。perf是Linux内核自带的性能剖析工具,snd对应的是内核音频子系统的ALSA驱动体系。这套组合不能说覆盖所有音频问题,但对于绝大多数的“声音卡顿”、“爆音”、“延迟抖动”问题,基本都能给出一个物理级的答案。

1.2 为什么是perf,而不是其他工具

设备上有top、htop,也有简单的日志打印,但我没有一上来就去看CPU占有率。原因很简单:音频爆音这类问题,CPU整体使用率往往不高,甚至只有10%到20%,问题出在“某个瞬间的延迟”上,而不是“长期的平均负载”。

举个容易理解的例子:把音频缓冲区想象成送水管道,平均流量看起来足够,但中间某个阀门偶尔卡一下,水流就断一刹那,下游就听到“咕噜”一下。top告诉你的是一分钟的平均流量,而perf能精确记录每一个阀门开合的间隔,它关注的是瞬时事件和中断行为,这正是音频问题需要的视角。

snd侧则需要看内核音频驱动栈:ALSA框架下,一个音频周期(period)的DMA传输完成后会触发中断,驱动在中断里更新指针并唤醒用户空间的等待线程。这一整套流程里的任何一个环节出现延迟,都会直接表现为音频数据的饥饿或者覆盖,也就是所谓的xrun(underrun/overrun)。所以排查路径自然分成两条:第一条是用perf看CPU侧的事件调度,第二条是看snd驱动侧的指针更新和中断响应。

1.3 一套完整的排查思路

我给自己定的排查路径是先粗后细,先系统后进程。

具体来说就是先用perf top看全局热点,看到底是谁在抢CPU;再用perf stat统计中断和上下文切换的频率;接着用perf record抓取一段带调度的运行记录,定位音频播放线程的唤醒延迟;最后用ALSA的xrun调试接口和经验公式来判断缓冲区配置是否合理。

这个思路里,perf负责回答“谁在什么时候耽误了音频线程”,snd框架负责回答“驱动侧配置是否留了足够的余量”,两边交叉验证就能把问题定位到具体环节。

2. 拆解音频链路:先知道声音是怎么“卡”的

2.1 一条完整的音频数据通路

在动手跑命令之前,我先把音频链路在脑子里过了一遍。一条典型的Linux音频播放通路由四层组成:

  • 用户空间应用层:比如alsa-lib之上的播放程序,负责把解码后的PCM数据写入缓冲区。
  • ALSA框架层:snd_pcm层的核心逻辑,维护hw_ptr和app_ptr两个指针。
  • 驱动层:snd_soc平台驱动和编解码器驱动,负责配置I2S控制器、DMA通道和编解码芯片。
  • 硬件层:DMA控制器把内存中的音频数据搬运到I2S发送引脚,由编解码芯片转换为模拟信号。

正常情况下,DMA工作在循环模式,硬件自动从环形缓冲区搬运数据,应用层只需要保证数据写入的速度大于DMA消耗的速度即可。DMA搬运完一个周期就会产生一个中断,驱动在中断上下文中更新hw_ptr并唤醒在read/write上等待的应用线程。

这个设计本身没有大问题,但正因为依赖“中断驱动”,整个链路的实时性其实很脆弱。中断被延迟、驱动锁竞争、应用线程唤醒不及时,任何一个环节掉链子,爆音就来了。这也解释了为什么单纯调大缓冲区不总是有效:它能延缓问题,却不能消除问题。

2.2 什么是xrun

音频圈子里的老手常挂嘴边的一个词叫xrun,它是overrun和underrun的合称。

underrun(欠载)指的是DMA那边要数据,但环形缓冲区里已经没有新数据可送了,硬件读到的是一段静音或者旧数据,表现就是“咔哒”一声。overrun(过载)则相反,多发生在录音场景,硬件往缓冲区写数据时应用没来得及读走,新数据覆盖了旧数据。

对播放场景来说,underrun是最常见的爆音来源。排查时需要知道两件事:一是当前设备的xrun计数是多少,二是每次xrun发生前后,系统里到底发生了什么。

ALSA驱动数据结构里有一个xrun计数器,如果驱动实现了调试接口,可以在/proc下看到相关信息。但这个计数器只是结果,不是原因。真正的原因分析要回到时序上,这就要用到perf的调度事件了。

2.3 perf能覆盖的音频链路环节

perf工具能覆盖的链路环节分成三个维度:

  • 硬件层面:通过PMU(性能监控单元)采集CPU周期、缓存命中率等数据,主要用于判断代码执行效率。
  • 内核事件层面:通过tracepoint采集sched_switch、irq_handler_entry、softirq_entry等事件,判断线程调度和中断响应时机。
  • 软件事件层面:通过context-switches、cpu-clock、task-clock等事件,判断进程实际获得了多少CPU时间。

音频爆音问题90%的根因都落在第二层,也就是调度和中断延迟上。所以我这次排查的主角是perf sched、perf record -e sched:*、perf record -e irq:*这一组事件。

另外perf还可以用--call-graph抓取调用栈,这在定位“到底是哪段代码持锁太久”时尤其好用。结合snd驱动里的snd_pcm_period_elapsed、snd_pcm_update_hw_ptr0等函数名,可以直接看到驱动在中断里实际做了什么。

2.4 为什么不先用ftrace

有人可能会问,为什么不用ftrace或者trace-cmd,它们的内核追踪能力比perf更细。

我的实际经验是:ftrace偏底层,适合追踪某个内核函数的调用过程,但不太适合快速回答“系统整体怎么了”这个问题。perf的优势在于抽象层次更高,自带统计分析能力,I/O重定向、采样率控制、调用栈聚合都做得比较顺手,而且和内核符号表、调度器事件结合得很好。

两个工具不是互斥的。后面如果锁定到具体函数,我会再用ftrace去确认。但第一轮排查,perf的效率明显更高。

3. 实操记录:四步定位卡顿根因

3.1 步骤一:perf top快速看热点

我习惯先把设备接上串口,跑一条perf top命令观察全局热点。命令很简单:

perf top -a -g -F 999

-a表示全系统采样,-g开启调用链,-F 999把采样频率设为999Hz。这里不用1000是因为避免和其他周期性任务产生共振采样,这个技巧是某位前辈教我的,实际效果确实更稳定。

屏幕上跳动的内容能很快告诉你哪些函数在消耗CPU。如果看到snd_pcm_update_hw_ptr0占的CPU比例异常高,说明中断里做了太多事;如果看到某个应用线程频繁出现在热点里,说明它在忙轮询,而不是基于中断唤醒。

我们设备上跑出来的结果,热点分布比较分散,没有明显异常。这也符合预期,因为爆音是间歇性的,perf top是累计统计,会被大量正常时段稀释掉。所以这一步只是摸底,真正的证据链要靠后面的定向录制。

3.2 步骤二:统计中断与软中断分布

第二步是看中断和调度事件的总量级。我用了一条统计命令:

perf stat -e irq_vectors:irq_workout,irq:irq_handler_entry,irq:softirq_entry,sched:sched_switch,sched:sched_wakeup -a -- sleep 30

这条命令会统计30秒内系统发生的硬中断、软中断、任务切换和任务唤醒次数。虽然sleep 30期间未必能覆盖到爆音时刻,但可以先拿到基线数据。

统计结果显示中断频率并不夸张,软中断数量正常,这至少把“中断风暴”类问题排除了。接下来就要等爆音发生的时刻,用录制的方式把现场抓下来。

3.3 步骤三:录制调度与中断事件,抓取现场

3.3.1 录制命令与参数选择

录制用的是这条组合:

perf record -a -e sched:sched_switch -e sched:sched_wakeup -e sched:sched_wakeup_new -e irq:irq_handler_entry -e irq:softirq_entry -g -- sleep 120

如果嫌事件太多导致环形缓冲溢出,我通常会在后面加-m 64M,把用户态缓冲区扩大到64MB。录制期间让测试人员正常播放音频,尽量让爆音出现两三次。

3.3.2 解读调度延迟:sched_switch与sched_wakeup

录制完成后,先在VMLINUX缺失的情况下使用perf script导出原始事件,再用perf sched timehist看任务调度的时序分布。

perf sched timehist能输出每个线程在每个CPU上的运行时间段。我特别关注音频应用线程的唤醒延迟,计算方法是从sched_wakeup事件时间戳到实际sched_switch切换回该线程的时间戳之差。这个差值正常情况下应该小于1毫秒,如果经常大于2毫秒到3毫秒,说明调度器响应慢了。

在我这次排查中,音频线程唤醒延迟的峰值达到了约4毫秒,而且频繁出现。这已经超过了缓冲区设计余量,基本可以断定爆音来自调度延迟。

3.3.3 抓出抢CPU的进程

继续深入看perf sched latency,这个命令会汇总每个任务的平均调度延迟、最大延迟和占用CPU时间。输出里出现了一个意外的名字:某个系统监控守护进程,它每2秒触发一次周期性任务,在部分采样周期里占用了单个CPU核接近70%的处理时间。

这就有点意思了。按理说这个进程优先级不高,不至于抢占音频线程。但问题出在它触发时会产生大量的softirq和线程唤醒,外加CPU调频策略在负载变化时反应不及时,几个因素叠加起来就把音频中断和唤醒路径给拖慢了。

3.3.4 关键证据:中断响应延迟

为了进一步确认,我专门过滤了音频DMA对应的中断事件:

perf script --no-inline -i perf.data | grep -E "irq_vectors|I2S"

通过比较连续的音频中断事件的实际间隔,发现正常时中断间隔非常均匀,但在爆音发生时,中断间隔突然拉大了一次,然后紧接着出现一个补偿性的短间隔。这个现象说明DMA IRQ本身被延迟执行了。

3.4 步骤四:结合tracepoint定位xrun来源

3.4.1 xrun计数与ALSA日志

调度延迟找到了,还得把它和音频侧的欠载事件关联起来。这时我打开ALSA层面的调试输出,在设备上执行:

echo 1 > /sys/module/snd_pcm/parameters/xrun_debug

这个参数打开后,每次发生xrun时,内核日志会打印一条包含underrun或overrun的记录。日志显示出现的是underrun,也就是DMA消耗速度超过了应用填充速度。

3.4.2 计算缓冲余量

接下来是算账的时间。假设采样率是48000Hz,格式为16bit双声道,那么每秒的数据量是:

48000 * 2 * 2 = 192000 字节/秒

如果缓冲区的buffer_size设置为4096帧,每个周期period_size为1024帧,那么缓冲区能容纳的时间长度是:

4096 / 48000 = 约85毫秒

每个周期触发一次中断的间隔是:

1024 / 48000 = 约21毫秒

这意味着应用最多有21毫秒的时间来填充下一个周期的数据。只要有一次调度延迟超过这个时间窗口,underrun就会发生。而前面实测的4毫秒延迟虽然单看不足以击穿21毫秒的周期,但如果系统在某个瞬间同时出现中断延迟、DMA拥塞和调度延迟叠加,就会超过阈值。

我进一步检查了驱动侧的实际运行参数,发现硬件设计上只用了较浅的DMA FIFO,而period_size配置相对偏小,导致中断频率过高,增加了延迟风险。最终我通过调整ALSA配置,把period_size提升到2048帧,同时修改内核启动参数里的调频策略为性能模式,爆音问题得到明显缓解。

3.5 步骤五:参数调整与复测验证

参数调整后,我重新执行了相同的录制流程,对比perf sched timehist的结果。音频线程唤醒延迟从4毫秒附近降到1毫秒以内,xrun计数不再增加。随后设备持续播放24小时,没有再现爆音。

4. 高频参数与命令速查

4.1 常用perf参数说明

参数组合用途典型场景
perf top -a -g -F 999全系统热点与调用栈快速摸底CPU占用
perf record -e sched:sched_switch -e sched:sched_wakeup -a -g抓取调度事件定位唤醒延迟与调度抖动
perf record -e irq:irq_handler_entry -e irq:softirq_entry -a抓取中断与软中断检查中断响应是否被拖慢
perf sched timehist展示任务时间线观察线程实际运行区间
perf sched latency汇总任务调度延迟找出延迟最大的任务
perf script导出原始事件流结合脚本做自定义分析

参数细节方面,-F设定采样频率,太小看不出瞬时抖动,太大又会导致自身CPU开销过高而干扰结果。嵌入式设备上我一般用500到1000Hz。-g开启调用链后,输出诊断信息明显变多,但分析根因的时候价值很大,宁可慢一点也要开。

4.2 音频调试相关的内核参数

参数/接口作用
/sys/module/snd_pcm/parameters/xrun_debug开启xrun事件日志输出
/proc/asound/card*/pcm*/info查看PCM设备的参数信息
/proc/asound/card*/pcm*/substream/*/hw_params查看运行时参数
snd_pcm.period_size应用层配置周期大小,影响中断频率
snd_pcm.buffer_size缓冲区总大小,影响可容忍的最大延迟
内核启动参数cpufreq调频策略影响中断和线程调度响应的及时性

在实际设备上调参数时,可以先在应用层通过ALSA API修改,确认有效后,再固化到配置文件里。不要一上来就改默认值,否则会分不清是哪个改动起了作用。

4.3 一条验证清单

每次改完参数,我会按这个清单复核:

  • 确认xrun_debug日志里不再出现新的欠载/过载记录。
  • 用perf sched timehist对比音频线程的最大唤醒延迟是否明显下降。
  • 检查中断间隔的分布是否更均匀,标准差是否变小。
  • 长时间回放测试听感是否稳定。
  • 最后再查看一次CPU整体占用,确认调整没有带来额外的性能开销。

5. 常见问题与避坑记录

5.1 权限不够,perf报错

遇到最多的报错是:

Error: You may not have permission to collect stats.

内核默认开启perf_event_paranoid,普通用户只能访问部分事件。调低安全级别可以在调试时临时使用:

echo -1 > /proc/sys/kernel/perf_event_paranoid

但是嵌入式设备重启后配置会失效,要么写进sysctl配置文件,要么用root用户执行。需要注意这个操作在生产环境要评估安全影响,调试完成之后就恢复原样。

5.2 采样事件太多,数据写入失败

录制长时间音频场景时,事件量可能非常大,perf.data文件会快速膨胀。遇到“failed to write"之类报错,先检查磁盘剩余空间,再用-m参数把环形缓冲加大到64MB甚至128MB。另外可以精简事件集合,先用调度事件,确有必要再加中断事件,避免一把梭。

5.3 内核符号表看不到snd驱动函数

perf无法解析snd_soc_*符号时,通常是因为内核没有导出符号或者符号表被压缩了。解决办法是检查内核配置里是否开启CONFIG_KALLSYMS,另外确保映射到调试用的vmlinux路径正确。在嵌入式设备上,交叉编译工具链里通常有一个vmlinux,通过--kallsyms参数指定路径即可:

perf report --kallsyms /path/to/vmlinux

如果跑的是压缩内核,要先用对应工具解压出完全符号表,再进行符号解析。这一步骤不复杂,但没有正确配置时容易让人误以为是perf本身的问题。

5.4 到底是内核问题还是应用问题

一个容易踩的误区是只盯着内核驱动看。如果排查发现唤醒延迟不高、中断也及时,但应用线程填充数据本身的间隔就不均匀,问题可能出在应用层解码或者网络读取上。区分方法很简单:用perf sched timehist看应用线程的运行持续时间,如果每次运行时间参差不齐,而唤醒延迟稳定,就去检查应用自身的工作负载。

还有一种情况是录音场景下的overrun。它和播放的underrun的成因方向相反,但排查手段完全对称,只需把关注点从“应用读得够不够快”改成“应用读得够不够快”。

5.5 定时器不准确导致buffer计算错误

有的开发板用高精度定时器做音频时钟源,但不同板卡上定时器频率有偏差。如果按照理论值计算缓冲区时长,实际上会和硬件时钟存在几个百分点的误差,长期运行时会出现缓慢累积的underrun。遇到这种问题,要在板卡上实测PCM时钟频率,用实测值重新计算缓冲配置,而不是直接信任数据手册。

5.6 别忘了看长时稳定运行结果

我最开始只是在爆音明显的时候跑了几分钟数据,修改配置后短测也通过了,本以为收工。结果长测一跑,问题又零星出现了一次。后来才发现是某个后台服务周期性运行,触发的时间跨度很长,短测根本覆盖不到。对于这类问题,一定要做至少数小时甚至24小时的持续抓取,否则很容易得出错误结论。

6. 写在最后:一点个人体会

6.1 经验的积累来自真实录音

这次排查给我最大的收获是:音频性能问题不是靠看代码“猜”出来的,而是靠观测工具“看”出来的。perf能非常客观地告诉你CPU在什么时间做了什么,snd框架则能告诉你音频链路在哪里丢了数据。把两条线索对上,根因自然就浮出水面了。

如果非要总结一套心法,我会说:先看全局热点,再录调度时序,然后对照中断间隔,最后算清缓冲区余量。这套流程我已经在不同设备上验证过多次,每次都能快速收敛,比漫无目的地修改内核参数要高效得多。

6.2 一个可以继续深挖的方向

这次用的是纯中断驱动的播放方式。如果设备需要更低延迟,还可以考虑PCM_MMAP配合忙轮询的方式,或者直接用ALSA的dmix插件做混音测试。另外,perf里还有很多tracepoint和PMU事件没来得及用,比如cache miss、branch miss,在更深层的音频算法优化阶段会派上用场。有朋友问过能不能把整套排查脚本自动化,我目前的做法是把常用命令写成一个shell脚本,配合perf.data的按时间分段分析,基本可以做到一键出报告。后续如果有机会,我会把脚本整理出来分享。

最后再分享一个小技巧:在调试之前,给音频线程设置明确的实时优先级,并且在perf record时用--tid只跟踪该线程,可以显著降低其它进程的干扰,让分析结果更加干净。这个做法对后续根因定位帮助非常大,强烈建议一试。

相关新闻

ppt-master:基于Python的演示文稿自动化生成工具,实现内容与样式分离

ppt-master:基于Python的演示文稿自动化生成工具,实现内容与样式分离

1. 项目缘起与整体设计思路1.1 为什么会有 ppt-master 这个想法做技术分享、项目汇报、课程讲解的人都有一个共同的痛点:内容本身已经想清楚了,但把内容变成一份拿得出手的演示文稿,往往要花掉比写内容本身更多的时间。我自己就经历过这种折磨…

2026/10/20 6:32:36 阅读更多 →
Gitee DevOps实战:从代码托管到一站式自动化交付

Gitee DevOps实战:从代码托管到一站式自动化交付

国内团队聊 DevOps,绕不开一个话题:代码托管平台选哪家。前几年大家习惯把代码往国外平台一放,省心省事,但这两年风向变了,越来越多企业把目光转回国内,Gitee 就是这轮讨论里被反复提起的名字。标题里说“王…

2026/10/20 6:32:36 阅读更多 →
进程与线程的创建与并发执行:操作系统实验核心解析

进程与线程的创建与并发执行:操作系统实验核心解析

1. 实验任务拆解:进程与线程的创建和并发执行,到底在验证什么刚做完操作系统课的第一个实验,题目是“进程、线程的创建与并发执行”,说实话刚开始觉得无非就是调用几个API的事,结果真正上手才发现水很深。这个实验看起…

2026/10/20 6:31:35 阅读更多 →

最新新闻

Repast多智能体建模实战:从SIR模型到Headless批量仿真

Repast多智能体建模实战:从SIR模型到Headless批量仿真

简介:一份针对 Repast 建模与仿真平台的英文原版教程与配套实例,面向社会科学、经济学及复杂系统领域的研究者,也适合希望快速上手多智能体仿真的学生和工程师。整个资源包内共包含72个文件,主体为66个HTML/HTM格式的教程页面&…

2026/10/20 7:11:54 阅读更多 →
用Python实现海明校验码:原理、代码与纠错实战

用Python实现海明校验码:原理、代码与纠错实战

海明校验码(Hamming Code)这个名词,学计算机、通信、嵌入式方向的朋友多少都见过。它属于那种看着简单、真自己上手写一遍才发现细节特别容易翻车的东西。我最早在项目里用到它,是做一个数据回传模块,想在软件层面给每…

2026/10/20 7:11:54 阅读更多 →
深入解析Lombok源码:javac编译期如何通过注解处理器修改AST生成代码

深入解析Lombok源码:javac编译期如何通过注解处理器修改AST生成代码

简介:面向Java开发者的Lombok实现原理源码资源包,围绕注解处理器、字节码操作与IDE集成展开,适合希望弄清楚Lombok为何能在编译期自动生成getter/setter、equals/hashCode、构造器等样板代码的读者。资源共40个文件,约88KB&#x…

2026/10/20 7:11:54 阅读更多 →
KAN网络与Stacking集成:五模型回归对比实战

KAN网络与Stacking集成:五模型回归对比实战

这两年做回归预测,圈子里的讨论慢慢从“要不要换模型”变成了“能不能把新模型塞进老框架”。我这次的项目标题很直白:把传统机器学习模型和新型KAN网络做Stacking集成,同时做一轮五模型回归对比。做出这个项目之前,我满脑子还是“…

2026/10/20 7:11:54 阅读更多 →
轨道交通延误下跳站与限流协同优化模型:建模、求解与避坑指南

轨道交通延误下跳站与限流协同优化模型:建模、求解与避坑指南

简介:这份资源面向城市轨道交通运营管理领域的研究人员、工程师及高校师生,围绕列车延误场景下的跳站与多车站客流控制协同优化问题,提供了一套双层线性规划模型的完整实现方案。上层以列车总延误最小为目标并考虑载客能力约束,下…

2026/10/20 7:11:54 阅读更多 →
改进双向人工势场引导RRT*的三维路径规划与Matlab实现

改进双向人工势场引导RRT*的三维路径规划与Matlab实现

先放一个结论:在三维空间里裸跑 RRT*,效果通常不会太好。我前两年做无人机巡检路径规划项目的时候,被这个经典算法折磨过:100100100 的地图,迭代 5000 次,首条可行路径拐了十几个弯,拿到手还得再…

2026/10/20 7:10:53 阅读更多 →

日新闻

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实现详解

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

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

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

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

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

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

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在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 阅读更多 →