news 2026/10/19 14:50:09

RK3588 CPU温度监控链路拆解:从TSADC硬件到驱动与sysfs

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RK3588 CPU温度监控链路拆解:从TSADC硬件到驱动与sysfs

刚开始接触 RK3588 的时候,我最先翻的不是 CPU 手册,而是散热相关的那几页。这颗8核 SoC 跑起来是真的“热情”,四颗 A76 大核加上 NPU 和 GPU 一起拉满,热密度一点不比台式机小。很多人以为温度监控就是读个 /sys/class/thermal/thermal_zone0/temp,但真正做驱动、做 BSP 的人都知道,这条链路从芯片内部温度传感器一直到用户态那条 sysfs 路径,中间隔着硬件采样、内核驱动、thermal 框架、策略决策、冷却设备执行好几层。这篇文章就顺着这条链路,把 RK3588 的 CPU 温度监控机制从硬件到驱动到应用层一层层拆开,中间会贴上我实际调试时用过的设备树配置、驱动关键代码和排查经验。适合正在搞 RK3588 板卡 Bring-Up、要做散热策略定制,或者想搞懂 thermal 框架怎么工作的朋友阅读。

1. 先搞清楚监控链路:从传感器到用户态到底走了多少环

1.1 RK3588 的发热压力与片上温度传感器布局

RK3588 是 8nm 制程,8 核 CPU 采用 4+4 的大小核架构(4×Cortex-A76 + 4×Cortex-A55),最高主频能跑到 2.4GHz 左右。同时还集成了 Mali-G610 GPU 和 6 TOPS 算力的 NPU,以及 8K 视频编解码模块。这几个大功耗模块在同一个 Die 上,满负载时整颗芯片的功耗能冲到 10W 以上甚至更高,如果不做温度监控和降频保护,结温很容易撞到 100℃ 以上的危险线。

芯片级温度监控靠的是内置的 TSADC(Temperature Sensor ADC),这是一颗片上模数转换器,专门用来采集芯片内部温度。RK3588 内部不止一个温度传感器,CPU、GPU、NPU、SOC 等关键模块都有独立的传感器通道,对应内核里多个 thermal_zone。这样设计的好处是,不同模块的发热节奏不一样,CPU 可能瞬间飙到高温,而 NPU 可能是在跑推理一段时间后才热起来。如果只用一个全局传感器,那就没法针对具体模块做精细的降频策略。

需要注意一点,TSADC 不是外部热敏电阻接进 GPIO 那种方案,它是完整的片上模拟前端。驱动要做的事情,本质上是配置好采样参数、启动转换、读取转换结果,再把 AD 码值换算成真实的温度值。至于温度阈值、降频动作、风扇控制,这些是在 thermal 框架上层完成的,驱动本身只负责“把温度读准”和“提供硬件保护机制”。

1.2 整条监控链路的一句话版

如果要用一句话把 RK3588 的 CPU 温度监控链路说清楚,那就是:TSADC 硬件持续采样 Die 温度,tsadc 驱动把采样码值换算成毫摄氏度,thermal 核心通过轮询或中断拿到这个温度,触发 governor 决策,最终调用 cpufreq、风扇等 cooling device 执行降温动作,同时把温度暴露在 /sys/class/thermal/ 下供用户态读取。

这条链路上参与的角色很多,我列个表,方便对照后面的内容:

链路角色对应实体职责
温度采样硬件片内 TSADC + 多通道传感器把温度转成 AD 码值
底层驱动rockchip tsadc 驱动配置寄存器、启动采样、读码值、换算温度
内核框架thermal core + thermal_zone管理传感器设备、汇总温度、执行策略
策略决策governor(step_wise / power_allocator 等)根据 trip point 决定要不要降频、降多少
执行机构cpufreq cooling device、fan PWM执行降频、开风扇等动作
用户态接口sysfs / thermal netlink让监控程序和应用拿到温度数据

搞懂这张表,再看驱动代码和设备树节点就不会晕了。很多人调试温度问题,一上来就盯着 cat /sys/class/thermal/thermal_zone0/temp 的值波动,却没意识到读数只是整条链路最后呈现出来的结果。如果某个环节配置错了,读数可能“看起来正常”但实际策略完全没生效,这种问题最坑。

