最近不少人私信问我同一个问题Atlas 300V 24G到底算不算运算加速卡网上搜出来的说法很乱有人说是推理卡有人说是AI加速卡还有人直接拿它和NVIDIA显卡比问能不能像GPU一样跑训练。恰好这段时间我一直在用这块卡部署YOLOv5/YOLOv8从PyTorch权重一路折腾到昇腾环境最终跑通了实时视频流推理。这篇就把这块卡的硬件边界、部署全流程、模型迁移和实际踩坑记录一次说清楚给准备入手或者已经在这条路上挣扎的朋友一个参考。如果你只在网上看过Atlas 300V的产品页大概率会被“24G”这个显存数字吸引。但真正把它插进服务器、开始跑模型之后你才会发现它和熟悉的GPU生态是两套完全不同的玩法。这篇文章不打算堆参数而是按我实际操作的顺序从硬件定位讲到环境搭建再讲到YOLO模型的迁移和推理落地最后把那些文档里不会明说、但一旦踩到就非常耽误时间的坑都摆出来。1. 先把它摆在桌面上Atlas 300V 24G的硬件边界与定位很多人在“运算加速卡”这个概念上卡住了因为这个词既可以指通用计算卡也可以指专用AI加速卡。Atlas 300V 24G准确说是一张AI推理加速卡不是通用并行计算卡。它里面用的是昇腾310P处理器专门面向边缘推理、视频分析、图像识别这类场景设计而不是为了跑科学计算或训练大模型存在的。1.1 关键规格与“24G”的实际价值这张卡的物理形态很友好半高半长所以普通的2U服务器或者边缘盒子都能塞进去。功耗大概是75W这个数字非常关键意味着大多数情况下不需要外接供电风道也不需要大改在一些老旧服务器上也能直接插。24GB显存这块要单独说。对于视觉模型来说显存容量直接影响单卡能同时承载的模型复杂度和batch size。举个实际例子用YOLOv8s做检测640×640输入单帧推理显存占用不到1GB24GB听起来好像严重过剩。但如果你做的是多路视频流分析或者需要一次性处理一批图像显存就变成硬指标了。我实测过单卡跑8路1080p视频流的YOLOv8s检测加上帧缓存和预处理buffer显存占用大约在7GB到9GB之间空间仍有富余。但如果换成YOLOv8m甚至更重的模型路数不变的情况下显存占用会明显上升这时候24GB的余量就成了能不能继续加路数的关键。顺便提一下算力。官方标称的INT8算力在百TOPS级别具体数字会因为子型号和频率策略略有差异。INT8算力高是这类推理卡的共性因为推理场景普遍用INT8量化换吞吐。如果你习惯了GPU上用FP16或者FP32跑模型刚开始会对“INT8才是主流”这件事有点不习惯。1.2 为什么说它不是通用的“运算加速卡”这个问题我在各个群里看了太多争论其实拆开说没那么复杂。真正决定一块卡“通用”不通用不是硬件形态而是软件生态。NVIDIA的CUDA生态几乎覆盖了所有主流框架你写一段PyTorch代码只要显存放得下基本都能跑。但Atlas 300V 24G不行它依赖昇腾特有的CANN软件栈对外暴露的接口是ACLAscend Compute Language模型格式主要是OM。哪怕你用PyTorch训练好了权重也必须先导出成ONNX再用ATC工具转成OM才能在卡上运行。这就带来几个直接后果你不能把PyTorch的.pt文件直接丢给这块卡加载你也不能指望所有PyTorch算子都能顺利转换转换过程中会遇到算子不支持的情况你没法用CUDA的profile工具去调它性能分析要走昇腾自己的npu-smi和msprof工具链。所以如果非要问“它是不是运算加速卡”我的回答是它是AI推理加速卡但把“运算”理解成“什么都能算”那它不通用。理解这个边界非常重要因为照着GPU的思维去用肯定会碰壁。1.3 这块卡适合替掉哪类GPU从我的实际体验来看它最适合替换的场景是做视频结构化、工业质检、边缘智能这类以推理为核心的业务系统。这些系统通常对功耗、成本、路数密度有要求而不是追求训练灵活性。以前很多项目用GTX 1080Ti、RTX 2080Ti或者T4做推理但老卡逐步停产、功耗偏高换到Atlas 300V 24G之后单卡路数能力相当但整机功耗降了很多。举个例子我手头一台服务器原先插两块RTX 2080Ti跑16路视频检测整机峰值功耗差不多700W。换成两块Atlas 300V 24G之后同样16路整机功耗降了大约200W。在机房电费按年结算的背景下这个差异还是明显的。当然代价是迁移工作量所有部署脚本、模型格式、推理代码都要重写一遍。2. 部署YOLO前最容易被忽略的环境链路驱动、固件与CANN三件套如果你用GPU用习惯了可能觉得插上卡、装个驱动、拉个镜像、跑个容器就完事。但昇腾卡不是这样它需要固件、驱动、CANN开发套件三层配合缺一不可。而且这三者的版本不是独立的彼此之间有严格的配套关系。我第一次装的时候就是没搞清楚这个顺序结果固件版本和驱动不匹配npu-smi直接看不到卡排查了整整一下午。2.1 一张表看懂环境栈的样子与其看晕人的官方文档不如直接记这个分层关系层级组件作用硬件层Atlas 300V 24G提供NPU计算单元固件层昇腾NPU固件初始化硬件、管理处理单元驱动层昇腾驱动让操作系统识别NPU提供设备节点基础软件层CANN Toolkit提供算子库、运行时、图编译等能力应用层ACL / MindX SDK / 你的推理代码实际跑模型驱动和固件的关系类似主板BIOS和操作系统驱动的关系固件不对驱动再新也白搭。CANN Toolkit则承担了类似CUDA Toolkit的角色所有上层接口和算子编译都依赖它。在昇腾官方文档里每一个固件版本都会标注支持哪些驱动版本和CANN版本照着配套表选能省掉大量麻烦。2.2 环境安装的具体顺序和检查命令按我验证过的流程建议顺序是先装驱动和固件再装CANN Toolkit最后安装MindX SDK如果你打算用SDK方式跑。装驱动固件之前最好先进入BIOS确认把SR-IOV和Above 4G Decoding按需打开否则后续PCIe设备访问可能有问题。驱动和固件安装包通常是.run文件安装命令大致为./Ascend-hdk-310P-npu-driver_xxx.run --full ./Ascend-hdk-310P-npu-firmware_xxx.run --full装完重启后用npu-smi工具检查是否识别到卡npu-smi info正常情况下会看到板卡列表里面会有芯片型号、显存容量、温度、当前功耗这些信息。如果这里报错或者看不到卡先别急着装CANN优先排查驱动和固件版本是否配套以及物理插槽是否被识别。接着安装CANN Toolkit。下载对应版本的Ascend-cann-toolkit_xxx_linux-aarch64.run或x86_64版本按顺序安装并设置环境变量./Ascend-cann-toolkit_xxx.run --install source /usr/local/Ascend/ascend-toolkit/set_env.sh为了不每次开终端都手动source可以把它写进~/.bashrc。设置完之后用下面的命令确认CANN是否能正常找到运行环境which atc只要atc命令能查出来说明模型转换工具已经就绪接下来才能做YOLO模型的迁移。2.3 环境版本最容易踩的坑我踩过最大的坑是在老服务器上装新固件时忘记升级驱动结果npu-smi虽然能列出来但CANN初始化时报“E30002 runtime init failed”这类错误。后来发现官方配套关系表里那个新固件要求的最低驱动版本比我装的旧驱动高导致运行时不认设备。还有一个坑非root用户跑到卡权限问题。默认情况下只有root用户或加入HwHiAiUser用户组的用户才能访问设备节点。很多同学喜欢用普通用户跑服务结果发现卡初始化成功但open device失败其实就是权限不足。解决办法很简单usermod -aG HwHiAiUser yourname另外服务器上如果已经装了旧版本的CANN建议先卸载干净再装新版。否则环境中多个CANN版本共存set_env.sh里路径会互相干扰最后atc命令版本跟Runtime版本不一致编译出来的OM模型在推理时直接报算子版本不匹配。别问我怎么知道的重装了三次才清净。3. 模型迁移的实战场把YOLO权重换成昇腾认识的OM格式环境装好之后最核心的问题就是YOLO模型文件怎么变成昇腾能跑的格式。很多人在这里绕不过去因为思维还留在“PyTorch加载权重”的惯性里。昇腾推理的标准格式是OM而OM文件不是从.pt直接转换来的基本流程是.pt导出成ONNXONNX再通过ATC工具转成OM。3.1 为什么不能直接用PyTorch权重这个可以从底层逻辑理解。PyTorch的.pt文件保存的是模型结构和参数运行时依赖PyTorch框架去解释计算图。但NPU上执行的指令集和GPU完全不同昇腾需要先把计算图转成自己的一套IR再经过算子调度和内核映射最后生成可执行文件。ATC工具做的事情就是这个编译过程。所以跳过ONNX直接转OM理论上也可以但实际操作非常少。ONNX相当于一个中间表示几乎所有主流框架都能导出ONNX而ATC对ONNX算子的支持是最完整的。选择ONNX做中间格式是为了把“模型训练”和“模型部署”解耦也方便排查问题。转换流程总结如下PyTorch导出ONNX简单校验ONNX结构使用ATC将ONNX转为OM用OM文件写推理程序。3.2 YOLO模型的ONNX导出注意事项YOLOv5和YOLOv8在导出ONNX时都有一些细节要注意。先说YOLOv5官方仓库里自带导出脚本最省事的做法是python export.py --weights yolov5s.pt --include onnx --img-size 640 640但如果你自己改过模型结构建议检查一下导出时是否带着后处理。YOLOv5的原生导出会把模型的主体算子导出来但检测头的解码部分可以由你自己选择是否保留。我的建议是导出时不带后处理保留原始输出tensor后处理放在推理侧自己实现。原因很简单ATC转换时如果后处理算子不支持会带来不必要的麻烦而且放在推理侧你可以针对不同精度的需求自由写解码逻辑。YOLOv8的导出也类似yolo export modelyolov8s.pt formatonnx imgsz640需要注意导出ONNX时的opset版本。ATC对不同opset的支持存在一个区间实测opset 11到13之间比较稳妥。如果opset太高可能会遇到某些新增算子不能映射太低的话有些动态shape相关的行为又没法表达。这里再提醒一个常见的低级错误导出的ONNX输入维度是1,3,640,640但ATC转换时如果你打算用动态batch需要额外指定动态维度范围。初学阶段建议直接用固定shape先把流程跑通再考虑动态能力。3.3 ATC转换模板与参数ATC工具的参数不多但每个都需要用得恰到好处。以YOLOv5s为例最小可用命令是atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_bs1 \ --soc_versionAscend310P3 \ --input_shapeimages:1,3,640,640参数说明--model输入的ONNX路径--framework5表示ONNX输入这个数字不要改--output输出的OM文件名前缀--soc_version芯片型号可以用npu-smi info查看常见310P系列填Ascend310P3--input_shape固定输入维度顺序和ONNX导出时的输入名保持一致。如果要将图片预处理逻辑缩放、归一化、色域转换也一并固化到OM模型里可以配置--insert_op_conf参数指向一个AIPP配置文件。我初期为了调试方便没有启用AIPP而是把预处理完全放在推理代码里这样问题定位时更直观。正式部署想压榨性能时再把预处理挪到AIPP里会更好。转换完成后终端会提示success并显示生成的OM文件路径。如果出现报错不要慌九成是算子问题可以通过开启详细日志定位。3.4 转换失败排查记录我在转YOLOv8s时遇到过比较典型的报错是某个自定义模块里的resize算子不被ATC识别。报错信息大概是“Unsupported op kernel”或“Failed to compile operator”。这种问题不要硬扛通常有三个解决方向检查导出的ONNX里是否包含了后处理如果有先把后处理剥离掉用可视化工具查看ONNX算子列表找到不被支持的算子考虑用等价算子替换升级CANN版本新版本会不断补充算子支持列表。另一个容易出错的地方是动态shape。ATC默认按静态shape优化如果你在导出ONNX时用了动态shape转换时就必须用--dynamic-input-shape或--dynamic-dims参数指定动态范围否则可能报shape推导失败。我的建议还是那句话第一次转换全部用固定shape。4. 两套推理落地路径MindX SDK与ACL Python接口的选择逻辑OM模型拿到手之后接下来就是怎么在业务里调用。昇腾生态里最常用的两条路子MindX SDK和Python ACL。两条路我都写过生产代码各有各的取舍。4.1 MindX SDK适合快速搭起多路视频分析业务MindX SDK是昇腾上层提供的开发套件核心思想是“插件化流水线”。你不需要手写算子级别的调用而是把解码、缩放、推理、后处理等步骤配置在一条pipeline里每个步骤是一个plugin插件之间通过buffer串起来。对于视频流场景非常友好因为MindX SDK自带的视频解码插件能直接吃RTSP流或本地视频文件省掉自己开FFmpeg的功夫。一个典型的推理pipeline配置简化为这样imgo: factory: opencv input: data/input.mp4 ... crop: factory: imagecrop ... infer: factory: tensorinfer in_desc: preprocess_output model_path: ./yolov5s.om postprocess: factory: yolov5postprocess ...这里只是示意实际字段名称因为SDK版本迭代可能有变化但整体思路一致把处理流程拆成多个插件每个插件输入和输出由上下两级连接起来。好处是改动某个环节不需要动代码改配置就能重跑。坏处是定制性不够灵活遇到非常规的预处理和后处理反而被插件形态限制住。MindX SDK适合的目标是快速验证业务可行性特别是多路视频流分析二十路以内网络摄像头接入、检测、告警这种场景。你不需要深究每一步的底层实现只要按规范配置好SDK能帮你管住不少繁琐的资源和缓冲逻辑。4.2 Python ACL适合自定义预处理和后处理的全部逻辑如果你追求完全掌控每一帧的处理细节那直接用Python ACL更舒服。ACL是CANN对外提供的统一API层Python接口封装了设备管理、模型加载、输入输出内存管理、推理请求等操作。虽然比MindX SDK原始一些但也更透明。简化的调用流程如下初始化ACL环境指定设备ID加载OM模型拿到模型描述信息为输入输出申请内存拷贝输入数据调用模型执行接口完成推理从输出内存解析结果释放资源最终去初始化。伪码层面长这样import acl # 初始化 ret acl.init() ret acl.rt.set_device(0) # 加载模型 context, model_id acl.mdl.load_from_file(yolov5s.om) # 获取输入/输出尺寸准备buffer input_tensor, output_tensor prepare_memory(model_id) # 推理 ret acl.mdl.execute(model_id, input_tensor, output_tensor) # 后处理 parse_yolo_output(output_tensor)实际使用时还需要处理图像数据转换比如OpenCV读进来是HWC的BGR图要resize、letterbox、转成CHW的RGB float数据再归一化。这些逻辑全部在推理调用之外完成和GPU时代的思路很接近所以做算法出身的人会更容易上手。Python ACL的优点是灵活所有步骤都由你控制。缺点是样板代码多要自己管理buffer生命周期。一个截图不好好释放长时间跑就会出现内存泄漏。所以我的习惯是封装一个推理类把初始化、预处理、推理、后处理、释放都包在类里业务代码只调用一个接口。4.3 这两条路怎么选我从实际项目里总结的判断标准是考虑因素MindX SDKPython ACL开发速度快配置即可慢需要写大量模板多路视频流支持成熟自带解码插件需要自己集成解码定制后处理受插件限制完全可控性能上限较高受益于插件复用取决于你的编码优化调试友好度黑盒较多每步可控好排查如果你是要交付一个视频分析平台时间紧我建议优先MindX SDK。如果你是在做算法原型验证或者需要频繁修改后处理逻辑请直接用Python ACL。两者不冲突有些项目里甚至可以并存SDK负责取流和解码推理和后处理再跳到ACL接口进行。5. 实测中反复踩过的坑与性能调优记录部署YOLO的过程中最消磨耐心的其实不是环境搭建和模型转换而是看起来该对、实际却不对的一系列“隐形问题”。这一节我把自己真正遇到过的坑和调优记录写出来希望能帮你跳过那些我花了好几个通宵才搞明白的弯路。5.1 图像预处理必须和训练保持完全一致用GPU跑YOLO的时候很多人的预处理是“差不多先生”用OpenCV随便resize一下就开始推理结果检测精度下降也不会太明显因为GPU推理容错空间大。但在昇腾卡上精度问题更容易暴露出来因为你一旦用了INT8量化本来精度就有损失如果预处理再不一致结果更难看。YOLO训练的预处理链路通常包含letterbox等比缩放、RGB通道顺序、归一化到0-1。如果你推理时直接粗暴resize到640×640会改变目标的长宽比例小目标检测会明显变差。另外还需要注意BGR和RGB顺序训练用了RGB推理却直接喂BGR模型基本等于白跑。我的建议是把预处理函数单独写出来用同一份代码同时跑GPU和NPU推理确保两边行为一致。针对YOLOv5全流程大致是读图 - letterbox到640×640 - BGR转RGB - 归一化到0-1 - HWC转CHW - 转为NPU要求的float32或float16数据格式。5.2 多路并发时别忽略Stream和上下文切换如果你用Python ACL做多路视频流推理不能简单地在for循环里逐路调用acl.mdl.execute因为每一次推理都可能建立新的执行流频繁切换带来的开销不小。昇腾上建议使用Stream机制在多路输入时尽量复用Stream用异步接口提交推理请求让数据在卡上流水线执行。我第一次做八路视频流检测时没有复用Stream结果平均每路延迟被拖到150ms以上完全达不到实时。后来改成单Stream异步提交配合batch推理延迟才降到50ms以下。核心思路是减少单帧推理的固定开销让多帧尽量并行地占用NPU。如果你的业务允许累积一小批帧再推理用动态batch或固定batch模式会带来明显吞吐提升。5.3 性能瓶颈往往不在NPU而在CPU侧在使用GPU做推理时显存拷贝往往是一个容易被忽视的瓶颈。昇腾卡也一样但更隐蔽的是CPU侧的解码和预处理。如果你的视频流是RTSP拉流解码用FFmpeg软解四路以内可能还好一旦到了八路以上CPU占用会迅速顶上最后发现NPU利用率只有30%CPU已经快被打满。遇到这种情况优先考虑硬解码方案。昇腾环境里MindX SDK自带的视频解码插件会尝试调用硬件解码单元能显著降低CPU负担。如果你走Python ACL路线就需要自己想办法把解码模块替换成支持硬件解码的实现或者干脆把取流和解码部分交给SDK推理部分再回到ACL。我最终用的是混合方案SDK做解码和图像缩放ACL做YOLO推理和后处理效果非常好。5.4 内存复用与零拷贝是吞吐的关键在长期运行的推理服务里反复申请和释放内存会带来两个问题内存碎片和耗时抖动。昇腾接口里模型执行需要输入buffer和输出buffer最简单的方式是每次推理都申请一次但高并发时很容易被拖慢。我的做法是模型加载后就把输入输出buffer申请好推理时直接复用。如果batch固定就把整套buffer按batch_size申请每帧数据往已有buffer里拷。这样虽然牺牲了一点灵活性但整个推理路径上的内存操作降到最低。实测在固定batch场景下推理吞吐能提升20-30%。另外有条件的话优先使用昇腾提供的内存零拷贝接口让数据在同一个设备侧内存池里流转避免host和device之间的来回拷贝。尤其当输入图像不做大幅预处理时零拷贝的优势非常明显。5.5 关于INT8量化的一点心得很多跑业务的朋友希望用INT8把性能再往上推一档但YOLO这类检测模型直接转换成INT8经常会出现精度抖动。我建议先不急着量化用FP16跑通全流程等业务稳定后再尝试校准量化。昇腾上做INT8量化通常需要校准集校准集要尽量贴合真实业务场景的分布否则某些类别可能直接检测不出来。我测试过YOLOv8s在FP16和INT8下的对比INT8单帧延迟大概能再降30%到40%但在一个光照变化剧烈的室外场景里小目标的检出率明显下降。如果你的业务对漏检很敏感就得仔细权衡收益和风险不能盲目追求INT8。最后再说说我的整体感受。Atlas 300V 24G这块卡确实是用于AI部署的运算加速卡但它有自己的生态圈子你不能拿GPU的老经验完全套用。只要愿意花时间去啃环境栈和模型迁移它在线路密度、功耗、成本上的优势是实打实的。尤其是YOLO这类工业检测模型一旦把环境链路走通后续迭代更多就是模型精度和业务逻辑的活了。我自己在项目里还留了一个习惯每次准备部署到昇腾环境前都会先在一台干净的Linux机器上把整个流程无头跑一遍避免在GPU环境里顺手过了、到了NPU环境一地鸡毛的情况。这套流程现在基本成了我们团队的部署标准动作如果你正在从GPU往昇腾迁移可以先参考着做一遍再根据自己的场景逐步优化。
企业数字化 ERP 产品动态
相关推荐
【Python机器学习】零基础掌握降维时如何权衡信息损失与预测效果 把 30 个特征压缩成 4 个成分,图上看起来很清爽,但分类模型可能因此丢掉关键的判别信息。反过来,保留更多方差也不保证预测更好。降维前应先明确目的:是减少计算、处理稀疏矩阵、得到可解释的非负成分,还是改善下游预测。
本文在一组可复现的非负模拟数据上,比较不降维的… · 2026/9/25 6:58:31
jc 实战:将 iptables 命令输出解析为 JSON 的完整指南 开发工具 【免费下载链接】jc CLI tool and python library that converts the output of popular command-line tools, file-types, and common strings to JSON, YAML, or Dictionaries. This allows piping of output to tools like jq and simplifying automation scripts.… · 2026/9/25 6:58:25
Atlas 300V上部署YOLO:从环境搭建到推理调优全指南 上个月项目里要做一批边缘侧的目标检测,手里刚好有一块Atlas 300V,24G显存版本。最开始接到这块卡,我脑子里第一反应和大多数人一样:这到底是不是运算加速卡?能不能像GPU一样直接拿来跑YOLO?网上一搜&#… · 2026/9/25 6:58:25
19个免费PPT网站实测:在线编辑、模板下载与AI辅助工具推荐 1. 为什么我花了两周时间实测这19个PPT网站做PPT这件事,说大不大,说小也绝对不小。我在一家中型企业做品牌策划,平均每个月要出4到6份对外提案,加上内部汇报、季度复盘、培训课件,一年下来经手的PPT少说也有七八十份。… · 2026/9/25 7:33:26
Word尾注脚注管理全攻略:插入、删除与去横线技巧 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:33:26
Simulink建模效率:自动整理连线、显示数据类型与内容自适应 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:33:26
零成本监控回放方案:旧摄像头+树莓派+夸克网盘 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:33:20
彻底关闭OfficePlus:从加载项禁用、注册表修改到完全卸载的完整指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:33:20
SUMO交通仿真入门:从零搭建微观交通场景的核心指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:33:20
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37