“用 Visual Studio 2022 跑 ONNX 模型”这个需求光看标题简单真上手的人都知道里面全是细节。我帮几个项目搭过 ONNX 推理环境踩了不少坑也总结出了一套稳定可复用的流程这篇文章就把它完整拆给你看。不管你是刚拿到一个 .onnx 文件不知道怎么跑还是已经在用 Python 推理但想转到 C 环境都有可以直接抄的配置和代码。先说清楚这篇内容解决什么问题在 Visual Studio 2022 里从头配置一个能加载、运行 ONNX 模型的 C 项目包括环境选型、依赖安装、代码调用、模型来源和常见报错排查。适合 Windows 平台上的 C 开发者、算法工程师以及所有想把模型从 Python 挪到服务端 C 场景的人。1. 整体设计与方案选型1.1 是“ONNX Runtime”还是“ONNX”排第一个坑很多人把 ONNX 和 ONNX Runtime 混着说概念不清后面全乱。ONNXOpen Neural Network Exchange是格式是模型的文件形态就像一张图纸ONNX Runtime 是推理引擎是拿图纸盖楼的施工队。你在 Visual Studio 里要配置的其实是 ONNX Runtime 的运行环境而不是 ONNX 本身。那为什么不直接用 .h5 或者 PyTorch 的 .pt如果你只是做个实验用 PyTorch 直接推理当然没问题。但一旦涉及工程化比如要嵌入到 Windows 桌面软件、要避免 Python 运行时依赖、要提升加载和推理速度ONNX 加 ONNX Runtime 就是更合理的选择。ONNX 把模型从训练框架中解放出来ONNX Runtime 负责在不同硬件上高效执行两者配合模型就可以脱离训练环境独立部署。1.2 项目语言选择C 还是 C#Visual Studio 2022 里跑 ONNX Runtime通常在 C 和 C# 之间选。C# 配 ML.NET 的 ONNX 支持很成熟写起来也快但如果你要的是一个低延迟、无托管依赖的本地推理节点C 是更主流的选择。我这边以 C 为例原因有三个一是 ONNX Runtime 的 C API 覆盖最全很多高级配置比如自定义算子、设备 EP 扩展在 C 里最顺手二是 C 项目编译后可以直接分发运行库不要求目标机器额外安装 .NET 环境三是实际部署场景里C 的性能控制更细内存管理也清楚。你要是用 C#原理完全一样就是 API 形态不同。1.3 依赖管理NuGet 还是 vcpkg在 Visual Studio 2022 里装 ONNX Runtime有两条主路NuGet 包管理器直接引用或者 vcpkg 编译集成。我推荐直接用 NuGet 包Microsoft.ML.OnnxRuntime。为什么这个包官方维护CPU 版本编译好的 DLL 直接可用不需要自己编译 ONNX Runtime 源码——自己编一次至少半小时起步还容易因为 CMake 版本、MSVC 工具集不一致出问题。NuGet 方式安装后会自动引入头文件和 DLL配置简单真正做到“加包就跑”。vcpkg 适合你要定制编译选项的情况比如要指定 OpenMP 开关、要集成 TensorRT 或 CUDA 的特定版本。默认 CPU 推理完全没必要走 vcpkg。1.4 平台架构和依赖方向x64 是最优解新建项目后第一件事把解决方案平台设为 x64。这是我在多次实战中踩出来的硬教训——默认的 Win32x86架构会让你在配置 ONNX Runtime 时非常痛苦因为官方 NuGet 包同时提供了 x64 和 x86 版本但很多系统级的依赖、以及后续你要接的硬件加速库对 x86 的支持都很薄弱。实测下来x64 是稳定可复用的基准选择不要犹豫直接 x64。2. 项目环境准备与核心配置拆解2.1 Visual Studio 2022 版本适配Visual Studio 2022 的 Community、Professional、Enterprise 版本在 ONNX Runtime 配置上没有区别核心是安装了“使用 C 的桌面开发”工作负载。打开 Visual Studio Installer确认以下组件存在MSVC v143 生成工具C 编译器和链接器Windows 10 SDK 或 11 SDK最新 C 生成工具含 CMake 支持非必需但推荐很多人配置 ONNX 环境失败绕了半天最后发现是编译工具链没装全。这一步别省否则后面写好了代码编译直接报“无法打开包括文件”或者“找不到 MSVCP140.dll”全是地基问题。2.2 通过 NuGet 安装 ONNX Runtime具体步骤在 VS2022 里操作具体是这样的方向先是菜单栏“项目” - “管理 NuGet 程序包”在“浏览”页搜索onnxruntime认准包名Microsoft.ML.OnnxRuntime安装最新稳定版当前我常用 1.16 以上的版本API 稳定。装完以后项目的packages.config老式非 SDK 风格项目或者项目文件的PackageReference里都会出现这个依赖。最关键的是NuGet 会自动完成下面几件事复制头文件到项目的 include 路径复制onnxruntime.dll和对应导入库到输出目录设置好链接输入所以你在代码里直接#include onnxruntime_cxx_api.h就能用链接也能直接过。注意如果你看到onnxruntime和onnxruntime-gpu两个包别装错了。GPU 包可以跑 CPU 推理但体积大、依赖多纯 CPU 场景就装基础版小而稳。2.3 仍然要手动确认的配置项NuGet 帮你做完 80% 的事剩下 20% 必须自己看确认输出目录里出现onnxruntime.dll编译一次以后去x64\Debug或x64\Release目录看 DLL 在不在。如果不在说明 NuGet 的copy-local行为被干扰了右键引用里的包 - 属性 - “复制到输出目录”设为“如果较新则复制”。确认项目属性里的“包含目录”和“库目录”如果 NuGet 包恢复异常可能需要手动加。正常恢复情况下这两个值会自动配置不用动。确认“C 语言标准”ONNX Runtime C API 需要 C17项目属性 - C/C - 语言 - C 语言标准选/std:c17。这三条检查完环境基本锁死不容易再出幺蛾子。2.4 环境验证写个最小编码器先跑通配置完环境别急着写业务代码先写一个“加载并打印模型信息”的最小程序确认环境本身没问题。这一步能救你一下午。3. 核心代码流程与实操要点3.1 加载 ONNX 模型Session 创建ONNX Runtime 在 C 里的一切核心都是Ort::Session。你可以把它想象成一个“模型容器”创建它时需要给三样东西环境Env、会话选项SessionOptions、模型路径。代码结构是这样#include onnxruntime_cxx_api.h #include vector #include string #include iostream int main() { // 1. 创建环境 Ort::Env env(ORT_LOGGING_LEVEL_WARNING, test_onnx); // 2. 会话选项 Ort::SessionOptions session_options; session_options.SetIntraOpNumThreads(4); // 并行线程数 session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); // 开启全部图优化 // 3. 加载模型 const wchar_t* model_path Lmodel.onnx; Ort::Session session(env, model_path, session_options); // 4. 打印输入输出信息 Ort::AllocatorWithDefaultOptions allocator; size_t input_count session.GetInputCount(); size_t output_count session.GetOutputCount(); std::cout Input count: input_count std::endl; for (size_t i 0; i input_count; i) { auto input_name session.GetInputNameAllocated(i, allocator); auto input_type_info session.GetInputTypeInfo(i); auto tensor_info input_type_info.GetTensorTypeAndShapeInfo(); std::cout Input[ i ]: input_name.get() shape; auto shape tensor_info.GetShape(); for (auto d : shape) std::cout d ; std::cout std::endl; } return 0; }这段代码解决的是第一个核心问题拿到一个 .onnx 以后先看它“长什么样”——输入叫什么、维度是多少、有几个输出。很多模型跑不通就是你没对上输入输出的形状和数据类型。3.2 构造输入数据 Tensor模型加载出来以后下一步就是把数据喂进去。ONNX Runtime 要求的输入是一个Ort::Value你要把内存数据“包”成 Tensor并告诉引擎数据是什么形状、什么类型。直接看代码// 假设模型输入是 float32形状为 [1, 3, 640, 640] std::vectorint64_t input_shape {1, 3, 640, 640}; std::vectorfloat input_data(1 * 3 * 640 * 640, 0.5f); Ort::MemoryInfo memory_info Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); Ort::Value input_tensor Ort::Value::CreateTensorfloat( memory_info, input_data.data(), input_data.size(), input_shape.data(), input_shape.size() ); // 获取输入名称 Ort::AllocatorWithDefaultOptions allocator; std::vectorconst char* input_names {session.GetInputNameAllocated(0, allocator).get()}; std::vectorconst char* output_names {session.GetOutputNameAllocated(0, allocator).get()};这里的核心思路是Ort::Value::CreateTensor不会复制数据它只是把你的vector内存地址交给推理引擎。什么意思你后续不能再随便动input_data因为推理时它会直接读这块内存。这个细节很关键——有人改完数据发现推理结果不变排查半天发现是内存被复用了但内容已经被覆盖。3.3 执行推理调用推理核心代码// 运行模型 auto output_tensors session.Run(Ort::RunOptions{nullptr}, input_names.data(), input_tensor, 1, output_names.data(), output_names.size()); // 取输出 float* output_data output_tensors[0].GetTensorMutableDatafloat(); auto output_type_info output_tensors[0].GetTensorTypeAndShapeInfo(); auto output_shape output_type_info.GetShape(); size_t output_size 1; for (auto d : output_shape) output_size * d; std::cout Output size: output_size std::endl;一个完整推理的耗时其实很短CPU 上跑一个轻量分类模型可能只要几毫秒。真正影响体验的是“加载模型”和“预处理”——后面详聊。3.4 数据布局NHWC vs NCHW这个坑ONNX 模型的标准布局通常是 NCHW也就是“通道在前”形状是[batch, channel, height, width]。而 Windows 上很多图像库比如 GDI、WIC出来的是 HWC 内存格式尤其是工业摄像头 SDK给的数据大多是 HWC。处理思路有两种在代码里做 HWC - CHW 转置这是最通用的做法。导出模型时把布局设为 NHWC有些框架支持推理前少一步处理但可移植性差一些。我建议走方案 1因为模型来源多变你不能控制对方的导出习惯改自己的预处理代码是最稳妥的。转置代码不复杂但要注意维度索引算清楚// HWC to CHW, 以 [batch1, H, W, C] - [batch1, C, H, W] 为例 void HWC2CHW(const uint8_t* src, float* dst, int H, int W, int C) { for (int h 0; h H; h) { for (int w 0; w W; w) { for (int c 0; c C; c) { dst[c * H * W h * W w] (src[h * W * C w * C c] - 127.5f) / 127.5f; } } } }同时这里我顺手做了标准化像素从 [0, 255] 整数映射到 [-1, 1] 浮点。实际模型可能有自己的 mean/std 要求训练代码里怎么做的推理代码就怎么做两边必须完全一致。3.5 检查模型文件本身是否完好有一种很无语的情况代码没问题、环境没问题但模型文件本身是坏的。建议拿到模型后先用 Python 快速验证一下import onnx model onnx.load(model.onnx) onnx.checker.check_model(model) print(model.graph.input) print(model.graph.output) print(model.opset_import)这个操作的目的是在进 Visual Studio 之前就排除“模型文件损坏”这个变量。做工程有个习惯叫“先隔离变量”模型本身的问题先自己查清楚别让 C 环境背黑锅。4. 模型怎么来PyTorch 转 ONNX 的配套经验4.1 导出时最关键的参数你可能遇到的模型来源大概率是 PyTorch 训练出来的转 ONNX 的核心调用是torch.onnx.export。三个关键参数import torch dummy_input torch.randn(1, 3, 640, 640) model.eval() torch.onnx.export( model, dummy_input, model.onnx, opset_version17, input_names[input], output_names[output], dynamic_axes{input: {0: batch_size}, output: {0: batch_size}}, )这里opset_version是个关键数字。ONNX 的算子集版本越高表达能力越强。Visual Studio 里用的 ONNX Runtime 新版本一般都能兼容高版本 opset但如果你要部署到旧版引擎opset 太高反而会报“Unsupported Operator”。我的建议是ONNX Runtime 跟着 NuGet 走最新稳定版导出时 opset 设一个适中值比如 15~17两边都好过。dynamic_axes是根据你自己的部署需求来的。如果推理时的 batch 永远是 1不要开动态轴静态 shape 的推理速度和内存占用都更友好如果可能有多 batch 需求就把 batch 维度设成动态。4.2 导出后必须做的验证在 Visual Studio 里跑之前先在 Python 里跑一次导出后的 ONNX 模型确认导出的输出和 PyTorch 原模型的输出一致import onnxruntime as ort sess ort.InferenceSession(model.onnx, providers[CPUExecutionProvider]) outputs sess.run(None, {input: dummy_input.numpy()}) print(outputs[0].shape)这一步能拦截掉 90% 的“模型导出错”案例——比如某个层不支持导出、动态 shape 设置错误、或者变量被追踪导致静态化了。先在 Python 端验证完毕再去 C 里跑整个过程会顺很多。4.3 简化 ONNX 模型可选操作有时候导出的 ONNX 图里含有一堆冗余节点比如 Identity、Cast 链、Dropout 残留等。用onnxsim简化一下能让模型更小、跑得更快pip install onnxsim python -m onnxsim model.onnx model_sim.onnx但注意简化模型后记得重新跑一遍输入输出确保精度不掉。有些极端情况下 onnxsim 会改变图的执行细节虽然不影响输出但对调试不友好我自己用它的时候都会保留原始模型文件做对照。4.4 量化模型INT8相关热搜里有“onnx量化int8”简单说两句。如果你想把模型压到 INT8 追求速度通常用 onnxruntime 的量化工具from onnxruntime.quantization import quantize_dynamic, QuantType quantize_dynamic(model.onnx, model_int8.onnx, weight_typeQuantType.QInt8)这个操作不需要重新训练是训练后量化。但我要提醒动态量化主要压缩权重激活值还是浮点速度提升有限静态量化要校准数据集麻烦一些但效果更好。决定量化前先想清楚目标硬件——CPU 上 INT8 是有加速效果的但如果你用的是 ARM 或者某些老 x86 芯片不一定有对应的内核实现。这块到 Visual Studio 里跑通浮点模型后再考虑量化别上来就量化否则排查问题时会多一层变量。5. 常见问题与排查技巧实录5.1 常见报错速查表报错内容原因解决方向无法打开包括文件 onnxruntime_cxx_api.hNuGet 引用未生效或头文件路径缺失检查 NuGet 包是否恢复成功手动确认包含目录找不到 onnxruntime.dll运行时 DLL 未复制到输出目录右键引用设置“复制到输出目录”检查 x64/Debug 目录OSError / 加载动态库失败平台架构不匹配x86 装了 x64 包确认解决方案平台是 x64输入名称不匹配代码里写死的输入名和模型实际名不一致用 GetInputNameAllocated 打印输入名别手写维度不匹配输入 Tensor shape 和模型输入 shape 对不上打印模型的 GetShape 信息逐维度对照算子不支持opset 版本过高或过低模型导出的算子太新换个 opset 重新导出升级 ONNX Runtime 版本推理结果全是 0 或固定值输入数据预处理不对标准化、排列顺序检查 HWC/CHW、均值方差、数据类型 float/int这个表格基本就是我在实际配置里碰过的全部问题类型照着对号入座多数情况下十分钟能定位。5.2 Debug 和 Release 混用问题这是个很阴间的坑Debug 编译的项目链接了 Release 的 onnxruntime 导入库或者反过来。ONNX Runtime 官方 NuGet 包默认对 Debug 和 Release 都提供同一个 DLL但 MSVC 的运行时库/MD 与 /MDd是有区别的。如果出现“检测到 RuntimeLibrary 不匹配”的错误去项目属性 - C/C - 代码生成 - 运行库确保 Debug 下是/MDdRelease 下是/MD不要乱改。我个人建议推理项目从头到尾就用 Release 配置Debug 留给逻辑调试。反正 ONNX Runtime 日志级别可以调一般排查业务逻辑不需要 Debug 编译。5.3 单线程还是多线程ONNX Runtime 默认会用多线程执行算子这是好事但也不是越高越好。SetIntraOpNumThreads设置的是每个图节点内部的并行线程数。我实测过4 线程和 8 线程在轻量模型上差距很小线程太多反而增加调度开销。如果部署的机器是多程序并行的服务端建议把线程数设小一点或者设成等于物理核心数的一半避免 CPU 争抢。另外还有SetInterOpNumThreads这个控制不同节点之间的并行一般不用动默认就行。5.4 模型加载过慢怎么优化ONNX Runtime 每次创建 Session 都会做模型加载、图优化、内存规划。对于大模型比如 LLM、检测模型这个加载时间可能是秒级。如果程序要频繁启动建议思路如下开启模型缓存session_options.SetOptimizedModelFilePath(Loptimized_model.onnx)第一次加载后引擎会把优化后的模型落盘之后每次加载直接读优化结果快很多。常驻 Session如果是服务端程序不要在每次请求时都新建 Session创建一个全局的 Session 对象复用它。实测省掉的开销非常可观。用内存加载模型不要每次从磁盘读可以一次性加载到内存然后用Ort::Session::CreateFromArray这类内存加载接口。对于频繁重启的场景很管用。5.5 集成到实际业务里的建议最后讲点工程化的体会。ONNX Runtime 配置本身不难难的是和既有代码的衔接。我自己做项目的流程是先搭一个独立的“推理模块”输入是图像或数组输出是结果结构体不把 ONNX Runtime 的调用散落在业务代码里。这个模块只做四件事Session 初始化、预处理、推理、后处理。这样就保证了以后换模型、换硬件加速比如从 CPU 换到 CUDA时业务层代码一行不用改。CPU 推理在延迟敏感的场景下确实不如 GPU但只要模型不大、优化开满很多场景 CPU 完全扛得住。也不用一上来就追求 GPU 加速先把 CPU 全链路跑通再考虑扩展 EPExecution Provider。ONNX Runtime 的 CUDA EP 是后续可以单独开一篇的话题这里只提醒一句接入 GPU 前先把 CPU 的输入输出、内存生命周期、预处理逻辑全部稳定下来不然到时候两套问题混在一起查到你怀疑人生。另外ONNX Runtime 的官方 C 示例在 GitHub 的microsoft/onnxruntime仓库里遇到细节问题多翻源码比搜博客管用。我自己几次折腾下来最大的心得就是配置环境这件事一定先小步验证、再大步集成。项目刚建好先跑通加载和打印模型信息确认没问题再写数据预处理再确认没问题再接入真实业务流程。每一步都有明确的输出判断出问题也能迅速锁定在哪一层。这套流程我用在多个项目上都很稳照着做你也能少熬几个夜。
企业数字化 ERP 产品动态
相关推荐
.NET 5 WinForms 免注册调用大漠插件:原理、代码与避坑指南 简介:面向C#桌面开发者的实战型资源包,聚焦在Windows 10环境下基于.NET Core 5.0的WinForm程序免注册调用大漠插件(dm.dll),解决传统注册方式带来的权限与部署难题。包内共204个文件,以78个dll动态库、40个… · 2026/11/5 10:18:54
基于Multisim的差动电容测距电路仿真与线性度分析 简介:这份资源面向电子信息、自动化等专业学生及传感器课程学习者,围绕差动电容传感器测距电路,提供一套可在Multisim中复现的虚拟仿真实验方案,帮助解决从传感器建模到电路调试的完整实践问题。压缩包共1个docx文件,约… · 2026/11/5 4:20:06
从零构建AI工程能力:模型部署、监控与MLOps实战路径 1. 为什么我会说“从零开始学 AI 工程”是一条更值得走的路先交代一下背景。我做了将近十年的软件开发和数据相关的工作,前几年开始把重心完全转向 AI 工程方向,带过团队、做过从 0 到 1 的项目,也面试过不少想转行进来的候选人。这两年大家总… · 2026/11/2 9:38:40
基于Hadoop+Spark的民宿智能推荐系统设计与实现 1. 项目概述:大数据时代的民宿智能推荐这个毕业设计项目融合了当前大数据领域最主流的技术栈,构建了一个完整的民宿推荐系统解决方案。作为一名长期从事大数据系统开发的工程师,我认为这个选题非常贴合实际应用场景——随着旅游业的复苏和共享… · 2026/11/5 14:59:25
ADS122U04IPWR+STM32F427ZI工业级温度测量信号链设计 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
UART接口24位ADC搭配RS485,解决远距离温度监控难题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
UART接口24位ADC搭配PIC18F46K22的RTD与热电偶测温实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
GD32E10x官方例程入门:从GPIO点灯到串口调试 简介:这份GD32E10x系列官方例程面向嵌入式硬件与单片机开发者,基于ARM Cortex-M3内核,汇集兆易创新GD32E10x的示例代码,适合工业控制、消费电子、通信设备等场景的学习、验证与二次开发。压缩包共935个文件,约11.16MB&… · 2026/11/5 13:52:29
基于Django的运维平台实战:资产、发布、巡检、告警一体化 简介:这份资源是一套基于Django框架开发的运维平台源码,面向具备Python基础、希望了解Web运维系统实现思路的开发者与运维人员,可用于学习主机管理、服务监控、日志查看、任务调度、权限控制、资源统计、脚本执行与故障告警等常见运维模块的落… · 2026/11/5 13:49:27
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07
多智能体协作系统架构设计与实践优化 1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39
大模型系统性入门实战指南:从原理到部署微调的全链路路线 提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39
鸿蒙文件系统沙箱机制解析与开发实践 1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07