2. 驱动之外的软件框架,必须先弄明白 thermal 子系统

2.1 内核 thermal 核心的角色划分

Linux 内核的 thermal 子系统不是为 RK3588 专门设计的,它是一个通用的电源与温度管理框架。理解这个框架,比单独看驱动代码重要得多。核心概念有三个:thermal_zone_device、thermal_cooling_device、governor。

thermal_zone_device 代表一个温度区域,比如 CPU 温度、GPU 温度、主板温度。它内部维护当前温度值、trip point 列表、轮询周期等状态。tsadc 驱动注册的就是这种设备,内核会为它创建一个 /sys/class/thermal/thermal_zoneN 目录。thermal_cooling_device 代表一种降温手段,可以是调节 CPU 频率、控制风扇转速、降低 GPU 频率等,对应 /sys/class/thermal/cooling_deviceN。governor 则是策略大脑,它拿到当前温度和 trip 点数据后,决定要不要让 cooling device 动作。

RK3588 平台上最常见的 governor 是 step_wise。这个策略的特点是“阶梯式”调节,温度超过一个 trip 点就上调一档冷却力度,低于 trip 点并越过迟滞区间就下调一档,避免在阈值附近来回抖。跑功耗敏感的服务器类产品时,也有人用 power_allocator,它会结合功耗预算和温度余量做更平滑的分配,但配置门槛也更高,需要在设备树里给每个 zone 配 sustainable-power 参数。

sysfs 里可以直接看到当前用的是哪个 governor:

cat /sys/class/thermal/thermal_zone0/policy

如果系统编译时选择了多个 governor,也可以在运行时切换。我调试时经常临时切到 user_space 模式,这样内核不做自动调节,方便复现高温场景,专心想驱动程序的问题。

2.2 设备树里的温度策略:trip 点和 cooling-map

温度策略的灵魂不在 C 代码里,而在设备树里。trips 定义“温度到了多少度要干什么”,cooling-map 把“温度事件”和“执行动作”关联起来。RK3588 平台默认的 CPU 温度 zone 大致长这样:

thermal_zones: thermal-zones { cpu_thermal: cpu-thermal { polling-delay = <1000>; polling-delay-passive = <100>; sustainable-power = <5000>; trips { cpu_alert0: cpu_alert0 { temperature = <55000>; type = "active"; hysteresis = <2000>; }; cpu_alert1: cpu_alert1 { temperature = <75000>; type = "passive"; }; cpu_crit: cpu_crit { temperature = <95000>; type = "critical"; }; }; cooling-maps { map0 { trip = <&cpu_alert0>; cooling-device = <&fan0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; }; map1 { trip = <&cpu_alert1>; cooling-device = <&cpufreq_cooling0 0 4>; }; }; }; };

这里有几个关键点。polling-delay 是主动轮询周期,单位是毫秒,指内核在没有中断触发时每隔多久重新读一次温度。polling-delay-passive 是当温度超过 passive trip 之后进入被动模式时的轮询周期,一般要设得更短,比如 100ms,这样温度爬升阶段响应更快。如果把这两个值设成 0,表示完全靠硬件中断触发,减少 CPU 开销。

trip 的 type 决定行为。active 意味着需要主动散热设备介入,比如开风扇;passive 意味着系统自身开始被动降温,典型动作就是降频;critical 是硬件级别的最后防线,内核收到后会触发系统紧急关机或者重启。hysteresis 是迟滞,单位也是毫摄氏度,设 2000 表示 2℃。它的作用前面提过,避免温度在阈值附近反复触发和释放。假设阈值是 55℃,没有迟滞,温度从 54.9 升到 55.1℃ 触发风扇,然后降到 54.9℃ 又关风扇,再升上去又开,这种抖动很伤风扇寿命。

2.3 冷却设备:cpufreq 与风扇怎么挂进来

冷却设备是 thermal 框架里的“执行者”。RK3588 平台上最常见的两个 cooling device 是 cpufreq cooling device 和风扇 PWM。

