先说一个我自己的真实经历。前两年给客户做一台基于 RK3588 的边缘 AI 盒子,部署在园区配电房做 YOLOv8 安全帽检测,设备放在弱电井里,7×24 跑推理。头一个月一切正常,第二个月凌晨三点,值班电话打过来——画面全黑,设备失联。我驱车来回两小时,结果到现场一看,机器没死,是 NPU 卡死了,推理线程阻塞,系统负载飙到 20 多,网络假死。按一下复位,好了。但这个场景发生在偏远站端呢?来回一次的成本够买三台设备。从那以后,我开始认真研究 RK3588 平台的“不死机”问题,陆陆续续做了三版方案,后来慢慢沉淀成一套完整的守护机制,我管它叫 Guardian。今天把整套思路、配置、踩过的坑全部摊开讲,希望能帮到正在做边缘 AI 设备的朋友。
适合谁看?如果你正在做基于 RK3588 的AI盒子、边缘计算终端、工业视觉设备,或者已经遇到了设备长时间运行后死机、NPU 卡死、SSH 假死这类问题,这篇文章应该能帮你省掉至少两个月的试错时间。
1. 先说结论:RK3588 边缘设备为什么需要“守护”,而不是“运维”
很多做产品的朋友一上来就讨论散热、电源、看门狗,方向对,但格局小了。RK3588 这块芯片本身是非常成熟的,8nm 工艺、四核 A76 + 四核 A55、6 TOPS NPU,算力在边缘侧完全够用。7×24 不死机的难点从来不在芯片,而在芯片之外的整个“系统生态”。
1.1 三个真实场景告诉你“不死机”有多难
第一个场景:工业检测。设备挂在产线旁边,环境温度 40℃ 以上,旁边还有电焊机、变频器这类强干扰源。RK3588 的 NPU 满载跑 YOLOv8,整板功耗能摸到 15W 以上,散热稍跟不上,芯片温度直奔 85℃。温度一高,CPU 降频,推理帧率掉一半,客户以为你算法不行,其实你已经快死机了。
第二个场景:无人店 / 自助终端。这种场景往往是市电不稳,晚上电压波动大,设备用着用着突然断电,或者“半断电”——电压忽高忽低,导致 eMMC 写入出错。第二天开机,系统起不来,卡在 kernel panic,只能派工单上门刷机。
第三个场景:车路协同边缘站。设备放在户外机柜,网络是 5G CPE,偶尔断网是常态。断网期间,软件里的网络重连逻辑如果写得不好,线程会不断堆积,内存慢慢涨,几天后 OOM,系统自动杀掉关键进程,然后整机进入假死状态。
这三个场景说明一个问题:边缘 AI 设备的不死机,不是靠某一个看门狗芯片就能解决的,而是一整套从硬件到内核再到应用的分层兜底机制。单纯靠远程运维去“救火”,成本高、时效差,用户根本等不起。
1.2 死机是表象,根因都在这些位置
我统计了自己手上 RK3588 设备的故障记录,大概 80% 的“死机”其实不是真正意义上的硬件死机,而是下面这几种情况:
- NPU 卡死:RKNN 推理进程在调用
rknn_run时长时间不返回,或者返回了错误的错误码但应用层没处理,导致进程不死不活。 - 内核锁死:常见于某些外设驱动异常,比如 USB 摄像头掉线后内核在等待 USB 枚举超时,或者 MIPI-CSI 信号异常导致 dmesg 刷屏,把系统 IO 拖垮。
- OOM 被杀:Python 推理进程内存泄漏,跑几天后 RSS 涨到几个 GB,触发内核 OOM killer,关键进程被杀。
- 文件系统写满或损坏:日志没做轮转,eMMC 被写满;或者异常掉电导致 overlayfs/ext4 元数据损坏。
有意思的是,这些根因在实验室里都很难复现,因为实验室环境太“干净”了。真正到了现场,电网波动、高温、电磁干扰、断网、人为误操作,各种因素叠加,才会暴露问题。
1.3 Guardian 守护的设计目标与分层思路
我定义 Guardian 的目标很简单:即使应用层已经“感知”不到系统了,设备也必须能在无人干预的情况下自动恢复,而且恢复时间越短越好。理想状态下,最坏情况是 3 分钟内重启恢复,期间如果用户询问,运维人员只需要说一句“设备自愈了”就行。
为了实现这个目标,我采取的是分层兜底架构:
| 层级 | 守护手段 | 解决的问题 |
|---|---|---|
| L0 硬件层 | 硬件看门狗、电源保护、温控 | 系统崩溃、电池/电源异常 |
| L1 内核层 | 内核 panic 自动重启、EDAC 内存错误 | 内核级死锁、硬件错误 |
| L2 系统层 | systemd 服务健康检查、日志兜底 | 关键服务异常退出 |
| L3 应用层 | 进程守护、心跳上报、RKNN 超时恢复 | 推理进程卡死、内存泄漏 |
核心思路是:每一层都只兜住自己下面的那一层,不要把宝押在单一机制上。比如你写了很好的应用层守护,但内核已经 panic 了,应用层代码再漂亮也没用,必须靠硬件看门狗把系统拉起来。反过来,你不能只靠硬件看门狗,因为它能做的只是“重启”,重启之后应用层问题依旧存在,设备会陷入反复重启的死循环。
下面我把每一层的具体做法拆开讲,能贴配置贴配置,能贴代码贴代码,都是实测验证过的。
2. 硬件看门狗 + 软件看门狗:把底兜住
看门狗是整套 Guardian 的地基。如果地基打不好,上面盖什么楼都没用。RK3588 内部自带硬件看门狗,DW_wdtIP,在 Linux 下对应dw_wdt驱动,设备节点是/dev/watchdog。这个看门狗一旦启动,系统就必须周期性去“喂狗”,否则硬件强制复位。
2.1 devicetree 里开启硬件看门狗
很多 RK3588 开发板出厂默认 watchdog 是 disabled 的,因为默认固件不启用喂狗服务,开了不喂会一直重启。所以第一步是在内核 devicetree 里把它打开。以 RK3588 的 dtsi 节点为例:
&wdt { status = "okay"; timeout-sec = <16>; };timeout-sec我建议设置在 10~16 秒之间。太短了,系统高负载时喂狗线程本身被调度延迟,容易误触发重启;太长了,真死机时要等半天才复位,业务中断时间太长。16 秒是我测试下来比较稳的值。
然后是 Linux 内核配置,需要确保这几个 config 打开了:
CONFIG_WATCHDOG=y CONFIG_DW_WATCHDOG=y CONFIG_WATCHDOG_CORE=y如果用的是 Rockchip 的 SDK,默认一般都有。但如果你是自己 build 的 mainline 内核,务必检查一下,缺了这两个 config,后面的所有看门狗配置都是白搭。
2.2 systemd 软件看门狗怎么配合
硬件看门狗只是最后一道保险,正常运行时,我们更希望用 systemd 的软硬结合机制来提前发现异常。
systemd 本身内置了 watchdog 支持,可以让 PID 1 周期性去喂硬件看门狗。只要 systemd 还活着,系统就被认为是健康的。配置在/etc/systemd/system.conf:
RuntimeWatchdogSec=10 ShutdownWatchdogSec=60RuntimeWatchdogSec=10的意思是 systemd 每 10 秒向/dev/watchdog喂一次狗。这个值必须小于 devicetree 里的timeout-sec(16 秒),否则喂狗间隔比硬件超时还长,系统会被误杀。ShutdownWatchdogSec=60是关机和重启阶段给系统更多时间去卸载文件系统、停服务,避免关个机还被看门狗打断。
配置完,重启 systemd 或者整机重启后,可以用下面命令确认喂狗生效:
cat /dev/watchdog正常情况下这条命令会打开设备节点并触发一次喂狗,然后会报错退出,这反而说明驱动正常。更靠谱的验证方式是:
watch -n 1 cat /sys/devices/platform/watchdog/watchdog0/state也可以直接禁掉喂狗服务,手动观察设备会不会在 16 秒后自动重启。我建议每一批板子出厂前都做一次这个测试,确保硬件看门狗是真实可用的,而不是 devicetree 里写了“okay”但实际没生效。
2.3 看门狗喂狗的正确姿势
这里有个关键细节:喂狗不是在应用层随便写个死循环就行的,必须设计成“健康检查通过才喂”。我见过太多团队,写一个while(1) { write(fd, "w", 1); sleep(5); }的线程一直喂,等于自己把看门狗废了——任何应用层故障都不会触发重启,因为你的喂狗线程还活着。
我之前在一套方案里做的是分级喂狗:
- 喂狗线程每 10 秒读取一次系统健康状态:关键进程是否存活、内存是否够用、最近的 dmesg 里有没有驱动报错。
- 只有所有检查项都通过,才写入
/dev/watchdog。 - 任一检查项失败,停止喂狗,等硬件看门狗超时自动复位。
伪代码大致长这样:
import fcntl import os import time WATCHDOG_FD = os.open("/dev/watchdog", os.O_WRONLY) def system_healthy(): # 1. 关键进程存活检查 if not check_process("rknn_infer"): return False # 2. 内存检查:可用内存低于 200MB 判定为异常 if available_memory_mb() < 200: return False # 3. 最近 60 秒 dmesg 是否出现连续驱动错误 if recent_dmesg_has_errors(): return False return True while True: if system_healthy(): os.write(WATCHDOG_FD, b"w") time.sleep(10)注意,这里我刻意把这个喂狗逻辑做成了独立进程,而不是放在主业务进程里。为什么?因为主业务进程是最容易卡死的,如果喂狗逻辑跟着它跑,卡死就一起卡死,守护就失去了意义。用 systemd 把喂狗进程拉起来,加Restart=always,即便这个进程本身崩了,systemd 也会把它重新拉起来继续喂——当然,如果 systemd 都崩了,还有硬件层兜底,这是双保险。
3. 应用层守护与 RKNN 推理异常自恢复
看门狗解决的是“系统级死机”,但实际项目里更常见的是“应用级卡死”。系统没死,SSH 能连,top 能看到进程还在,但 NPU 推理线程卡住不动了,画面一直停在那。这种问题靠看门狗是发现不了的,必须靠应用层自己的健康检查机制。
3.1 进程守护与心跳
我在方案里给每一个关键业务进程都配了 systemd service,统一加了Restart=always和健康检查。以推理服务为例:
[Unit] Description=RKNN Inference Service After=network-online.target rknn.service [Service] Type=simple ExecStart=/usr/bin/rknn_infer --config /etc/guardian/rknn_infer.yaml Restart=always RestartSec=5 WatchdogSec=30 StartLimitIntervalSec=60 StartLimitBurst=5 [Install] WantedBy=multi-user.targetRestart=always保证进程退出后 5 秒内被拉起来。但光这样不够,因为前面说的“卡死”不是进程退出,而是进程还活着但无响应。所以这里又用了WatchdogSec=30——systemd 会要求服务每 30 秒调用一次sd_notify(WATCHDOG),如果超时,systemd 会认为这个服务已经“僵死”,杀掉并重新拉起。
服务内部需要周期性地维护看门狗通知。C++ 里用sd_notify,Python 里用systemd库或者直接往/run/systemd/notify写消息:
import socket import time NOTIFY_SOCKET_PATH = "/run/systemd/notify" def notify(): sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) sock.connect(NOTIFY_SOCKET_PATH) sock.send(b"WATCHDOG=1") sock.close() while True: # 执行一帧推理 result = rknn_run() if result is not None: notify() time.sleep(1)这个机制的意义在于,只要推理主循环还能跑完一帧,我们才认为服务健康;如果一帧推理超过 30 秒没完成,即使进程还在,systemd 也直接把它干掉重来。实测下来,这套机制对“NPU 卡死但进程还活着”的场景特别有效。
3.2 RKNN 运行时的超时检测与自动重初始化
RKNN 的rknn_run默认是同步阻塞调用的,如果 NPU 内部出现了异常,这个调用可能永远不返回。你写再多的心跳,代码也走不到心跳那里。
所以我在封装 RKNN 推理时,会把推理放到独立线程里,用带超时的 join 去检测,一旦超时就认为 NPU 已卡死,进入恢复流程:
std::future<cv::Mat> future = std::async(std::launch::async, [this] { return infer(frame); // 内部调用 rknn_run }); if (future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) { // 推理超时,判定 NPU 卡死 recover_from_npu_hang(); return; } cv::Mat result = future.get();恢复流程recover_from_npu_hang()做几件事:先把旧的推理进程退出,调用rknn_destroy释放 NPU 上下文(如果还能调用的话),实在不行就直接exit(0)让 systemd 重启整个服务。然后把/dev/rknpu设备节点重新 bind/unbind 一次,把 NPU 驱动复位:
echo 0 > /sys/class/misc/rknpu/device/remove echo 1 > /sys/class/misc/rknpu/device/remove注意,/dev/rknpu设备节点的路径在不同 SDK 版本里可能不一样,有的叫rknpu,有的叫mpp_service,需要先确认一下你们板子的实际情况。复位 NPU 驱动后,再重新rknn_init,加载模型,继续推理。
这个过程最好能自动完成,不需要重启整机,恢复正常的时间控制在 10 秒以内,业务中断的感知就很小了。
3.3 进程崩溃后的现场保留
进程崩溃后,第一件事不是急着拉起来,而是把现场保护好。我只说一个点:coredump 一定要开,但要写到内存盘上,不能写 eMMC。否则每次崩溃都往 eMMC 写几百 MB core 文件,写几次 eMMC 就废了。
我的做法是:
# /etc/systemd/coredump.conf [Coredump] Storage=external Compress=yes # 把 coredump 临时保存到 /tmp(tmpfs),重启后自动清空配合挂载一个 tmpfs 到/var/log/coredump:
tmpfs /var/log/coredump tmpfs defaults,size=256m,mode=0755 0 0这样崩溃时 core 文件先落内存盘,不影响 eMMC;如果现场非常重要,再通过网络把 core 传到远端。很多团队的设备一到现场就频繁崩溃,但 Log 里什么都没有,就是因为 coredump 没开,或者开了直接写坏存储。
4. 散热与供电:防止“慢性死亡”
讲完了看门狗和应用层守护,可能有人觉得够了。但我的经验是,真正的“不死机”,有一半功夫要花在散热和供电上。白天 NPU 满载还好,到了满载几个小时后,如果没有合理的温控策略,RK3588 会热到降频,甚至直接高温关机。这种问题不是突发性的,而是“温水煮青蛙”——系统越来越慢,最后悄悄死给你看。
4.1 PWM 风扇控制与转速读取
RK3588 开发板一般都有 PWM 风扇接口,内核驱动是pwm-fan。我在设备树里是这样配的:
&pwm13 { status = "okay"; pinctrl-names = "active"; pinctrl-0 = <&pwm13m1_pins>; }; fan: pwm-fan { compatible = "pwm-fan"; #cooling-cells = <2>; pwms = <&pwm13 0 50000 0>; /* 50kHz 频率 */ cooling-min-state = <0>; cooling-max-state = <255>; };这里的核心参数是pwms里的 period,也就是 PWM 频率。不同风扇对频率的要求不同,但我测过的大部分 4 线 12V 风扇在 25kHz~50kHz 之间都能正常工作。太低的频率(比如 1kHz)会有明显噪音,太高的频率(100kHz 以上)风扇驱动芯片可能跟不上。
然后是读取风扇转速。很多 4 线风扇的转速反馈引脚(FG)可以直接接 RK3588 的 GPIO,用pwm-capture或者 GPIO 中断的方式测量。但我更推荐用hwmon的方式,RK3588 的 SDK 里通常已经支持了风扇转速的 hwmon 节点:
cat /sys/class/hwmon/hwmon0/fan1_input如果输出一个 RPM 数值,说明测速功能正常。如果没有这个节点,就需要检查一下风扇的 FG 引脚是否接到了正确的 GPIO 上,以及内核有没有开CONFIG_SENSORS_PWM_FAN或者你们需要的gpio-fan驱动。
转速读取的价值不只是让你知道风扇转没转,更关键的是可以做风扇失效检测。当 PWM 输出已经 100% 但转速始终为 0,说明风扇卡住了或者掉线了,必须立即告警,否则芯片温度会直线飙升。
4.2 温控策略:别让芯片扛到最后
RK3588 芯片的结温上限一般是 105℃ 左右,但长期跑在 85℃ 以上,不仅加速器件老化,还有可能触发内部热保护直接断电。我的温控策略是在用户空间写一个守护线程,直接读 SoC 温度做分级控制:
import os import time def read_temp(): with open("/sys/class/thermal/thermal_zone0/temp") as f: return int(f.read().strip()) / 1000.0 def set_fan_duty(duty): # 通过 pwm-fan 的 cooling device 或者 sysfs 设置占空比 with open("/sys/class/hwmon/hwmon0/pwm1", "w") as f: f.write(str(duty)) while True: temp = read_temp() if temp < 45: set_fan_duty(0) # 停转,安静省电 elif temp < 60: set_fan_duty(80) # 低转速 elif temp < 75: set_fan_duty(160) # 中转速 else: set_fan_duty(255) # 全速压温度 time.sleep(5)阈值可以根据实际机型微调,但思路是留出冗余:在 75℃ 就已经全速散热,不要等到 90℃ 才慌。另外,如果你的设备是静音优先的场景,可以考虑把 45℃ 这个停转阈值调高到 50℃,但一定要配合风扇失效检测,否则夏天必出事。
还有一点,RK3588 有thermal-zones的被动降频策略,建议在 devicetree 里打开,让内核在温度过高时主动给 CPU/GPU 降频,而不是硬扛:
&thermal_zones { soc_thermal { trips { cpu_cooling: cpu_cooling { temperature = <75000>; hysteresis = <5000>; type = "passive"; }; }; cooling-maps { map0 { trip = <&cpu_cooling>; cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; }; }; }; };4.3 供电与硬件防护
软件做再好,供电不行全白搭。RK3588 在满负载时核心电压对电源纹波极其敏感,我碰到过一个案例:设备白天跑得好好的,一到晚上就随机重启,查了半天,最后发现是客户配的劣质 DC 适配器纹波超标,电压一波动芯片就复位。
所以硬件选型上,电源部分至少要满足三个要求:
- 输入电压要留足余量:标称 12V 供电,实际要能扛住 9V~16V 的波动。很多开发板用的 DC-DC 方案输入范围就是 9~24V,但有些精简方案只在 12V 附近能稳住。
- 输出纹波要小:CPU 核心供电(VDD_CPU)的纹波最好控制在 50mV 以内,否则高频负载变化时系统会随机复位。
- 要有过压/反接保护:乡村、工控现场的市电环境很差,插错电源是常事,硬防护必须做。
如果成本允许,建议加一个简单的电源监控芯片,比如 INA226,在应用层定期读电压电流,一旦发现输入电压异常波动就主动告警,把问题暴露在“死机之前”。
5. 存储保护与日志策略:延长 eMMC 寿命,防止日志写爆
很多 RK3588 设备出厂配的是 eMMC,TLC 颗粒,擦写寿命大概在 3000~5000 次 P/E 之间。如果系统跑一些频繁写日志的软件,每天写几十 GB,eMMC 很快就废了,然后就是各种诡异故障:文件系统只读、应用启动报错、系统随机重启。这些都是慢性病,等到你发现的时候,往往只能返厂换板了。
5.1 根文件系统只读化:能读就不写
我的设计原则是:能只读的绝对不读写。根文件系统挂载为只读,/var、/tmp、/run这些动态目录挂到 tmpfs,所有需要持久化的数据集中到/data分区。
具体做法是,把根文件系统做成 squashfs 或者 ext4 只读分区,然后用 overlayfs 提供一个小规模的可写层:
mount -t squashfs -o loop /dev/mtdblock0 /mnt/ro # 或者 ext4 ro mount -t tmpfs tmpfs /mnt/rw mount -t overlay overlay -o lowerdir=/mnt/ro,upperdir=/mnt/rw/upper,workdir=/mnt/rw/work /这样程序只能往内存里写,重启后所有修改自动丢弃,从源头上杜绝了“日志写满磁盘”的可能。需要持久化的配置和数据,明确写到/data分区,并且做好掉电保护。
这个方案唯一的副作用是,现场临时改配置变得有点麻烦。但换个角度想,这也倒逼你把配置项通过 web 界面或者远程统一管理,而不是每次都用 SSH 上去 vi 一通改。
5.2 日志轮转与内存缓冲
如果因为业务原因必须写日志到磁盘,至少做到两点:限制大小、及时轮转。我通常用logrotate+systemd-journald配合,journald 本身已经有大小限制功能:
# /etc/systemd/journald.conf SystemMaxUse=128M RuntimeMaxUse=32M SystemMaxFileSize=32M MaxRetentionSec=3day ForwardToSyslog=no这个配置把 journald 的总量控制在 128MB 以内,避免它无限膨胀。业务日志如果走syslog或者直接写文件,也要在应用层做好 max size 控制,超过就滚动。
还有一个很实用的做法:日志先写内存盘,定期同步到远端。把/var/log挂成 tmpfs,应用和方法和之前的 coredump 一样。然后写一个脚本,每 5 分钟检查一次日志目录,有需要归档的内容,就通过 MQTT 或者 HTTP 上报到运维平台,上报完把这个文件删掉。这样日志既不会丢(远端有),也不会伤 eMMC。
5.3 异常掉电防护
工业现场最怕的其实是“写一半断电”。文件系统层最稳妥的方案还是 ext4 开 journal:
tune2fs -O journal_data_ordered /dev/mmcblk0p6如果用的是 overlayfs 的可写层(tmpfs),断电其实无所谓,内存直接清空。但data分区如果直写 ext4,掉电后有可能损坏。所以我建议 data 分区采用“先写临时文件,fsync,再 rename 覆盖”的方式保存配置,避免原地修改主文件时掉电导致文件损坏:
FILE *tmp = fopen("/data/config.yaml.tmp", "w"); fprintf(tmp, "key: value\n"); fclose(tmp); sync(); rename("/data/config.yaml.tmp", "/data/config.yaml"); sync();这算是嵌入式 Linux 上最实用、最可靠的掉电安全写文件方式了,简单但极其有效。
6. 常见问题排查与避坑速查
看完前面的内容,按部就班把这些机制搭起来,设备大概率已经比较稳了。但调试过程中一定会踩到各种奇奇怪怪的坑。我把常见的几个写下来,省得大家再走一遍弯路。
6.1 开机报 “can't find suitable delayline” 是什么问题?
这个错误在 RK3588 上很常见,很多人一搜出来就慌了,以为板子坏了。其实这是 RK3588 的rockchip_drm驱动在初始化时,尝试去查找某个显示接口的 delayline 校准参数失败。多数情况下是 devicetree 里没有包含对应的board.dtsi参数,或者是显示时序配置里缺了delayline相关字段。
如果设备不接显示屏、不跑 HDMI,这些报错通常不影响正常运行,可以直接忽略。但如果你的设备需要 HDMI 输出,就要去 devicetree 里补齐对应端口的参数:
&hdmi0_in_vp0 { status = "okay"; }; &hdmi0 { pinctrl-names = "default"; pinctrl-0 = <&hdmim0_tx0_cec &hdmim0_tx0_sda &hdmim0_tx0_scl>; };注意不同开发板的 pinmux 可能不同,这个要根据你的板子原理图来确认。遇到这类问题,我的建议是先确认功能是否受影响,不受影响就不用管。
6.2 网络连接受限 / 网络假死
RK3588 设备跑着跑着 SSH 连不上了、Ping 不通,但设备的串口界面还有响应,这是边缘设备最常见的“假死”现象。排查思路按顺序来:
- 先看
dmesg里有没有网卡驱动报错,比如 RTL8211F 这种 PHY 芯片在弱电环境下偶尔会挂掉。 - 检查是不是省电策略把网卡休眠了。很多 SDK 默认开了
ethtool的节能特性,现场环境下网络稍有波动就触发网卡挂起。直接关掉:
ethtool -s eth0 autoneg on duplex full speed 1000 ethtool -K eth0 tx-checksum-ip-generic off- 如果还是不定期断网,建议在应用层加网络质量巡检:每 30 秒 Ping 一次网关,连续 3 次超时就把网卡 down 掉再 up 一遍。
ifconfig eth0 down sleep 2 ifconfig eth0 up这个土办法治“网卡假死”特别有用,实际效果立竿见影。
6.3 启动进 recovery / maskrom 后怎么办
RK3588 的刷机模式有两种:Recovery 模式和 MaskROM 模式。Recovery 模式(按住 recovery 键再上电)用于日常固件升级;MaskROM 模式一般出现在 loader 损坏、固件刷坏的情况下。如果你的设备启动进不了系统而是被识别为 MaskROM 设备,别慌,先用 USB Type-C 线连电脑,打开 RKDevTool,重新烧录完整的 Loader 和固件。
从我踩过的坑来看,最容易导致进 MaskROM 的情况是:烧录过程中突然断电、或者用了和板子不匹配的 Loader/miniloader。所以我强烈建议:量产前把 loader 文件和固件打包锁定,不要让现场人员随意用新版工具去刷,否则很容易出现兼容性问题。
6.4 miniloader.bin 版本不匹配
如果你用的是自己编译的 U-Boot 或者从网上拉的新版工具链,烧录时经常遇到miniloader.bin报错,或者烧完起不来。原因通常是 SDK 版本和烧录工具版本不匹配。处理办法很简单——用什么 SDK 编出来的 loader,就用那个 SDK 配套的烧录工具,不要交叉使用。Rockchip 官方的工具版本很多,但每个版本的 miniloader 和 ddrbin 都跟具体芯片和系统版本强相关,混用很容易翻车。
6.5 问题排查速查表
| 现象 | 可能原因 | 优先排查方向 |
|---|---|---|
| 运行几小时后整机无响应 | 内存泄漏 / 内核锁死 | 检查OOM日志、dmesg尾部、top内存占用 |
| NPU 推理越跑越慢 | 散热不足导致降频 | 查看thermal_zone0温度、风扇转速 |
| 断电后起不来 | eMMC 分区损坏 | 进入 MaskROM 重刷固件 |
| 风扇不转但机器没死 | 风扇控制失效 / 温控策略未生效 | 手动写pwm1测试 |
| 日志刷屏导致系统卡顿 | 驱动反复报错 | 检查dmesg,关掉或降级出问题的外设驱动 |
| WiFi/以太网频繁断开 | 省电策略 / 供电不足 | 关节能ethtool -K、检查电源功率 |
这套表是我整理给自己团队线上排障用的,基本可以覆盖现场 90% 的难题。做边缘 AI 设备,拼的从来不是谁算法刷榜高,而是谁能让设备在无人看守的角落里安安稳稳跑上一年。RK3588 本身底子很好,用好了它能成为非常可靠的边缘算力平台,希望这些经验能帮你少走一些弯路。最后再啰嗦一句:任何守护机制,都要先做故障注入测试——手动 kill 进程、拔掉风扇、断电、写满内存——确认设备都能自愈,才算真正的“守护”。祝大家的设备都能安稳运行。