
Milk-V Duo 256M这块板子我在拿到手之前是有点怀疑的——主芯片SG2002的封装面积比一元硬币还小一圈里面塞了三个CPU核还不够居然还带了一块TPU这能玩出什么花来真正上手跑了半个月之后我彻底改变了看法。它不是一块花哨的玩具板而是一台非常典型的边缘AI异构计算小样机而且价格友好到一个不可思议的程度。这篇博客我围绕“深入Sophpi环境、SG2002芯片的TPU算力、Milk-V Duo 256M的双系统架构”这三条主线展开完整记录了从环境搭建、固件烧录、模型转换到双系统编排的整个实战过程。不管你是想做边缘视觉原型验证、嵌入式Linux入门还是在RISC-V生态里找一块能真正跑AI的开发板这篇文章都值得花十几分钟看完。我会把那些官方资料里写得含糊、但实际调试又绕不开的细节尽量讲透。1. 整体设计与思路拆解先说说我个人对这块板的定性Milk-V Duo 256M是一个“麻雀虽小、五脏俱全”的异构系统级验证平台。它的核心元器件是算能Sophgo的SG2002 SoC这颗芯片把三种不同指令集的CPU核和一颗独立TPU封装在一起再配上256MB的DDR3内存最后做成了Duo 256M这样一张比名片还小的开发板。这种设计思路本质上是在用一颗SoC同时承担“实时控制”和“复杂计算”两件事代价是需要开发者适应异构编程模型。1.1 为什么是Milk-V Duo 256M而不是树莓派或者K210如果你只是想在Linux里写写Python脚本控制GPIO那树莓派Zero或者任何一块全志H3板子都能干没必要选Duo。但这块板子的定位完全不同它的两大杀手锏是双核双系统异构和板载TPU推理能力。先说双系统异构。SG2002内部有一颗1.0GHz的RISC-V C906大核、一颗1.2GHz的ARM Cortex-A53大核还有一颗8051小核。C906和A53本质上都可以独立跑Linux或者RTOS这就意味着你可以让A53跑Linux处理网络协议栈和应用逻辑同时让C906跑RTOS做毫秒级实时控制也可以反过来C906跑Linux当主控A53跑RTOS做实时外设响应。这种大小核搭配在手机SoC上很常见但在几十块钱的开发板上可不多见。再说TPU。SG2002集成了一颗INT8算力约0.5TOPS的TPU对你没看错是0.5TOPS不是5TOPS也不是50TOPS。这个数字放在2025年看并不惊艳但考虑到整板功耗通常在1到2瓦之间它的能效比就很有说法了。树莓派4跑一个实时目标检测CPU占用接近打满功耗轻松冲到5瓦以上而Duo 256M用TPU跑同样的轻量模型CPU占用很低整体功耗不到2瓦。在电池供电的移动设备、智能家居传感器、工业检测探头上这个区别是决定性的。1.2 双系统架构到底解决什么问题很多人第一次接触双系统架构时会问既然Linux已经支持实时调度为什么还要再塞一个RTOS这个问题的答案要从中断延迟说起。Linux为了保证公平调度和内存管理会关闭中断、切换进程上下文这会带来不可控的微秒甚至毫秒级延迟。对于视频帧处理、网络包收发这种任务它没问题但对于电机控制、编码器读取、PWM波形输出这类硬实时任务Linux的调度延迟是不可接受的。RTOS正好相反它没有复杂的内存管理和进程调度中断响应时间可以做到微秒级但它的生态太弱网络协议栈、文件系统、驱动库都不够完善。双系统架构的思路就是让两个系统各干各的专长活再通过核间通信机制把结果串起来。实际项目里最典型的组合是A53跑Linux负责摄像头采集、网络上传、用户交互C906跑RTOS负责舵机控制、传感器轮询两边通过共享内存和mailbox中断同步数据。这样一来实时性和功能性都能保住。1.3 0.5TOPS的TPU能干什么、不能干什么0.5TOPS这个算力水平用一句大白话概括就是“能跑轻量级视觉模型但别幻想跑大模型”。我实测下来MobileNetV2这种轻量分类网络224x224输入单帧推理大约30到50毫秒YOLOv5s这种目标检测网络640x640输入单帧推理大约120到180毫秒换算下来大概5到8FPS。如果你把输入缩小到320x320帧率还能再翻一倍但检测精度会明显下降。这个算力区间最适合的任务是智能门锁的人脸识别门禁、工业质检的缺陷分类、农业物联网的害虫计数、穿戴设备的手势识别等等。本质上都是“输入一张图输出一个标签或者几个坐标框”的任务。至于Transformer类模型、大语言模型、视频理解之类的任务SG2002的TPU就带不动了建议直接上更高算力的平台。选型的时候心里要有这杆秤0.5TOPS是“够用”的边缘AI算力不是“全都能跑”的通用算力。2. 环境准备Sophpi一站式开发环境搭建在真正开始写代码之前第一步是搭好开发环境。SG2002这颗芯片同时涉及RISC-V、ARM两种指令集还有TPU模型转换工具链如果靠自己去网上一个个装交叉编译器很容易晕头转向。这时候Sophpi这个官方Docker化开发环境就派上用场了。2.1 Sophpi是什么为什么选择容器化方案Sophpi是算能官方维护的一个Docker镜像里面预装了Milk-V Duo系列开发板所需的完整编译工具链riscv64交叉编译器、aarch64交叉编译器、duo-examples示例代码、TPU-MLIR模型转换工具以及一系列辅助脚本。你在自己的电脑上只要装好Docker拉取Sophpi镜像就能获得一个与官方CI一致的编译环境不需要再手动安装各种依赖。容器化方案的优势是显而易见的。首先它避免了“在我机器上能编译在你机器上编译失败”的环境差异问题其次Docker容器天然隔离即使你在容器里把系统库搞坏了删掉容器重建一个就行完全不影响宿主机最后算能官方会持续更新这个镜像你只需要定期docker pull就能获得最新工具链。我第一次用的时候还不太习惯总觉得Docker跑嵌入式开发有点绕但用顺手之后发现这其实是嵌入式行业未来应该走的方向——让环境统一化、可复现。2.2 安装Sophpi与初始化开发环境Sophpi的安装过程非常直接前提是你的电脑上已经装好了Docker并启动了Docker服务。我这里以Linux宿主机为例macOS和Windows下操作同理只是挂载目录的路径写法略有不同。# 拉取Sophpi官方镜像 docker pull sophgo/sophpi:latest # 创建工作目录 mkdir -p ~/duo-workspace # 启动容器并把工作目录挂载进去 docker run -itd --name sophpi \ -v ~/duo-workspace:/home/sophpi \ --network host \ sophgo/sophpi:latest # 进入容器 docker exec -it sophpi /bin/bash进入容器之后先检查一下环境是否正常在容器内执行# 查看交叉编译工具链是否就绪 ls /opt/host-tools/ which riscv64-unknown-linux-gnu-gcc which aarch64-linux-gnu-gcc # 查看TPU-MLIR工具链是否就绪 source /etc/profile.d/tpu-mlir-env.sh which model_transform.py which model_deploy.py如果上面的命令都能正常输出路径说明Sophpi环境已经就绪。这里有几点经验分享提示容器最好用--network host方式启动否则后续在容器内下载SDK代码时会走NAT网络速度慢不少。另外工作目录挂载到宿主机是必须的因为容器默认文件系统在容器删除后会丢失所有重要代码和编译产物都应该放到挂载目录里。我第一次踩过的坑是忘记挂载目录在容器里编译了一下午的固件退出容器后全部丢失那种痛希望你们不用经历。2.3 烧录固件与首次启动环境就绪之后下一步是准备一块SD卡烧录官方固件。Milk-V Duo 256M支持从SD卡启动官方发布页面会提供多个镜像文件名字里区别很关键。比如duo256m_sd_c906_linux_a53_rtos_v1.2.1.img表示这个固件里C906跑Linux、A53跑RTOSduo256m_sd_c906_rtos_a53_linux_v1.2.1.img则表示C906跑RTOS、A53跑Linux。关于双系统镜像的详细选择逻辑后面第四章会专门展开这里先随便选一个能启动的镜像。烧录命令在Linux下很直观# 先确认SD卡设备名千万别搞错比如 /dev/sdb lsblk # 开始烧录注意替换成你自己的设备名 sudo dd ifduo256m_sd_c906_linux_a53_rtos_v1.2.1.img of/dev/sdb bs4M statusprogress convfsync烧录完成后SD卡会分成多个分区其中一个FAT分区可以直接在电脑上挂载查看里面是配置文件和一些工具脚本。把SD卡插到Duo 256M的卡槽用USB转串口模块连接板子的调试串口波特率115200上电后就能在串口终端里看到启动日志。默认登录账号是root密码是milkv登录成功后你会看到Linux的Shell提示符到这一步板卡的基础环境就算跑通了。3. TPU模型部署实战从ONNX到bmodel环境跑通只是热身这个项目的重头戏是让TPU真正跑起来。SG2002的TPU不能直接跑PyTorch产生的模型文件它只认算能自定义的bmodel格式。所以我们得先把手里的模型从PyTorch/ONNX转换成bmodel再把bmodel部署到板子上推理。整个过程可以分为硬件原理理解、模型转换、板卡部署三个阶段。3.1 SG2002 TPU硬件与量化要点先讲清楚TPU到底是什么。TPU的全称是Tensor Processing Unit也就是张量处理单元本质上是为矩阵乘法这类卷积运算专门设计的加速器。CPU做乘法是“一条指令处理一个数据”而TPU是一条指令处理一个矩阵这就带来了数量级上的并行度提升。SG2002这颗TPU的核心规格我整理如下规格项参数峰值算力0.5 TOPS INT8支持量化格式INT8、BF16主要推荐INT8支持网络类型CNN主流结构、部分轻量Transformer工具链TPU-MLIR、CVI Runtime支持的框架PyTorch、ONNX、TFLite、PaddlePaddleINT8量化是TPU部署里最关键也最容易出问题的一环。简单说量化就是把模型训练时用的FP32浮点权重和激活值映射到INT8整数范围。这个映射过程会丢失一定精度但换来的收益是显存占用减少到四分之一、计算速度提升数倍。TPU计算INT8的效率远高于FP32所以实际部署基本都是走INT8路线。这里要特别强调校准集的重要性。校准集是量化过程中用来统计激活值动态范围的真实样本它决定了量化参数的合理性。我见过很多人图省事随便拿几十张不相关的图片做校准集结果模型在测试集上掉点严重然后就抱怨工具链垃圾。这实际上是把锅甩错了地方。校准集应该尽量贴近真实推理场景比如你做的是工业零件检测就要拿工业现场的工件图做校准而不是拿风景照凑数。3.2 使用TPU-MLIR进行模型转换在Sophpi容器内TPU-MLIR工具链已经预置好了。模型转换的完整流程是先把你训练好的PyTorch模型导出成ONNX格式然后把ONNX通过model_transform.py转换成mlir中间表示再通过model_deploy.py把mlir经过量化和编译最终生成bmodel。先看一个实际例子。假设我有一个YOLOv5s目标检测模型需要用它检测工业场景里的螺丝缺陷。第一步在训练环境里把PyTorch模型导出成ONNXimport torch from models.experimental import attempt_load model attempt_load(best.pt, map_locationcpu) model.eval() dummy_input torch.randn(1, 3, 640, 640) torch.onnx.export( model, # 模型 dummy_input, # 模拟输入 yolov5s_defect.onnx, # 输出ONNX文件 opset_version11, input_names[images], output_names[output0] )导出ONNX时有几个容易出问题的地方。YOLOv5仓库里自带的export.py脚本已经处理好了一些细节比如去掉训练时特有的检测头分支如果你是自己写的模型结构就需要手动确认ONNX输出的张量格式是否与后处理代码匹配。另外ONNX算子版本不适合选太高因为TPU编译器对算子的支持是分版本迭代的我在实际转换中遇到过opset 17的模型在编译器里报算子不支持改成opset 11就好了。拿到ONNX之后进入Sophpi容器执行模型转换# 1. 完成框架转换得到mlir文件 model_transform.py \ --model_name yolov5s_defect \ --model_def yolov5s_defect.onnx \ --input_shapes [[1,3,640,640]] \ --mean 0,0,0 \ --scale 0.0039216,0.0039216,0.0039216 \ --pixel_format rgb \ --output_names output0 \ --mlir yolov5s_defect.mlir # 2. 准备校准图片列表 ls calib_images/ | head -n 100 calib_images.txt # 3. 生成INT8量化表 run_calibration.py \ yolov5s_defect.mlir \ --dataset calib_images.txt \ --input_num 100 \ -o yolov5s_defect_cali_table # 4. 编译生成bmodel model_deploy.py \ --model_name yolov5s_defect \ --mlir yolov5s_defect.mlir \ --quantize INT8 \ --calibration_table yolov5s_defect_cali_table \ --chip sg2002 \ --output yolov5s_defect_bmodel整个过程看起来像流水线但每一步参数都需要根据你的模型来微调。input_shapes要和导出ONNX时的dummy_input形状保持一致mean和scale要与模型训练时的预处理方式完全一致否则推理结果会出现系统性偏差。以YOLOv5为例官方训练时用的是0到1的归一化没有均值相减所以mean设为0scale设为1/255。3.3 在板卡上跑通目标检测Demobmodel生成之后把它拷贝到Milk-V Duo 256M的SD卡里接下来要写一个C或者Python程序来加载bmodel并执行推理。Sophpi自带的duo-examples仓库里有很多现成demo包括分类、检测、人体关键点等我强烈建议先跑通这些官方示例再逐步改成自己的逻辑。下面这个是简化后的C语言推理核心流程基于CVI Runtime API编写#include stdio.h #include cviruntime.h int main(int argc, char **argv) { // 1. 初始化runtime并加载bmodel CVI_MODEL_HANDLE model NULL; CVI_RC rc CVI_NN_RegisterModel(model, argv[1]); if (rc ! CVI_RC_SUCCESS) { printf(Failed to register model: %d\n, rc); return -1; } // 2. 获取模型输入输出tensor信息 CVI_TENSOR *input_tensors, *output_tensors; int32_t input_num, output_num; CVI_NN_GetInputOutputTensors(model, input_tensors, input_num, output_tensors, output_num); // 3. 把预处理后的图像数据拷入输入tensor CVI_TENSOR *input CVI_NN_GetTensorByName(CVI_NN_GetInputTensors(model), input_num, images); // TODO: 把YUV/RGB图像resize到640x640并转为fp32 memcpy(input-sysMem[0].virAddr, image_data, 640 * 640 * 3 * sizeof(float)); // 4. 执行推理 CVI_NN_Forward(model, input_tensors, input_num, output_tensors, output_num); // 5. 解析输出并做NMS后处理 CVI_TENSOR *output output_tensors[0]; float *data (float *)output-sysMem[0].virAddr; // TODO: 解析输出执行class-aware NMS CVI_NN_CleanupModel(model); return 0; }这段代码的关键点是输入数据的内存布局。TPU要求输入图像resize到模型指定的分辨率且数据排列方式与转换时的pixel_format一致。如果你的模型在转换时用的pixel_format rgb那在板端就要把摄像头采集的BGR图像转换成RGB再送入tensor。我一开始没注意这个顺序总是把OpenCV默认的BGR图直接喂进去结果输出类别置信度总是不对排查了很久才发现是通道顺序反了。跑通之后实测一下推理速度。在Sophpi环境编译demo用性能测试工具跑一遍可以看到单帧推理耗时大约150毫秒左右。这个速度对于实时视频流确实有点紧张但如果你把输入分辨率降到320x320耗时能降到60到80毫秒基本可以做到10FPS以上。3.4 提升TPU推理帧率的几个技巧既然推理速度是关键指标这里集中分享几个实测有效的优化手段优先降低输入分辨率。很多检测模型在320x320输入下mAP掉点并不多但推理速度能翻倍对嵌入式场景是性价比最高的优化方式。使用多线程流水线。把图像采集、预处理、TPU推理、后处理放到不同线程用环形缓冲区衔接理论上可以把帧率做到接近单帧推理时延的倒数而不是串行地“采一帧、处理一帧”。尽量少做CPU上的预处理。resize和颜色转换可以用板载的VIPVideo Image Processor硬件模块完成把CPU从重复的像素操作中解放出来。量化后精度下降明显时优先检查校准集质量和预处理参数不要急着换模型。很多情况下精度问题出在预处理不一致而不是量化本身。在这些技巧里最容易被忽略的是像素格式带来的性能影响。SG2002的VIP输出通常直接是NV12格式如果你的模型转换时使用BGR输入那板端每一帧都需要做一次NV12到BGR转换这个转换在CPU上跑几百毫秒都不稀奇但用硬件IPU去做就几乎免费。在实际项目中我会优先选支持YUV420输入的模型转换参数再在模型内部用算子把YUV转成RGB把像素格式转换的压力交给TPU而不是CPU。4. 双系统架构实战Linux与RTOS同芯共舞跑通了TPU接下来进入第二个核心主题双系统架构。这是SG2002区别于一众同级芯片的最大卖点也是很多开发者觉得“难以上手”的地方。实际上只要理解了它的启动逻辑和通信机制双系统开发并没有想象中那么复杂。我一直建议拿到镜像先别急着烧录花十分钟看一眼镜像命名和官方发布的启动脚本想想“这个系统是怎么把这些核拉起来的”后面调试会顺很多。SG2002的异构启动大致可以这样理解芯片上电后8051小核最先运行执行固化在芯片内部ROM里的引导代码然后根据拨码或eFuse配置决定从SD卡还是SPI Flash加载FSBLFirst Stage Boot LoaderFSBL负责初始化DDR内存然后把主核的固件镜像加载到内存指定地址让主核跳转运行。4.1 固件选择与启动分工关键点来了主核是哪一个取决于你烧录的镜像。官方发布的固件镜像有两类c906_linux_a53_rtosC906作为主核启动LinuxA53作为从核加载RTOSc906_rtos_a53_linuxA53作为主核启动LinuxC906作为从核加载RTOS。这个选择直接影响开发方式。如果你以前是做嵌入式Linux的对A53生态比较熟那选c906_rtos_a53_linux可能更容易入手因为主系统是ARM Linux工具链、调试器、库都是传统嵌入式Linux那一套如果你更关心RISC-V原生体验对Linux主控只要求够用就好那选c906_linux_a53_rtos会更合适毕竟C906跑Linux才是这颗芯片最有辨识度的玩法。启动流程里还有个细节值得注意主核Linux启动时会在设备树里为从核预留一段内存空间然后通过remoteproc框架把从核的固件加载到预留内存的指定位置再触发从核复位和启动。也就是说从核并不是和主核同时上电的而是由主核Linux在合适的时候主动拉起来的。理解这一点对排查问题非常重要很多“从核没跑起来”的问题本质都是“主核Linux还没来得及加载从核固件”。4.2 核间通信与资源划分双系统跑起来只是第一步两个系统之间得能通信否则就是“同床异梦”。SG2002的核间通信主要依赖两种机制共享内存和Mailbox中断。共享内存是数据交换的主通道。主核和从核在DDR地址空间中各自分到一段内存其中一段被指定为共享区域。一方往共享区域写数据另一方读取就能完成数据交换。但这里有个经典问题如果双方同时读写同一地址数据就乱了。解决办法是靠Mailbox中断做握手协议——写方写完共享内存后触发一个Mailbox中断通知读方“数据准备好了”读方收到中断后从共享内存取数据处理完再回一个中断表示“这次数据我收到且处理完了”。这样“先写共享内存再发中断”的流程才能保证数据同步。在内核代码里SG2002的核间通信驱动通常会把共享内存封装成一个简单的环形缓冲区或者消息队列。官方SDK里一般会提供几个demo比如在Linux端有一个用户态程序通过/dev/rpmsg设备文件向从核发送消息。下面这段是Linux端的用户态通信示例#include stdio.h #include fcntl.h #include string.h #include unistd.h int main() { // 打开rpmsg设备节点 int fd open(/dev/rpmsg0, O_RDWR); if (fd 0) { perror(open /dev/rpmsg0); return -1; } // 周期发送控制指令 char cmd[64]; for (int i 0; i 10; i) { snprintf(cmd, sizeof(cmd), SET_PWM %d, i * 100); write(fd, cmd, strlen(cmd) 1); char buf[128] {0}; read(fd, buf, sizeof(buf)); printf(RTOS reply: %s\n, buf); usleep(500 * 1000); } close(fd); return 0; }对应的RTOS端会有一个接收任务监听mailbox中断消息解析指令执行PWM输出或者读取传感器再把状态回传。这样一个“Linux发指令、RTOS执行实时动作”的闭环就建立起来了。实际项目中你可以把图像推理结果通过共享内存传给RTOS让RTOS根据结果控制机械臂、云台或者门锁实现“看得见、想得到、动得了”的完整智能体逻辑。這裡再重点提一句资源划分。双系统要用好必须提前规划好内存分配。SDK里一般会在dts或者配置文件里给主核和从核划好内存比如256MB中主核Linux分到224MB从核RTOS分到32MB。如果你发现从核程序编译完加载后跑飞多半是从核的内存超过预留值踩到了主核的内存空间。这时候优先调整dts里的reg和reserved-memory配置而不是盲目优化程序体积。4.3 双系统开发时几个高频陷阱双系统开发的坑比单系统多一截我把实践中碰到过的几个典型问题整理出来供参考第一个坑是串口抢占。默认情况下串口0是主核Linux的调试串口从核RTOS默认没有输出。你想看在RTOS里printk的输出需要额外配置从核的串口否则你会觉得RTOS完全没有跑起来。排查方法是查看共享内存里的状态标志或者直接在从核代码里把某个GPIO拉到高电平用示波器确认从核是否起来。第二个坑是中断路由。Mailbox中断是Per-CPU型的你在写驱动时一定要确认中断号绑定到哪个核。如果配置错了主核发中断的代码执行了但从核对应的中断号不对通信就断了。这属于底层配置问题排查起来比较隐蔽建议对照官方SDK里的中断映射表逐一核对。第三个坑是固件版本匹配。双系统固件和TPU驱动经常是配套发布的主核Linux版本和从核RTOS固件版本不匹配会导致核间通信协议对不上轻则功能异常重则系统崩溃。每次升级固件的时候最好同时升级两边的镜像别只升级一个。第四个坑是功耗和散热。双系统全速跑的时候SG2002的功耗会比单系统高30%以上我实测持续跑TPU推理加双核通信时整板功耗能到2瓦左右。板载芯片表面温度在室温下能达到60多度长时间运行需要考虑加散热片。这个在项目落地时一定要提前设计好别等产品量产了才发现发热问题。5. 常见问题与排查技巧实录最后这部分我把调试中遇到的典型问题整理成速查表基本都是我自己踩过或者帮朋友排查过的案例希望能帮读者少走弯路。5.1 启动与烧录问题速查现象可能原因排查方法板上电后串口无任何输出串口线接错TXD/RXD交叉检查串口模块接线确认使用3.3V电平串口输出乱码波特率不对确认波特率设置为115200上电后只打印BootROM信息就卡死SD卡分区或固件损坏重新执行dd烧录烧录后执行syncLinux启动到一半panicSD卡读取不稳定更换品牌SD卡Class 10以上检查供电是否稳定登录后无法分配IP地址网络配置未启用检查/etc/network/interfaces确认eth0启用DHCP启动阶段最常见的问题其实是串口接线。Duo 256M的调试串口是1.8V电平的普通的USB转串口模块往往是3.3V电平直接接虽然大多数情况下能通但长期使用有风险最好用支持1.8V电平的模块或者加电平转换。我第一块板子的串口模块就是这么烧坏的教训深刻。5.2 模型转换与TPU推理问题速查现象可能原因排查方法model_transform报算子不支持ONNX算子版本过高或模型含特殊算子降低opset版本用onnxsim简化模型算子替换为支持版本转换成功但推理结果全为0输入数据格式或预处理不一致检查pixel_format、mean、scale是否与训练一致推理速度远低于预期模型中有大量非TPU支持算子回退CPU执行查看编译日志中的算子分配表确认算子在TPU上执行量化后模型精度大幅下降校准集太小或者不具代表性扩大校准集规模到几百张确保覆盖各种光照和姿势推理程序段错误输入tensor大小与模型不匹配再次确认input_shapes打印tensor信息对比模型转换问题最隐蔽的是算子和预处理不一致这两个点。算子方面遇到不支持的算子不值得硬刚通常的做法是在PyTorch侧把模型结构换成等价的算子组合比如把大尺寸Transposed Convolution拆成两个小卷积就能绕过编译器的限制。预处理方面请务必将模型训练时的数据预处理代码原封不动地搬一份到推理端不要凭记忆重写这是我用代码里的血泪换来的建议。5.3 双系统通信问题速查现象可能原因排查方法从核固件加载后无响应从核未正确启动检查remoteproc状态cat /sys/class/remoteproc/remoteproc0/staterpmsg设备节点不存在内核配置未开启rpmsg检查内核配置CONFIG_RPMSG_VIRTIO和CONFIG_RPMSG_CHAR通信数据偶发丢包共享内存缓冲区溢出扩大缓冲区或改用流控协议核间通信延迟高中断处理和上下文切换耗时优化RTOS任务优先级关闭不必要的中断嵌套这里再补充一个我最近遇到的问题Linux端向从核RPMSG发送数据时偶发“Resource temporarily unavailable”错误。排查了很久发现是从核的RTOS处理速度跟不上接收缓冲区满了Linux端发送接口默认采用非阻塞模式缓冲区满就直接返回错误。解决办法是把Linux端发送函数改成阻塞等待或者加大共享内存环形缓冲区。这类问题没有统一的灵丹妙药核心思路是“看状态、看日志、看内存”把问题定位到是链路、缓冲区还是处理速度出了问题。最后再分享一点我个人折腾完这一整轮之后的体会Milk-V Duo 256M这块板子虽然小但它几乎把边缘AI应用的完整链路都串了起来——异构双系统、硬件加速推理、嵌入式Linux开发、RISC-V生态探索这些知识点单独拉出来都能写很多篇文章。如果你拿着这块板子却不知道从哪开始我建议按照“Sophpi环境搭建到烧录启动、跑通官方TPU示例、再把模型替换成自己的、最后尝试双系统通信”的顺序走一遍。等这一套流程走顺了再回头去看SG2002的芯片手册里的内存映射、中断控制器和核间通信协议你会发现屏幕上的框图突然就活了。