cpufreq cooling device 的注册一般由内核的 cpufreq_cooling 模块完成,设备树里通过类似 cpu-cooling-maps 的机制把 CPU 节点和 cooling 设备关联起来。它的原理不是直接改 cpufreq 的策略,而是通过注入 idle 周期来限制 CPU 的实际运行频率。内核会计算一个折算后的频率上限,然后通过调度器让 CPU 在时间片上“偷懒”,等效降低功耗和发热。这种方式的好处是不需要真的切换频率档位,响应快、粒度细。设备树 cooling-device = <&cpufreq_cooling0 0 4> 后面的两个数字是冷却状态的最小和最大档位,0 基本等于不限制,4 表示最多限制到比较低的档位,具体档位对应多少频率要看驱动里的映射表。

风扇是通过 PWM 或 GPIO 控制的。在 RK3588 参考设计中,风扇通常挂在 PWM 控制器上,驱动注册为一个 fan cooling device。active trip 一旦触发,governor 就会让风扇从低转速往高转速调。这里容易踩坑的地方是 PWM 频率和占空比映射关系没调好,导致风扇要么不转,要么一上来就全速。后面实操章节我会给一个排查思路。

3. TSADC 驱动的核心实现,逐段拆给你看

3.1 驱动如何找到并启动 TSADC

RK3588 的 TSADC 在芯片内部有一套独立的寄存器块,基地址一般在 0xFEC00000 附近(具体以芯片手册为准)。驱动要正常工作,首先得在设备树里找到对应的节点。节点的大致结构是这个样子:

