首页 / 资讯中心 / 文章详情

PSS/E自定义模型开发:标志位驱动、STRTIN与USRMDL排查

PSS/E自定义模型开发:标志位驱动、STRTIN与USRMDL排查 ★ FEATURED ARTICLE
简介面向电力系统动态仿真学习者这份PSS/E自定义模型学习资料围绕用户自建模型的需求展开适合已具备PSS/E基础、需要编写Fortran模型子程序并接入动态仿真的工程师与研究人员。文档从标志位MODE、KPAUSE、MSTATE、MIDTRM切入说明TBLCNC、CONEC、CONET等连接子程序如何按标志量调用模型语句并梳理SUBROUTINE modelname(MC,ISLOT)的书写规范、STRTIN数组入口含义及机组相关模型的自动调用机制。资源包共1个doc文件约27KB篇幅精简便于集中查阅与打印批注内容还涉及发电机、电流补偿器、励磁稳定器、励磁、汽轮机/调速器与混合模型的调用顺序以及自定义模型逻辑书写步骤和USRMDL记录格式。已有680人学习可帮助读者理解动态数据输入格式、模型初始化与调用链路减少自建模型时的接口、数组指针与地址配置困惑。1. 一份 Fortran 子程序为什么值得单独整理成资料PSS/E 自定义模型的门槛不在 Fortran 语法语法三天就能捡回来。真正让人卡住的是你写完SUBROUTINE、编译通过、DYRE 也不报错一跑动态仿真就发散然后完全不知道框架在什么时候调了你的哪一段代码。这份资料的价值就在这儿——它把标志位MODE、KPAUSE、MSTATE、MIDTRM、STRTIN 指针、模型调用顺序、USRMDL 记录格式这几件事串成一条线讲的不是怎么写 Fortran而是PSS/E 主体框架怎么驱动你的模型子程序。适合两类人一类是潮流已经跑顺、准备上手自定义励磁、调速器、电力系统稳定器模型的人另一类是接手了别人的模型代码面对满屏IF (MODE .EQ. ...)分支需要先搞清楚每个分支在仿真时序里的位置。PSSE 自定义模型这块网上能找到的多数是片段这份资料难得的是把调用方式和调用顺序放在一起讲了而这恰恰是翻车最集中的地方。2. 标志位驱动行为链MODE、KPAUSE、MSTATE、MIDTRM 各自管什么2.1 行为链一个仿真步里模型会被调用好几次资料里有一句话是整个自定义模型的地基PSS/E 主体框架中的动态仿真计算是以一个一个的行为串联起来的而这些行为又和内置的标志量相联系。这句话拆开看有两层意思。第一层仿真是分行为推进的——初始化是一个行为网络方程求解是一个行为积分推进是一个行为遇到不收敛回退又是一个行为。第二层每个行为在进入模型子程序之前框架会先把一组标志量写成特定的值再由连接子程序TBLCNC、CONEC、CONET 这些根据标志值把控制权交给模型子程序里对应的那一小段语句。所以你的模型子程序不是一个仿真步调用一次而是一个仿真步调用多次每次带着不同的标志组合。这一段被调用时可能是在算初值下一段可能是在往网络方程里注入电流再下一段可能是在做积分回退。理解不到这一层最典型的后果就是把状态量的赋值写在不该写的位置然后波形上出现莫名其妙的跳变查一整天。资料里给的例子很具体STRT 行为在进行初始化时与 MODE1 内联于是连接子程序调用模型子程序里对应 MODE1 的初始化语句。这个链条值得反复读——行为、标志、连接子程序、模型分支四者一一对应缺一环就断。2.2 MODE第一层分派也是漏写代价最大的分支MODE 是这四个标志里最该先吃透的。它承担的是第一层分派职责框架当前处于哪个行为阶段MODE 就取对应的值你的子程序据此决定走哪条分支。资料里明确了 MODE1 对应初始化STRT 行为这条最常用也最不能省。新手最容易犯的错是只写正常仿真那段逻辑初始化分支随便糊两句甚至直接不写。编译不会报错DYRE 也不会报错因为框架只检查模型注册和数组长度它不检查你有没有给状态量赋初值。等到仿真第一步状态量里躺着的是 COMMON 区上一次运行残留的值或者未初始化内存波形直接从第一个点开始偏你还以为是参数设错了。我一般会要求凡是NS不为 0 的模型MODE1 分支里必须逐个 STATE 显式赋值哪怕初值就是 0.0 也要写出来。这不是形式主义是让这个状态量的稳态初值是多少这件事在代码里留下可读的证据。后面调参调到怀疑人生的时候这行赋值就是后悔药。需要提醒的是MODE 各取值与行为的具体对应关系不同版本的资料表述偶有差异务必拿你本地那份字段对照表核对一遍别照抄别人的分支编号。2.3 KPAUSE 与 MIDTRM仿真节拍上的两个开关KPAUSE 管的是暂停。仿真被要求暂停时框架会带着这个标志进来此时你的模型通常只需要做输出整理、把当前量刷到该刷的地方而不应该推进任何状态量。这条规则写起来简单违反的后果却很隐蔽状态量在暂停点被推进了半步恢复仿真后整个轨迹就偏了而且偏得很小小到你怀疑是积分步长的问题。MIDTRM 管的是中间时刻。它标记当前是不是积分过程中的半步或中间评估点。很多数值积分方法会在一个整步内做多次函数评估中间评估点算出来的导数只用于拼最终结果不应该写回状态量本身。如果把中间步和整步的处理写成同一套语句等于让状态量在一个整步里被推进了多次表现出来就是阻尼异常、振荡衰减得比理论值快或者反过来发散。我的习惯是把这两个标志放在同一个层级判断先判断是不是暂停再判断是不是中间步最后才走正常推进。顺序反了会让暂停逻辑被中间步逻辑吃掉这种 bug 在波形上几乎看不出来只能靠日志。2.4 MSTATE状态量当前处于哪种处理阶段MSTATE 关联的是状态量本身的处理阶段。它和 MODE 不是一回事MODE 描述框架行为MSTATE 描述状态量在该行为下应该被怎么对待——是允许写入、是只读、还是需要恢复到上一个被接受的值。这一条在带限幅、带抗积分饱和或者带内部逻辑切换的模型里特别关键。比如励磁模型里的电压调节器输出撞到限幅之后积分环节必须停止累积否则退出限幅时会出现明显的滞后。这个停止累积的判断如果只靠自己的变量去记回退的时候就对不上跟着 MSTATE 走框架回退时状态量能一起回退逻辑才是自洽的。具体到取值含义还是那句话以你手上资料的对照表为准。但判断原则可以统一——凡是会改变 STATE 的语句都必须处在 MSTATE 允许写入的阶段其余阶段一律只读。2.5 把四个标志写成一个骨架下面这段是骨架级的写法不是为了直接跑而是为了把四个标志的层级关系固定下来。include 的文件名按你本地模板替换不同版本头文件名不完全一致别照抄。SUBROUTINE MYGOV (MC, ISLOT) C C MC : PSS/E 内部机组码框架传入全程只读 C ISLOT : 本模型在内部数组位置表中的槽位只读 C C 注意下面两个 include 名按本地模板替换 INCLUDE COMON4.INS INCLUDE CONND.INS C INTEGER MC, ISLOT INTEGER JCON, JSTA, JVAR, JICN C C ---- 先把四个区首地址取出来后面全靠它们 ---- JCON STRTIN(1, ISLOT) ! 第一个 CON 的偏移 JSTA STRTIN(2, ISLOT) ! 第一个 STATE 的偏移 JVAR STRTIN(3, ISLOT) ! 第一个 VAR 的偏移 JICN STRTIN(4, ISLOT) ! 第一个 ICON 的偏移 C C ---- 第一层框架行为分派 ---- IF (MODE .EQ. 1) THEN C 初始化行为只读参数、算初值、写 STATE STATE(JSTA) 0.0 STATE(JSTA1) CON(JCON) C ELSE IF (KPAUSE .EQ. 1) THEN C 暂停行为不推进状态量只整理输出 VAR(JVAR) STATE(JSTA) C ELSE C 正常仿真行为再分整步与中间步 IF (MIDTRM .EQ. 0) THEN C 整步允许按 MSTATE 的语义写入 STATE IF (MSTATE .EQ. 0) THEN STATE(JSTA) STATE(JSTA) VAR(JVAR) END IF ELSE C 中间步只算导数、只写 VAR绝不碰 STATE VAR(JVAR) CON(JCON) * STATE(JSTA) END IF END IF C RETURN END逻辑说明这段代码的重点不是算法是层级。MODE 在最外层做行为分派KPAUSE 次之MIDTRM 和 MSTATE 在最内层管状态量能不能写。这个顺序不是随便排的——暂停行为优先级必须高于积分推进中间步优先级必须高于整步写入否则状态量会在不该动的时候被动。参数说明STRTIN是整型二维数组第一维固定 1 到 4分别对应 CON、STATE、VAR、ICON 四个区的首偏移第二维是ISLOT。当某个区的个数为 0 时对应那一行返回 0所以代码里必须先确认个数再取地址别上来就用STRTIN(3, ISLOT)去索引 VAR 区。MC和ISLOT都是框架传进来的实参在子程序里改它们没有任何意义只会让后续索引错乱。3. 从 MC/ISLOT 到四个数组子程序骨架、STRTIN 指针与调用顺序3.1 签名是硬约束不是风格问题与机组相关的自定义模型子程序形式必须是SUBROUTINE modelname (MC, ISLOT)。这里没有任何商量余地。MC是该模型相连机组的 PSS/E 内部机组码ISLOT是该模型调用的内部数组位置表指针。两个参数都是整型都由框架填入。为什么必须是这个签名因为调用方是框架内部的连接子程序它只会按这个约定压栈。你写成SUBROUTINE MYGOV(MX, ISLOTA)编译也能过链接也能过但运行时取到的值是什么就没保证了。这类问题最恶心的地方在于它常常不崩只是数值不对你会先怀疑参数、再怀疑积分方法最后才怀疑签名。血泪经验新建模型时先把签名和 include 抄对再动别的。还有一点容易忽略这两个参数在子程序内部只能读不能写。ISLOT一旦被改后面所有STRTIN(:, ISLOT)的取值全部偏移而 Fortran 的数组越界默认不检查你会得到一段看起来能跑的垃圾数据。3.2 STRTIN 四行指针就是四个区的门牌号资料里写得很清楚在 DYRE 活动结束时与机组相关的每一个模型都被分配了数组位置表入口STRTIN 行指向个数为 0 时STRTIN(1, ISLOT)本模型第一个 CON 的指针返回 0STRTIN(2, ISLOT)本模型第一个 STATE 的指针返回 0STRTIN(3, ISLOT)本模型第一个 VAR 的指针返回 0STRTIN(4, ISLOT)本模型第一个 ICON 的指针返回 0这张表值得贴在显示器边上。它解释了为什么你的子程序里看不到任何全局数组下标的硬编码——所有寻址都是首偏移 本模型内部序号。第一个 CON 就是CON(STRTIN(1, ISLOT))第二个是CON(STRTIN(1, ISLOT)1)以此类推。这种设计带来的直接好处是模型可以复用同一个模型挂到不同机组上ISLOT不同首偏移不同代码一行不用改。坏处是它对个数极其敏感——你在 USRMDL 记录里把NS写成 3 而实际用了 4 个状态量第 4 个就写到隔壁模型的 STATE 区去了而且不会有任何提示。所以我在 MODE1 分支里通常会加一段自检打印把四个首偏移和四个个数一起打出来跟记录逐位对一遍。这段打印在正式版里可以关掉但调试期一定留着。3.3 TBLCNC 对用户不可见别自作聪明手写调用资料里有一句很容易被跳过的话与机组相关的用户自定义模型均由 PSS/E 自己通过子程序 TBLCNC 自动调用而不是通过 CONEC 调用由于 TBLCNC 对用户不可见用户不用手工在连接子程序中写入调用语句或指定相应地址。这句话解决了一个新手常有的困惑我看别人写 CONEC为什么我的模型不用写答案是机组相关模型和网络相关模型走的是两条不同的挂接路径。机组相关的走 TBLCNC地址由框架自动分配你只要把子程序按规则写好、编译通过、在动态数据文件里正确注册剩下的事框架全包。反过来说如果你手动去 CONEC 里加了一句CALL MYGOV(...)编译能过运行可能也不报错但模型会被调用两次——一次框架调的一次你调的。表现出来就是状态量推进速度翻倍波形周期只有理论值的一半。这个坑我在别人的代码里见过注释还写着加上这句才能被调用属于典型的以讹传讹。3.4 调用顺序是固定的写错不会报错只会偏资料给出的模型库调用顺序是发电机模型 → 电流补偿器模型 → 励磁稳定器模型 → 励磁模型 → 汽轮机/调速器模型 → 混合模型。这个顺序不是随意排的它反映的是同一时刻各模型之间的数据依赖关系。发电机模型先根据网络解算结果确定机端电压和电流电流补偿器在此基础上修正注入稳定器根据转速或功率偏差算出附加信号励磁模型拿稳定器的输出和机端电压一起算励磁电压调速器独立算机械功率最后混合模型汇总。顺序模型类别主要输入顺序写错的典型现象1发电机模型网络解出的机端电压、电流机端量落后一个步长2电流补偿器发电机注入电流补偿后电流不收敛3励磁稳定器转速偏差、功率偏差附加信号相位滞后4励磁模型稳定器输出、机端电压励磁电压阶跃跳变5汽轮机/调速器转速偏差机械功率与电磁功率不同步6混合模型上述各模型输出汇总量取到上一步的旧值实际操作中顺序本身是框架定的用户改不了但模型之间的数据怎么传是用户能改的。如果你把励磁需要的稳定器输出存在一个自己定义的模块变量里而不是走 VAR 区那么当框架因为回退重算而重复调用某一段时这个变量的值就可能来自上一次未完成的计算。正确做法是凡是跨模型传递的量一律走 VAR 区让框架统一管理生命周期。3.5 一个能编译通过的最小骨架真正上手时建议先写一个什么都不算的空壳只有签名、include、四个指针、MODE1 里把 STATE 全置零其余分支留空。先让它跑通加载和一次仿真再往里填算法。这比一上来写三百行然后花两天定位要快得多。SUBROUTINE MYSTAB (MC, ISLOT) C ---- 最小骨架只做注册与初值不做任何计算 ---- INCLUDE COMON4.INS INCLUDE CONND.INS INTEGER MC, ISLOT INTEGER JSTA, K C IF (MODE .EQ. 1) THEN JSTA STRTIN(2, ISLOT) C 逐个状态量置零个数与记录里的 NS 严格一致 DO 100 K 0, 1 STATE(JSTAK) 0.0 100 CONTINUE END IF C RETURN END逻辑说明MODE1 之外什么都不做是为了先验证框架确实会调用我这一件事。如果这个空壳跑完仿真不崩、波形是平的说明签名、include、注册名、个数全部对上了后面再往里加逻辑出问题就只可能是算法本身。参数说明DO 100 K 0, 1里的上界 1对应的是记录里NS 2。这个数字改的时候必须和 USRMDL 记录同步改两处不一致就是 3.2 节说的越界。用DO循环而不是逐行赋值是为了让个数这个概念在代码里只出现一次减少漏改的机会。4. USRMDL 记录六位整数怎么数、data list 怎么排4.1 一条记录拆成七个字段资料给出的格式是BUSID USRMDL IM modelname IC IT NI NC NS NV data list逐段拆开看。BUSID是机组所在母线号USRMDL是记录关键字告诉解析器这条不是普通模型IM是同一母线上多台机组时的机组序号单机可以填 0 或 1具体按资料的约定modelname是模型名必须和在框架里注册的名字一致拼错一个字母就是模型未找到。后面六位整数是资源清单也是最容易数错的地方。其中NC、NS、NV、NI四位与 STRTIN 的四行一一对应NC是 CON 个数NS是 STATE 个数NV是 VAR 个数NI是 ICON 个数。剩下IC和IT两位属于整型参数区的计数与标志按资料的字段表填它们不影响四个数据区的内存布局填错的典型表现是加载期报错而不是运行期越界。所以核对顺序上先保NC/NS/NV/NI四位再看IC/IT。有一个容易混的点必须强调IC不是 ICON 个数ICON 个数是NI。这两位的字母顺序看着像实际含义完全不同。把IC当成 ICON 个数填进去加载能过但模型里读ICON时会拿到别的模型的整数常量。4.2 六位整数怎么跟数组声明对账对账方法很土但有效把子程序里实际用到的最大下标写出来然后加一就是个数。比如代码里出现了CON(JCON)到CON(JCON2)那NC就是 3出现了STATE(JSTA)到STATE(JSTA3)NS就是 4。我一般会在纸上或者注释里列一张表左边写记录字段右边写代码里的最大下标逐行划勾。听起来笨但比在仿真里靠波形反推快十倍。C ---- 资源清单自检调试期保留正式版可注释---- C NC 3 - CON(JCON) ... CON(JCON2) C NS 4 - STATE(JSTA) ... STATE(JSTA3) C NV 2 - VAR(JVAR) ... VAR(JVAR1) C NI 5 - ICON(JICN) ... ICON(JICN4)这张注释表放在子程序开头谁接手都能一眼核对。我见过最省事的做法是把这张表和 USRMDL 记录放在同一个文本文件里改一个改两个物理上杜绝不同步。4.3 data list 的排列顺序由模型内的读取顺序决定记录最后那段data list是模型的参数数据。它没有名字只有位置所以顺序必须和模型里读取的顺序完全一致。第一个参数读进哪个变量第二个读进哪个变量全靠你自己约定。建议在子程序里按读取顺序写一段连续赋值中间不要插别的逻辑更不要跳着读。参数一多比如十几个增益和时间常数跳着读的代码三个月后自己都看不懂。常见做法是先按物理意义分块励磁增益一块、时间常数一块、限幅一块块内按记录顺序块与块之间用注释隔开。还有一点参数读进来之后最好在 MODE1 里做一次范围检查。时间常数读到 0 或者负数仿真会直接爆掉而报错信息通常只说数值错误不会告诉你是哪个参数。加几行IF判断把非法值打印出来能省掉大量排查时间。4.4 用一段脚本先把记录数一遍动态数据文件是文本改起来容易错起来也容易。我一般会先用脚本把每条 USRMDL 记录的字段数、六位整数、数据段长度统计出来跟模型清单对一遍再交给 DYRE。import re # 读取动态数据文件逐行找 USRMDL 记录 with open(dyn_data.dyr, r, encodingutf-8, errorsignore) as f: lines f.readlines() pat re.compile(rUSRMDL, re.IGNORECASE) for i, line in enumerate(lines, 1): if not pat.search(line): continue # 按逗号或空白切分兼容两种写法 parts [p for p in re.split(r[,\s], line.strip()) if p] # 定位 modelname 之后的六个整数 idx next((k for k, p in enumerate(parts) if p.upper().strip() USRMDL), None) if idx is None or len(parts) idx 9: print(f第 {i} 行: 字段数不足, 实际 {len(parts)}) continue model parts[idx 2].strip() nums parts[idx 3: idx 9] try: ic, it, ni, nc, ns, nv (int(x) for x in nums) except ValueError: print(f第 {i} 行: 六位整数里有非数字 {nums}) continue data parts[idx 9:] print(f第 {i} 行 {model}: IC{ic} IT{it} NI{ni} fNC{nc} NS{ns} NV{nv} 数据项{len(data)})逻辑说明先按USRMDL定位关键字再从关键字后面第三段开始取六个整数剩下的全部算数据段。切分用逗号或连续空白两种方式兼容是因为不同来源的文件写法不统一有人全用逗号有人全用空格。参数说明idx 2是因为关键字后面紧跟IM和modelname两段idx 3 : idx 9正好六位idx 9之后是数据段。这几个偏移量如果和你的文件写法不一致改这三个数字就行逻辑不用动。跑出来的数据项个数要和你模型里实际读取的参数个数对上对不上就是少写或多写了。4.5 改参数时最容易出的错位调参阶段改记录是家常便饭但每次改动都可能引入错位。最常见的场景是给模型加了一个限幅NV从 2 改成 3记录改了代码里VAR(JVAR2)也用上了唯独忘了在 MODE1 里给这个新变量赋初值。仿真跑起来这个变量是垃圾值限幅一直在起作用波形上表现为输出被削平你以为是限幅值设错了改了半天下限上限都没用。另一种错位是删参数。删掉记录里一个数据项代码里对应的读取语句没删后面所有参数整体前移一位增益读成了时间常数时间常数读成了限幅。这种错误不会报错只会让波形变得很像但不对是最难查的一类。习惯上我改记录和改代码必须在同一次编辑里完成改完立刻跑一遍 4.4 的脚本再跑一遍 MODE1 的自检打印。两个都过了才进仿真。5. 避坑与排查五次翻车实录5.1 先分清是哪一层出的问题自定义模型出问题先定位在哪一层比直接读代码有效得多。三层加载层DYRE 读记录、注册模型、编译链接层子程序签名、include、外部符号、仿真运行层数值发散、波形不对。加载层的问题通常会明确报错说哪个字段非法、哪个模型找不到编译层的问题链接器会告诉你缺哪个符号只有仿真层的问题最沉默需要靠自检打印和波形反推。我一般的顺序是先看 DYRE 输出有没有警告警告也要看不只是错误再确认编译产物里确实包含新子程序最后才进仿真。这三步任何一步跳过后面都会加倍还回来。5.2 坑一MODE 分支漏写初始化静默通过现象DYRE 跑完无报错仿真第一个点开始波形就偏或者干脆发散把积分步长缩小一半波形形状不变只是更密。原因MODE1 分支里没有给全部状态量赋初值。框架不检查这件事它只保证在 STRT 行为时带着 MODE1 调用你一次你写不写、写几个它不管。没写的状态量保持 COMMON 区里的原值可能是上一次仿真的残留也可能是内存里的随机数。解决把所有 STATE 的初始化写成一段连续赋值个数与记录里的NS严格一致在 MODE1 结尾加一段打印把每个状态量的初值打出来和理论稳态值对一遍。这一步花五分钟能省掉后面几小时的怀疑。5.3 坑二NS 和 NV 数错STRTIN 指针整体偏移现象仿真不崩但波形有周期性抖动或者某个内部量纲明显不对比如本该在 0 到 1 之间的量跑到了几十。原因USRMDL 记录里NS写少了实际用了 4 个状态量却只声明 3 个。第 4 个状态量的写入落到了 VAR 区的第一个位置而 VAR 区的首偏移又是按记录里的NV算的两边一起错等于把状态量写进了变量表变量表又被模型当成状态量读。解决在 MODE1 里打印STRTIN(1..4, ISLOT)四个首偏移和四个个数和记录逐位核对。更彻底的办法是把四个个数定义成参数PARAMETER代码里所有循环上界都引用这个参数记录里的数字直接从参数取值填物理上消除不一致。5.4 坑三ICON 和实数量混存现象整数常量读出来是 0或者某个本该是整数的标志位变成了很奇怪的小数。原因Fortran 默认的隐式类型规则加上 COMMON 块声明顺序不一致。ICON 区是整型数组CON 区是实型数组如果 include 文件里的 COMMON 声明顺序和你子程序里的隐式假定不匹配同一个地址会被按两种类型解释读出来就是垃圾。解决所有变量显式声明INTEGER和REAL一个都别省如果编译器支持打开IMPLICIT NONE把所有隐式类型断掉COMMON 块的声明顺序以 include 文件为准不要自己在子程序里另写一份。这条在换编译器或者换版本时尤其容易复发。5.5 坑四输出量在错误的调用顺序里刷新现象励磁电压的响应比机端电压慢一个步长小扰动下相位明显滞后或者把步长减半后滞后依然存在只是绝对值变小。原因模型之间的数据传递没有走 VAR 区而是用了自定义的模块变量或者 SAVE 变量。框架在一个仿真步里会多次调用模型回退重算时也会重复调用这些变量的值可能在半途被覆盖等到下游模型读的时候拿到的是上一次未完成计算的结果。解决所有跨模型传递的量一律走 VAR 区由框架统一管理生命周期模型内部的临时量如果必须保留明确用 SAVE 并在每次进入时判断是否需要重置。调用顺序本身发电机 → 补偿器 → 稳定器 → 励磁 → 调速器 → 混合模型改不了能改的是你怎么在这个顺序里传数据。5.6 坑五数据段字段错位和空格陷阱现象参数读进去数值差十倍、符号反了或者某个时间常数读成了 0。原因动态数据文件是固定格式和自由格式混用的多一个空格、少一个逗号、行尾多了个制表符都可能让解析器把两个字段粘成一个或者把一个字段切成两个。尤其是从表格里复制粘贴过来的数据肉眼完全看不出问题。解决先用 4.4 的脚本统计每条记录的数据项个数和模型读取的参数个数对齐再用 MODE1 打印把每个参数读进来的值打出来和原始数据逐个比对。这两个动作做完还有问题才去怀疑模型逻辑。粘贴数据时统一用逗号分隔杜绝空格混排能挡掉一大半这类问题。6. 验证与进阶三层验证把模型钉死模型写完不算完能不能信是另一回事。我的做法是三层验证从便宜到贵依次做。第一层是初始化自检成本最低。在 MODE1 分支末尾加一段打印把四个STRTIN首偏移、四个个数、每个状态量初值、每个参数读入值全部打出来。这一层能挡掉绝大多数记录写错、个数不匹配、参数错位的问题而且不需要跑完整仿真。我一般会把这段打印用注释开关控制调试期打开定型后关掉但代码保留。C ---- 初始化自检调试期打开定型后注释 ---- WRITE (6, 900) ISLOT, STRTIN(1,ISLOT), STRTIN(2,ISLOT), STRTIN(3,ISLOT), STRTIN(4,ISLOT) 900 FORMAT ( MYGOV ISLOT, I4, CON, I5, STA, I5, VAR, I5, ICN, I5)逻辑说明只打印偏移不打印个数是不够的偏移对但个数错一样会越界所以实际使用时这两组数要一起打。第二层是小扰动验证在单机无穷大系统上给一个小的电压阶跃或者机械功率阶跃看响应形状是否符合物理直觉阻尼是否为正、稳态是否回到初值、限幅是否在预期位置起作用。这一层不看绝对数值看趋势。趋势不对说明模型结构或者符号有问题不用往下走。第三层是对比验证把同一套参数分别灌进自定义模型和一个已知可靠的参考模型看小扰动响应曲线是否重合。这一层最费时间但也是唯一能证明你的模型和理论一致的手段。对比时建议只看前两到三个摆动的峰谷值和时间不看全过程——积分方法差异会让长期轨迹慢慢分开那不是模型错。三层做完模型才算能用。说实话前两层大多数人都做第三层跳过的人不少然后模型带着一个符号错误上线几年后才发现。从那以后我每次新建自定义模型都强制先把空壳跑通、把自检打印打开、把三层验证走一遍一个都不跳。图省事跳过的每一步后面都会以两倍的时间还回来。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站