TrueTime 2.0 网络化运动控制仿真环境搭建与调度实践

发布时间:2026/11/4 17:03:53
TrueTime 2.0 网络化运动控制仿真环境搭建与调度实践 简介面向使用 MATLAB 进行网络化运动控制仿真、资源调度实验的师生与工程人员这份 docx 文档围绕 TrueTime 2.0 的入门使用与配置展开适合需要搭建工具箱环境、理解模块组成并复现实例的读者。压缩包内共 1 个 docx 文件大小约 402KB以图文形式整理工具箱安装、路径与环境变量配置、模块说明及仿真实验要点内容集中便于按章节查阅。已有 1593 人学习说明其在 TrueTime 入门资料中具有一定参考度。读者可据此了解 TrueTime 2.0 的整体结构、常见配置思路与仿真实验场景并补充最大误差优先调度、优先级反馈控制等运动控制知识点适合用于课程实验、课题预研和配置排错时的速查参考。1. 一套挂机多年的工具箱为什么现在还有人翻出来装某高校实验室交付的一份仿真报告里网络化运动控制回路的调度曲线是在 TrueTime 里跑出来的。真要在纯 Simulink 里拿 Transport Delay 模块硬凑抖动和丢包一叠加曲线立刻对不上。TrueTime 2.0 就是一套挂在 MATLAB/Simulink 下的网络化控制系统仿真工具箱它把实时内核、网络协议栈和被控对象塞进同一张 Simulink 框图资源调度到底卡在哪一拍才看得见。这份 truetime2.0使用说明.docx 记录的是从解压、配路径、配环境变量一路走到 make_truetime 出现 successful 的全过程适合手里有 MATLAB、要复现网络化运动控制仿真、又被 mex 编译器卡在半路的人。下面按我自己的装机顺序重排一遍顺手把原文档里几处现在容易翻车的地方标出来。2. 装之前先看清楚TrueTime 2.0 的三层结构与编译链选型很多人一上来就照着步骤表敲命令结果卡在 mex 那一步就开始怀疑人生。问题不在命令在于没搞清楚这套工具箱到底由哪几块拼成、哪一块必须编译、哪一块只是 M 文件。先把结构理清后面的报错基本能自己对上号。2.1 Kernel、Network 与被控对象三个角色各管什么TrueTime 的仿真模型里一个典型的网络化运动控制回路至少要摆三类模块。第一类是 TrueTime Kernel 模块它代表一台挂在网络上的计算节点可能是控制器也可能是传感器或者执行器内部跑的是用 M 文件写的内核代码——任务创建、优先级设定、周期、报文收发全在这个模块的初始化回调里描述。第二类是 TrueTime Network 模块它模拟节点之间那根线支持总线型、交换式以太网、令牌环之类的常见拓扑报文延迟、排队、丢包都由它算出来。第三类是被控对象也就是普通的 Simulink 连续域模块电机模型、机械臂连杆模型都行通过 A/D、D/A 端口和 Kernel 对接。这个结构决定了仿真精度落在哪里。纯 Simulink 加延迟模块你能看到一个平均延迟看不到某个控制任务因为优先级低而被高优先级任务抢占、错过截止期的那一拍。Kernel 模块把调度器显式建模出来网络模块把排队时间和冲突显式建模出来两头一夹控制品质的退化原因才可拆解。做运动控制的人真正关心的往往不是稳态误差而是第 37 个采样周期那次抢占到底造成了多大抖动——这个问题只有把内核和网络都建进去才回答得了。所以选型上要有个判断如果你的课题只是验证一个线性控制器在固定延迟下的鲁棒性纯 Simulink 完全够用不必引入 TrueTime因为它的学习成本和编译链麻烦是真实存在的。反过来只要你的论文或项目里出现调度策略优先级网络丢包截止期错过率这类词TrueTime 几乎是绕不开的自己从零写一个带调度器的 Simulink 框架工作量远大于装一次工具箱。2.2 为什么绕不开 mexM 文件外壳包着 C 内核原文档里最关键的一步是make_truetime这一步之前在mex -setup里选编译器。很多人不理解为什么一个MATLAB 工具箱非要装 gcc。原因是 TrueTime 的内核调度器和网络仿真引擎是用 C 写的为了速度编译成 MEX 文件在 MATLAB 进程内直接跑。你解压之后看到的那些 .m 文件只是外壳——初始化函数、模块回调、参数解析真正的调度逻辑跑在由 .cpp 编译出来的二进制里。这就解释了为什么会出现truetime命令能打开模块库、一仿真就报 undefined function这种诡异现象库浏览器读的是 .m 和 .mdl能加载仿真时内核要调 MEX 文件那个文件没编译出来或者编译失败就报未定义。所以编译器不是可选项是硬性前置依赖。编译器怎么挑这里有个现实边界。Win7 时代配 MATLAB R2017b原文档用的是 tdm64-gcc-5.1.0-2这套组合当年验证过能过。放到今天MATLAB 从 R2019a 前后开始对 MinGW 系编译器的支持收紧版本对不上会直接在mex -setup里拒绝注册。我一般会先把本机 MATLAB 的版本号确认清楚再去查它官方支持哪个编译器版本卡死了就退回用 Visual Studio 的 C 工具链虽然装起来重但兼容性最稳。别死磕一个装不上的 gcc那是最容易耗掉半天的地方。2.3 版本三方对齐表MATLAB、编译器、TrueTime 三者不能乱配这套东西的版本敏感度比一般工具箱高因为它同时依赖 MATLAB 的 mex 接口、编译器 ABI 和自身 beta 版的代码。下面这张表是我踩过几次之后总结的判断依据具体版本号以自己机器上的为准。组合项老环境可复现新环境需重编判断依据MATLABR2017b 附近R2019a 及以后mex -setup的语法在两代之间有差异编译器tdm64-gcc 5.1.0MSVC 或受支持的 MinGW编译器是否出现在mex -setup列表里TrueTime2.0 beta 1同版本但需重新make_truetimeMEX 二进制与 MATLAB 版本绑定位数64 位64 位MATLAB 是 64 位就必须配 64 位编译器最要紧的一条MEX 文件是跟 MATLAB 版本绑定的。你在 R2017b 上编译出来的 mex 文件换到 R2021b 上多半加载不了必须在新版本里重新跑一次make_truetime。这不是工具箱的毛病是 MATLAB 本身的机制所以任何把整个 toolbox 目录拷贝到新电脑就能用的设想都不成立路径和二进制都得重来。3. 按顺序落地从解压到 make_truetime 出现 successful步骤本身不复杂复杂的是顺序——先配路径还是先编 MEX、环境变量什么时候生效、startup.m 要不要跟环境变量一起上。原文档的顺序基本可抄但有几处可以省掉的冗余动作我在下面按实际操作顺序重排每一步都写清楚它在解决什么问题。3.1 目录落位放 toolbox 下不是必须但有它的道理原文档的做法是把解压出来的 truetime-2.0 整个目录复制到 MATLAB 安装目录的 toolbox 文件夹里。这个做法能成但不是唯一解。放 toolbox 下的好处是路径相对固定用matlabroot拼路径的时候不容易出错多个项目共享同一份坏处是 MATLAB 升级或者重装会把这个目录清掉而且某些安装位置需要管理员权限才能写入复制过程中容易半途失败留下个残缺目录。如果这台机器是你自己的开发机我一般会把它放在一个独立的、纯英文无空格的工作目录比如D:\work\matlab\truetime-2.0然后在 startup.m 里用绝对路径加进去。这样 MATLAB 升级不影响多个版本共存时也能各自指各自的路径。下面这条原则比具体路径重要% 路径里不要出现中文、空格、括号 % 反例D:\我的文档\MATLAB 工具箱\truetime-2.0 % 正例D:\work\matlab\truetime-2.0逻辑说明gcc 在编译时会把路径原样传给底层工具链遇到空格会当成参数分隔符遇到非 ASCII 字符在某些 locale 下编码不一致表现出来就是文件明明存在编译报找不到源文件。这类错误信息通常很含糊排查成本极高索性一开始就别踩。参数说明路径深度不必刻意压平但整个路径长度建议控制在 100 字符以内Windows 的老路径长度限制在编译链里仍然可能触发。解压完之后先别急着配路径进去看一眼目录结构。根目录下会有 kernel、init 之类的子目录kernel 里藏着真正的 M 文件和 .cpp 源文件。这个目录长什么样直接决定了下一步 setpath 要加到哪一层。3.2 setpath 到底加哪一层一次加对别反复返工原文档里提到共包含 4 个关于 truetime-2.0 的文件最好都包含进去也可只包含到 kernel 的文件夹如果出错再重新包含进去。这段话反映的是实际装机时的反复加少了找不到函数加多了路径冲突。稳妥的做法是用genpath一次性把 kernel 下面所有子目录都加进去比手工点 setpath 对话框一层层勾选可靠得多。% 在 MATLAB 命令窗口执行或写进 startup.m ttRoot D:\work\matlab\truetime-2.0; addpath(genpath(fullfile(ttRoot, kernel))); % 连同子目录一起加入搜索路径 addpath(ttRoot); % 根目录供 make 脚本定位 savepath; % 把当前路径存下来下次启动自动生效逻辑说明第一行addpath(genpath(...))是核心genpath会递归返回该目录下所有子目录拼成的路径串一次调用就把 kernel 及其全部子目录挂上避免了加了一层、发现还缺一层的循环。第二行把根目录也加上是因为make_truetime这类脚本常常用相对路径去找同级目录里的源文件只加 kernel 会让它找不到。第三行savepath把结果写进 pathdef下次启动不用重来如果你更倾向用 startup.m 管理见下一节这一行可以省掉否则两套机制会互相覆盖。参数说明genpath默认会跳过以private、开头的特殊目录这是 MATLAB 的既定行为不用管但如果你的解压目录里混进了.git、__MACOSX之类的垃圾目录也会被一并加进路径虽然通常无害但会让path输出很乱装完顺手清一下更舒服。验证方式很直接在命令窗口敲which ttInitKernel能返回一个 .m 文件的完整路径就说明路径通了返回not found就是没加对别往下走先把这一步解决。3.3 环境变量与 startup.m两条路选一条别都上原文档在这一步给了两套动作先在系统环境变量里新建再改 startup.m还额外让你把 matlabrc.m 另存成 matlab.mat。当年这么做大概是因为不同 MATLAB 版本的启动加载机制不一样索性全都上一遍保证能生效。放到现在两条路选一条就够了都上的结果往往是启动时路径被加载两次path里出现重复条目虽然多数时候只是警告但排错时会被误导。走环境变量这条路做法是在系统变量里新建一条变量值指向 truetime-2.0 的根目录让 MATLAB 启动时从环境里读到初始搜索路径。走 startup.m 这条路则是在 MATLAB 安装目录的toolbox\local\下编辑 startup.m写入上一节那几行 addpath。前者对机器上所有 MATLAB 版本生效后者可以按版本精细控制我一般选后者。% toolbox\local\startup.m —— MATLAB 每次启动时自动执行 ttRoot D:\work\matlab\truetime-2.0; if exist(ttRoot, dir) addpath(genpath(fullfile(ttRoot, kernel))); addpath(ttRoot); else warning(TrueTime 目录不存在检查路径配置); % 换机器时不会静默失败 end逻辑说明exist(ttRoot, dir)这个判断很值得加。工具箱目录被移动、被清理、或者换了一台没装 TrueTime 的机器时没有这个判断startup.m 会直接报错MATLAB 启动过程被打断表现是一堆莫名其妙的红字加上之后只留一条 warning不影响其他工作。参数说明dir参数表示只判断目录存在性返回值是 7如果你把这段贴进别的脚本注意ttRoot变量名不要和工程里已有变量冲突。注意设置完环境变量不需要重启电脑把所有 MATLAB 进程关干净再重新打开就够了。环境变量只对新启动的进程生效已经开着的 MATLAB 窗口读不到新值这一点原文档里让重启电脑其实是当年为了省事的一刀切说法。至于把 matlabrc.m 原样另存为 matlab.mat 那一步属于老版本的遗留经验作用接近于强制重建一次启动缓存。新版本里通常不需要如果你照做之后发现 MATLAB 启动变慢或者行为异常把那个 matlab.mat 删掉即可复原。3.4 mex -setup 到 make_truetime完整执行链和每一步的失败信号编译器装好之后先在命令窗口让它被 MATLAB 认出来。这一步的语法在不同版本之间有差异老版本是交互式选择新版本要求直接指定语言。% 先看 MATLAB 认不认这个编译器 mex -setup C; % R2019a 及以后用这个写法 % mex -setup; % R2018b 及以前交互式列表里选逻辑说明mex -setup C会在已注册的编译器里找 C 工具链并写进 mex 的配置文件成功时会打印出编译器名称和版本。如果报找不到支持的编译器问题不在 MATLAB在于编译器没装、位数不对、或者版本不被当前 MATLAB 支持这时候别反复敲同一条命令去查版本对应关系。编译器的具体安装这里不展开但有一个细节必须提装完之后如果mex -setup仍然看不到把 MATLAB 完全退出再进。PATH 环境变量的更新对已运行进程不可见很多人卡在这一步以为是编译器不兼容其实只是没重启 MATLAB。接下来是真正编译。先切到 truetime 根目录再执行 make 脚本。cd(D:\work\matlab\truetime-2.0); % 必须先切到根目录 make_truetime; % 编译所有内核与网络的 MEX 文件逻辑说明make_truetime是一个 M 脚本内部用相对路径去遍历各个子目录里的 .cpp 文件并逐个调用 mex所以工作目录必须是根目录在别的目录下调用会因为找不到源文件而失败。执行过程中会刷出一大段编译输出中间夹杂警告比如某些类型转换、废弃 API这些警告按原文档的说法可以先不管只要最后出现 successful 就说明编译通过。参数说明如果只想重编某一部分可以直接对单个 .cpp 调mex例如mex -O kernel\xxx.cpp-O是开启优化速度差别在长时间仿真里能感觉出来。真正要关注的是failed字样和它前面的那一行文件路径那才是问题所在。编译通过后在命令窗口输入truetime会弹出工具箱的模块库浏览器能看到 Kernel、Network、Battery 之类的模块。到这一步装机完成可以开始搭模型了。4. 跑通第一个回路网络化运动控制的仿真骨架怎么搭装完只是拿到工具能不能跑出有意义的曲线是另一回事。这一章用一个最小可运行的网络化运动控制回路把整条链路串起来被控对象在连续域控制器在 Kernel 里按周期跑中间隔一个 Network 模块。这个骨架搭通之后换被控对象、换调度策略都是局部替换。4.1 搭骨架三个模块怎么连、采样时间怎么定在 Simulink 里新建一个空模型摆三样东西一个 TrueTime Kernel 模块当控制器节点一个 TrueTime Network 模块当总线一个普通的连续传递函数当被控对象。连线方式是参考输入和对象输出进 Kernel 的 A/D 端口Kernel 的 D/A 端口出控制量给对象Kernel 的收发端口连到 Network。这个拓扑在模块库的示例里能找到近似版本直接改比从零搭快。采样时间是第一个要定的参数。控制任务的周期决定了 Kernel 里任务的触发间隔也决定了这个仿真模型该用什么样的步长。经验上仿真步长要比最快任务周期小一个量级否则调度事件会被跨过去看到的结果是任务从来不抢占全是理想调度。参数建议取值说明控制任务周期10 ms运动控制的常见量级可按对象带宽调整网络比特率依协议设定总线型与交换式差异明显仿真步长周期的 1/10 左右太大会丢调度事件仿真时长至少覆盖 50 个周期否则统计量没有意义网络模块的协议类型要和你的研究场景对得上讲优先级调度一般用总线型讲交换式以太网的排队则选交换式。选错了不是跑不出结果是结果解释不通。4.2 写内核代码任务、优先级与周期Kernel 模块的行为由一个初始化函数定义函数名填在模块对话框的初始化回调里。这个函数里做三件事声明调度策略、创建任务、把任务和具体的代码函数绑上。function node_init % Kernel 模块的初始化回调声明调度策略并创建周期控制任务 ttInitKernel(prioFP); % 固定优先级调度 ttCreateTask(ctrl_task, 0.010, ctrl_code); % 名称、周期(s)、入口函数 % 报文到达的处理函数优先级设得比控制任务高 ttCreateHandler(msg_handler, 1, net_msg_handler); end逻辑说明ttInitKernel的第一个参数决定调度器类型固定优先级是最常用的一种换成绩效更好的策略只是换这个字符串。ttCreateTask的三个参数分别是任务名、周期和入口函数名入口函数名不带.m后缀写错会报找不到函数。ttCreateHandler注册的是中断式处理网络报文到达时触发优先级数值越小越高设得比控制任务高才符合中断抢占任务的真实行为。参数说明各 beta 小版本之间ttCreateTask的入参顺序可能略有出入有的版本要求先给偏移量再给周期。动手之前先help ttCreateTask看一眼本机签名这半分钟能省掉后面半小时的调试。任务本体就是一个普通的 M 函数从端口读数、算控制量、写回端口。function ctrl_code % 控制任务主体读输入、算控制量、发报文 r ttAnalogIn(1); % 参考输入 y ttAnalogIn(2); % 对象输出 u 1.2 * r - 0.08 * y; % 一个极简的比例控制先跑通再换算法 ttAnalogOut(1, u); % 送给被控对象 ttSendMsg(2, 64, u); % 发往节点 264 字节负载 end逻辑说明ttAnalogIn和ttAnalogOut的编号要和 Kernel 模块上端口的顺序对上对不上就是读数错位表现出来是曲线形状对但幅值离谱。ttSendMsg的第一个参数是目的节点号第二个是字节数第三个是负载字节数会直接影响网络模块算出来的传输时间别随手填个 1。4.3 调度策略换挡从固定优先级到最大误差优先跑通之后最有价值的一步是换策略对比。固定优先级简单但周期任务在过载时的表现很脆单调速率按周期定优先级适合周期已知的场合最早截止期优先在理论上更优过载时却容易连锁失效。原文档提到的最大误差优先属于自定义策略逻辑是让误差大的回路先拿到计算资源这在多回路运动控制里很实用。自定义调度一般通过注册一个优先级函数实现调度器在每个调度点把当前就绪的任务集合交给你你返回一组优先级数值。function prio prio_mef(now, tasks) % 自定义调度误差大的任务优先返回优先级向量 n numel(tasks); prio zeros(1, n); for k 1:n e tasks(k).err; % 该任务当前的控制误差 prio(k) n - round(e * 100); % 误差越大数值越小优先级越高 end end逻辑说明返回值必须是和任务数等长的向量数值越小代表优先级越高这一点和很多实时系统的约定相反写反了就是完全相反的调度效果而且不报错只能从波形上看出来。参数说明tasks结构体里的可用字段随版本变化误差值可能是内置字段也可能需要自己维护用之前先打印一次看看里面到底有什么。换策略的时候一次只改一个变量别同时改周期和策略否则结果差异归因不了。我一般会固定同一组初始条件跑三遍——固定优先级、单调速率、自定义策略——把三条曲线叠在同一张图上错过截止期的次数直接数得出来。5. 避坑清单五个让我返工过的编译与运行故障装机过程的报错大多集中在编译链仿真阶段的报错大多集中在路径和绑定关系。下面五条是我自己返工过的按现象 → 原因 → 解决写遇到同类症状可以直接对号入座。5.1 现象mex -setup 里一个编译器都列不出来现象是敲完命令只回一句找不到支持的编译器但编译器明明装了安装目录里也能看到 exe 文件。原因是三条中的一条装的是 32 位编译器而 MATLAB 是 64 位编译器版本超出当前 MATLAB 的支持范围或者 PATH 里虽然有但 MATLAB 进程启动在设置 PATH 之前。解决顺序是先确认位数computer命令返回带PCWIN64就说明要 64 位编译器再确认版本对应关系对不上就换最后把 MATLAB 全部退出重开让新 PATH 生效。这三步走完还不行就换用 Visual Studio 的 C 工具链装一次虽然重但它和 mex 的兼容性最稳。5.2 现象make_truetime 跑到一半报 error但 truetime 命令还能打开库现象是模块库浏览器能弹出来看上去装好了一仿真就报某个函数 undefined。原因是make_truetime是逐个文件编译的中途某个 .cpp 失败了脚本继续往下跑其余文件都成功于是你得到一个半成品工具箱。库浏览器只读 .mdl 和 .m所以能开仿真要调那个没编出来的 MEX于是崩。解决方法是把make_truetime的完整输出往上翻找failed前面的那行文件路径记下来单独对它执行一次 mex报错信息会清晰得多。常见诱因是那个文件用了当前编译器不认的语法特性换个编译器版本通常就好了。5.3 现象仿真跑起来波形是一条直线Kernel 没有任何输出现象是模型能编译、能仿真、不报错但 D/A 端口输出恒为零示波器上一条平线。原因多半是 Kernel 模块的初始化回调没绑上对话框里填的函数名和实际 .m 文件名对不上或者填的时候带了.m后缀。函数根本没被调用任务没创建自然没有输出。解决办法是把函数名去掉后缀再填一遍然后在初始化函数第一行临时加一句打印仿真时看命令窗口有没有输出有输出就说明绑上了。这一招比反复检查对话框可靠得多。5.4 现象换了个 MATLAB 版本truetime 能开但一仿真就报错现象是同一台机器上装了第二个 MATLAB 版本工具箱路径也加了库浏览器能开一跑就报某个 MEX 文件无效。原因是 MEX 二进制和 MATLAB 版本强绑定老版本编出来的文件在新版本里加载不了。解决办法是在新版本里重新跑一遍make_truetime。注意别把两个版本的编译产物混在一起各自编各自的。如果两个版本要长期共存我一般会给每个版本单独配一份 startup.m 里的路径变量避免互相踩。5.5 现象编译时报找不到源文件但文件明明就在那儿现象是 mex 报错说某个 .cpp 打不开你去目录里看文件确实存在路径也没写错。原因是路径里有空格或中文编译链把路径拆错了。这种情况在把工具箱放在Program Files或者中文用户名目录下时特别常见。解决办法是把整个 truetime-2.0 目录挪到纯英文、无空格、层级较浅的位置比如D:\work\matlab\然后重配路径、重新编译。这个坑最没技术含量但最耗时间所以我在 3.1 节把它放在最前面讲一开始就避开比事后排查划算。6. 把仿真变成数据批跑脚本与三次结果自检单次跑出一条好看的曲线和拿到可以写进报告的结论中间隔着一层自动化。手工改参数、点仿真、截图跑五组就烦了跑二十组必然出错。我一般的做法是写一个小脚本把参数扫描和结果落盘包起来跑完直接拿到一张表。% sweep_run.m —— 扫控制周期把最大误差和均方根误差落成 CSV periods [0.005 0.010 0.020]; res zeros(numel(periods), 3); for i 1:numel(periods) Ts periods(i); assignin(base, Ts, Ts); % 供模型里的变量读取 out sim(ncs_demo.slx, StopTime, 10); e out.err.signals.values; % 误差信号提前在模型里引出 res(i, :) [Ts, max(abs(e)), sqrt(mean(e.^2))]; end writematrix(res, sweep.csv); % R2019a 起可直接写矩阵逻辑说明assignin(base, ...)是把当前周期值塞进基础工作区模型里的模块参数直接引用Ts就能读到省去每次手改模块对话框。sim的第二个参数控制仿真时长取十个秒足够覆盖几百个控制周期。误差信号要提前在模型里用 To Workspace 模块引出来并命名脚本才能按名字取到。参数说明writematrix在新版本可用老版本换成csvwrite或dlmwrite函数名不对会直接报错改一行就行。拿到这张表之后我不看曲线先看三个数最大绝对误差有没有随周期缩短而单调下降均方根误差和最大误差的比值是否稳定以及有没有某一组的最大误差突然跳一个量级。第三条最关键——跳变通常意味着那一组刚好踩到了某个任务的截止期而这正是这类仿真想抓的东西抓到了就别急着调参把它抹平先想想它对应的是哪个调度事件。还有一个习惯值得养成每次改完路径或重编 MEX先在命令窗口敲一遍which ttInitKernel和truetime两个都正常再开模型。这三十秒的检查我吃过亏才坚持下来——有一次重装 MATLAB 之后没注意 MEX 没重编模型跑了四十分钟出来一条全零曲线白等一场。从那以后我每次换环境都强制走一遍这两条命令再动手希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询