tsadc: tsadc@fec00000 { compatible = "rockchip,rk3588-tsadc"; reg = <0x0 0xfec00000 0x0 0x100>; interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cru CLK_TSADC>, <&cru PCLK_TSADC>; clock-names = "tsadc", "apb_pclk"; resets = <&cru SRST_TSADC>; reset-names = "tsadc-apb"; pinctrl-names = "default"; pinctrl-0 = <&tsadc_shut>; #thermal-sensor-cells = <1>; status = "disabled"; };

compatible 字符串决定了驱动和节点的匹配。驱动侧用 platform_driver 注册,match 表里写上 "rockchip,rk3588-tsadc" 就能配对。reg 是寄存器物理地址和长度,interrupts 是中断号。clocks 和 resets 节点配置了工作时钟和复位信号,TSADC 这种模拟前端必须有时钟才能启动采样,APB 总线时钟负责寄存器读写。

驱动 probe 函数做的事很固定:从设备树解析资源、获取并使能时钟、复位模块、申请中断、初始化寄存器、最后把 thermal_zone_device 注册进 thermal 框架。这里有个细节,probe 阶段一定要检查时钟是否真的起来了。我遇到过一次时钟频率没配对,TSADC 采样出来的码值整体偏移,温度误差超过 10℃。时钟是模拟 IP 的“心跳”,频率不对整个采样都是错的。

status = "disabled" 的节点,如果板级设备树里没有重新 status = "okay",驱动不会被加载。这是 RK3588 平台上最常见的设备树问题。检查方法很直接:

cat /sys/firmware/devicetree/base/tsadc@fec00000/status

读到 disabled 就说明节点没使能。

3.2 温度采样与 AD 码转换

TSADC 内部完成一次温度采样后,把结果放在数据寄存器里。驱动要拿到温度,核心逻辑就是从数据寄存器读出原始码值,再转换成真实的毫摄氏度温度。

转换方式取决于 TSADC 的传感器类型和模拟前端设计。RK3588 的 TSADC 采样结果和温度不是绝对线性关系,原厂驱动里通常采用分段线性近似或者查表法。简化后的数据结构大致是:

static const struct tsadc_conv_table rk3588_conv_table[] = { { .code = 810, .temp = 25000 }, /* code 810 对应 25℃ */ { .code = 770, .temp = 40000 }, /* code 770 对应 40℃ */ { .code = 720, .temp = 65000 }, /* code 720 对应 65℃ */ { .code = 650, .temp = 100000 }, /* code 650 对应 100℃ */ };

驱动在运行时会根据采样到的 code 落在哪两个标定点之间,做线性插值,得出对应的温度。这种做法的好处是标定点可以按芯片实测特性来调整。很多 BSP 后期做量产标定时,就是改这张表。如果你在调驱动,温度偏差过大,先怀疑这张表跟自己板子上的芯片特性是否匹配,不要急着改上层策略。

代码上的一次温度读取回调,核心内容大致长这样:

static int rk3588_tsadc_get_temp(void *devp, int *temp) { struct rk3588_tsadc *tsadc = devp; u32 code; int i; /* 读取采样结果寄存器,屏蔽无用的高位 */ code = readl_relaxed(tsadc->base + TSADC_DATA); code &= TSADC_DATA_MASK; /* 先查表找分段区间,再做线性插值 */ for (i = 0; i < TABLE_SIZE - 1; i++) { if (code <= table[i].code) { int frac = (table[i].temp - table[i + 1].temp); int range = (table[i].code - table[i + 1].code); *temp = table[i].temp - frac * (table[i].code - code) / range; return 0; } } /* 超出表范围时,按边界值处理 */ *temp = table[TABLE_SIZE - 1].temp; return 0; }

注意这里读寄存器用了 readl_relaxed,没有加内存屏障。这是有意为之,因为温度寄存器是终端寄存器,不涉及 DMA 一致性等问题,普通读操作足够,加屏障反而增加无谓开销。

换算出的温度单位是毫摄氏度,25℃ 就是 25000。用户态读到的 /sys/class/thermal/thermal_zone0/temp 也是这个单位,转成摄氏度除以 1000 就行。

3.3 温度阈值中断与硬件保护机制

轮询模式适合调试,但不适合做紧急保护,因为软件进程可能被调度延迟,中断才是硬件级响应的正道。RK3588 的 TSADC 支持设置高低温阈值,一旦采样值越过阈值,硬件直接触发中断,驱动在中断回调里上报 thermal 框架。

中断处理的简化逻辑:

static irqreturn_t rk3588_tsadc_irq(int irq, void *data) { struct rk3588_tsadc *tsadc = data; u32 isr; isr = readl_relaxed(tsadc->base + TSADC_ISR); if (isr & TSADC_HIGH_IRQ) { /* 高温中断:上报 thermal core,立刻进行降温决策 */ thermal_zone_device_update(tsadc->tz, THERMAL_EVENT_UNSPECIFIED); writel_relaxed(TSADC_HIGH_IRQ, tsadc->base + TSADC_ICR); } if (isr & TSADC_LOW_IRQ) { /* 低温中断:同理,清理中断状态 */ writel_relaxed(TSADC_LOW_IRQ, tsadc->base + TSADC_ICR); } return IRQ_HANDLED; }

这段代码里最容易被忽略的是末尾的清中断操作。ISR 寄存器读到的状态位,必须通过写 ICR(中断清除寄存器)来清零,如果漏了这一步,中断标志一直挂着,中断处理函数就会被反复触发,造成所谓的中断风暴。我见过不止一个工程师在这里翻车,现象是系统负载正常情况下,CPU 却一直被 IRQ 打断,整体性能下降。

更硬核的保护机制是 TSADC 的 shutdown 模式。设备树里 pinctrl-0 = <&tsadc_shut> 配的就是这个功能。当温度超过硬件编程的极限值后,TSADC 会直接控制芯片的关机引脚,硬件层面触发 SoC 掉电保护。这个动作不经过 CPU 执行软件,所以即使内核已经 hang 住,它依然能保护芯片不被烧毁。量产产品的散热兜底基本都靠这一层,软件降频只是第一道防线。

4. 实操:让监控真正跑起来,并读对温度

4.1 确认工作在哪个 thermal zone

RK3588 有多个温度传感器,内核会为每个传感器创建一个 thermal_zone。系统起来之后,先看一下到底注册了哪些 zone:

ls /sys/class/thermal/ for z in /sys/class/thermal/thermal_zone*; do echo -n "$z: " cat $z/type done

type 文件会告诉你这块 zone 对应哪个传感器。常见的名字是 cpu-thermal、gpu-thermal、soc-thermal,但也有平台把名字起得比较含糊,比如叫 tsadc-thermal。这时候不要靠猜,可以用 dmesg 看驱动注册日志:

dmesg | grep -i tsadc

日志里会把每个 zone 的注册顺序、传感器通道、温度类型打出来。确认了 zone 编号后,再读温度:

cat /sys/class/thermal/thermal_zone0/temp

返回的是毫摄氏度。比如输出 45781,意思就是 45.781℃。

4.2 命令行快查及各频率、负载联动观察

只读一个静态温度没意义,温度监控的价值在于看它随负载变化的动态过程。我会这么测:先用 stress-ng 拉满 CPU,一边观察温度一边观察频率,看 thermal 策略有没有正确介入。

stress-ng --cpu 8 --cpu-method matrix --timeout 60 & while true; do t=$(cat /sys/class/thermal/thermal_zone0/temp) f=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq) echo "$(date +%T) temp=${t} freq=${f}" sleep 1 done

CPU 频率单位是 kHz,比如 1800000 就是 1.8GHz。跑起来之后观察几点:温度有没有快速爬升到 passive trip;温度到了之后频率有没有被 clamp 住;温度稳定时曲线有没有在某个值附近形成平台。如果温度一直涨不停、频率纹丝不动,问题多半出在 cooling map 没有生效。

如果需要记录温度曲线做进一步分析,可以写一个简单脚本,输出 CSV 格式到文件:

#!/bin/bash echo "time,temp_cpu,temp_soc,freq0" > /tmp/thermal_log.csv while true; do t0=$(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null) t1=$(cat /sys/class/thermal/thermal_zone1/temp 2>/dev/null) f0=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 2>/dev/null) echo "$(date +%s),$t0,$t1,$f0" >> /tmp/thermal_log.csv sleep 1 done

跑完 stress 之后 Ctrl-C 停掉,把 CSV 拖到 Excel 或者画图工具里,曲线一眼就能看出问题。这个习惯我强烈建议养成,温度问题靠肉眼看终端输出的数字,很难发现采样噪声和策略振荡。

4.3 调策略:轮询间隔、被动冷却与风扇联动

调试时最常改的两个参数是轮询间隔和 trip 阈值。轮询间隔可以直接在 sysfs 里调:

cat /sys/class/thermal/thermal_zone0/policy echo step_wise > /sys/class/thermal/thermal_zone0/policy

policy 是 governor 选择。默认平台可能跑的是 step_wise,如果你想验证风扇动作,临时切到 user_space 就是把自动降温逻辑关掉,只保留手动控制。

trip 点的查看和修改在 sysfs 里对应一组文件:

cat /sys/class/thermal/thermal_zone0/trip_point_0_temp cat /sys/class/thermal/thermal_zone0/trip_point_0_type

不过要注意,sysfs 里这个值不一定能直接改,很多时候它是只读的,真正要改阈值还是得走设备树。修改流程是:改 dts 文件里的 trip 温度值,重新编译 dtb,然后刷到板子上。对 RK3588 来说,编译 dtb 通常走 Buildroot 或 SDK 的编译命令:

make dtbs

改完设备树后,先确认新 dtb 真的加载了。有的引导加载程序会有缓存,或者烧录工具没把新 dtb 刷进对应分区,导致你改了半天的 trip 温度根本没生效。检查方法是重新读:

cat /sys/firmware/devicetree/base/thermal-zones/cpu-thermal/trips/cpu_alert0/temperature

注意这里读出来是字节数组,数值要自己换算一下。如果值和设备树里一致,说明新配置生效了。

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

5.1 问题速查表

这一路调试下来,我遇到过的问题基本可以归成下面这几类,直接整理成速查表:

现象可能原因排查方向
thermal_zone 没出现或数量不对设备树节点 status 为 disabled检查 tsadc 节点使能状态
温度读出来总是固定值采样使能没写正确,或时钟没起检查寄存器 TSADC_CR 中的 ADC 使能位
温度比实际高很多换算表系数和芯片不匹配对比多块板子的输出,做标定校正
温度跳变剧烈轮询周期过短或传感器噪声增大轮询间隔,或者检查采样滤波配置
频率到了阈值却不降cooling map 没绑定 cpufreq_cooling检查 cooling-device 的 phandle 指向
风扇不转或转速失控cooling map、PWM 极性或频率问题先用 sysfs 手动控制风扇确认 PWM 通路
温度临近临界值直接关机critical trip 触发或硬件 shutdown确认是软件策略还是 TSADC shut 引脚动作
中断风暴导致 CPU 负载高中断状态未清除检查 ISR/ICR 清除逻辑
修改 DTS 后行为不变dtb 没重新编译或没刷入检查 dtb 分区和实际加载地址

这张表基本覆盖了 90% 的 RK3588 温度调试问题。遇到具体现象时,先判断问题出在链路哪一段,别一上来就怀疑芯片本身。绝大多数温度异常都是配置问题或者换算问题,真正的硬件损坏概率很低。

5.2 调试心得:不要盲信传感器

最后分享几条硬体会。第一,刚拿到板子时不要直接相信温度传感器的绝对精度。TSADC 出厂特性有离散性,同一型号芯片不同个体在同样工况下,读数可能差 3℃ 到 5℃。所以调试初期我更关注温度曲线的相对变化趋势,判断策略有没有生效,而不是死磕绝对值。等到产品的散热方案定型,再考虑用外部热电偶做一次标定,修正换算表。

第二,高温保护阈值不要只依赖 critical trip。软件链路再快也有延迟,真正到了临界温度再处理已经晚了。我会保留 TSADC 的硬件 shutdown 机制,同时在 passive trip 和 critical trip 之间留足余量。比如 passive 设 75℃,critical 设 95℃,中间 20℃ 的缓冲足够完成降频动作。

第三,电源和散热是一体两面。有时降低处理器电压比降频更有效,但这属于更深的调优范围,不在温度驱动层面。驱动能把温度读准、策略能正常执行,已经解决了 80% 的问题。

我最近调试的一块 RK3588 核心板,最头疼的反而不是驱动本身,而是风扇策略。温度已经超过 active trip 了,风扇却迟迟不启动,后来发现是 cooling map 里 fan 设备的 phandle 指错了节点。这个问题的隐蔽之处在于,sysfs 里 fan 的 cooling_device 节点已经存在,看起来一切正常,但 cooling map 绑定的是另一颗 PWM 控制器,风扇自然不响应。所以排查顺序一定是从下往上:先确认传感器读数真实,再确认冷却设备能手动控制,最后才检查策略映射。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 14:46:27

高性能计算平台运行时内存协同与异步流控机制解析

上次压测一套分布式数据处理管道&#xff0c;8个节点的时候吞吐量能稳定在480万条每秒&#xff0c;一扩到12个节点反而掉到了270万&#xff0c;网络利用率只有40%&#xff0c;CPU也没跑满&#xff0c;定位了半天才发现&#xff0c;瓶颈根本不在网卡和算力&#xff0c;而在运行时…

作者头像 李华
网站建设 2026/10/19 14:42:03

自研浏览器端图表设计器:数据模型、渲染交互与性能优化实战

diagram-design 这个项目&#xff0c;源头是一个很现实的问题&#xff1a;我们内部的流程文档、系统拓扑图、架构说明图越来越多&#xff0c;静态图改一次要重新画、重新截图、重新上传&#xff0c;几乎没人愿意维护。团队里也不是没试过现成的绘图工具&#xff0c;但接入之后总…

作者头像 李华
网站建设 2026/10/19 14:40:04

断网私有部署实战:依赖梳理、故障排查与交接指南

1. 断网场景下的私有部署到底在解决什么问题“沈阳私有部署&#xff0c;断网谁接手”这个标题第一次看到的时候&#xff0c;我脑子里蹦出来的不是技术架构图&#xff0c;而是一个很具体的画面&#xff1a;机房里网络指示灯突然全灭&#xff0c;运维群里开始刷屏&#xff0c;业务…

作者头像 李华
网站建设 2026/10/19 14:37:23

YOLOv8接入ASFF:多尺度目标检测小目标AP提升实践

做目标检测的朋友对YOLOv8应该不会陌生。我最近在做一个工业零件检测项目&#xff0c;目标尺度跨度很大&#xff0c;小零件最小只有十几像素&#xff0c;大零件几乎占了半张图。用YOLOv8s训练到一百多轮&#xff0c;整体mAP能看&#xff0c;但小目标AP一直卡在23左右&#xff0…

作者头像 李华
网站建设 2026/10/19 14:36:45

Python+Django+Vue3实现协同过滤推荐零食商城系统全解析

零食商城做推荐系统&#xff0c;这件事本身听起来没有多稀奇&#xff0c;但是真到动手实现的时候&#xff0c;你才会发现里面全是细节。很多人一上来就要搞协同过滤&#xff0c;觉得这是个“算法项目”所以很高端&#xff0c;结果连用户行为数据从哪来、商品之间的相似度怎么算…

作者头像 李华