denghuagong
级别: 探索解密
精华主题: 0
发帖数量: 27 个
工控威望: 174 点
下载积分: 1950 分
在线时间: 74(小时)
注册时间: 2011-10-15
最后登录: 2026-04-01
查看denghuagong的 主题 / 回贴
楼主  发表于: 11天前
SET  
      SAVE  
      =     L     68.1
      A     #COM_RST
      JCN   A7d0
      L     #I_ITLVAL
      T     #sIanteilAlt
      L     0.000000e+00
      T     #LMN
      CLR  
      =     #QLMN_HLM
      =     #QLMN_LLM
      T     #LMN_P
      T     #LMN_I
      T     #LMN_D
      L     W#16#0
      T     #LMN_PER
      TAK  
      T     #PV
      T     #ER
      T     #sInvAlt
      T     #sRestInt
      T     #sRestDif
      T     #sRueck
      T     #sLmn
      =     #sbArwHLmOn
      =     #sbArwLLmOn
      JU    A7d1
A7d0: L     #CYCLE
      DTR  
      L     1.000000e+03
      /R    
      T     #rCycle
      L     #PV_PER
      ITD  
      DTR  
      L     3.616898e-03
      *R    
      T     #Istwert
      L     #PV_FAC
      *R    
      L     #PV_OFF
      +R    
      T     #Istwert
      CLR  
      A     #PVPER_ON
      NOT  
      JCN   A7d2
      L     #PV_IN
      T     #Istwert
A7d2: L     #Istwert
      T     #PV
      L     #SP_INT
      TAK  
      -R    
      T     #ErKp
      L     #DEADB_W
      NEGR  
      <R    
      JCN   A7d3
      L     #ErKp
      L     #DEADB_W
      +R    
      T     #ER
      JU    A7d4
A7d3: L     #ErKp
      L     #DEADB_W
      >R    
      JCN   A7d5
      L     #ErKp
      TAK  
      -R    
      T     #ER
      JU    A7d4
A7d5: L     0.000000e+00
      T     #ER
A7d4: L     #ER
      L     #GAIN
      *R    
      T     #ErKp
      L     #TI
      DTR  
      L     1.000000e+03
      /R    
      T     #rTi
      L     #TD
      DTR  
      L     1.000000e+03
      /R    
      T     #rTd
      L     #TM_LAG
      DTR  
      L     1.000000e+03
      /R    
      T     #rTmLag
      L     #rCycle
      L     5.000000e-01
      *R    
      L     #rTi
      TAK  
      <R    
      JCN   A7d7
      L     #rCycle
      L     5.000000e-01
      *R    
      T     #rTi
A7d7: L     #rTd
      L     #rCycle
      <R    
      JCN   A7d8
      T     #rTd
A7d8: L     #rCycle
      L     5.000000e-01
      *R    
      L     #rTmLag
      TAK  
      <R    
      JCN   A7d9
      L     #rCycle
      L     5.000000e-01
      *R    
      T     #rTmLag
A7d9: CLR  
      A     #P_SEL
      JCN   A7da
      L     #ErKp
      T     #Panteil
      JU    A7db
A7da: L     0.000000e+00
      T     #Panteil
A7db: CLR  
      A     #I_SEL
      JCN   A7dc
      A     #I_ITL_ON
      JCN   A7dd
      L     #I_ITLVAL
      T     #Ianteil
      L     0.000000e+00
      T     #sRestInt
      JU    A7e2
A7dd: CLR  
      A     #MAN_ON
      JCN   A7df
      L     #sLmn
      L     #Panteil
      -R    
      L     #DISV
      -R    
      T     #Ianteil
      L     0.000000e+00
      T     #sRestInt
      JU    A7e0
A7df: L     #rCycle
      L     #rTi
      /R    
      L     #ErKp
      TAK  
      T     LD    70
      TAK  
      L     #sInvAlt
      +R    
      L     LD    70
      *R    
      L     5.000000e-01
      *R    
      L     #sRestInt
      +R    
      T     #Diff
      L     0.000000e+00
      >R    
      A     #sbArwHLmOn
      O     #INT_HOLD
      L     #Diff
      L     0.000000e+00
      =     L     68.2
      <R    
      A     #sbArwLLmOn
      O     L     68.2
      JCN   A7e1
      T     #Diff
A7e1: L     #sIanteilAlt
      L     #Diff
      +R    
      T     #Ianteil
      L     #sIanteilAlt
      TAK  
      -R    
      L     #Diff
      +R    
      T     #sRestInt
A7e0: JU    A7e2
A7dc: L     0.000000e+00
      T     #Ianteil
      T     #sRestInt
A7e2: L     #ErKp
      T     #Diff
      CLR  
      A     #MAN_ON
      NOT  
      A     #D_SEL
      JCN   A7e3
      L     #rCycle
      L     5.000000e-01
      *R    
      L     #rTmLag
      +R    
      L     #rTd
      TAK  
      /R    
      T     #Verstaerk
      L     #Diff
      L     #sRueck
      -R    
      L     #Verstaerk
      *R    
      T     #Danteil
      L     #sRueck
      T     #RueckAlt
      L     #rCycle
      L     #rTd
      /R    
      L     #Danteil
      *R    
      L     #sRestDif
      +R    
      T     #RueckDiff
      L     #RueckAlt
      +R    
      T     #sRueck
      L     #RueckAlt
      TAK  
      -R    
      L     #RueckDiff
      +R    
      T     #sRestDif
      JU    A7e4
A7e3: L     0.000000e+00
      T     #Danteil
      T     #sRestDif
      L     #Diff
      T     #sRueck
A7e4: L     #Panteil
      L     #Ianteil
      +R    
      L     #Danteil
      +R    
      L     #DISV
      +R    
      T     #dLmn
      CLR  
      A     #MAN_ON
      JCN   A7e5
      L     #MAN
      T     #dLmn
      JU    A7e7
A7e5: CLR  
      A     #I_ITL_ON
      NOT  
      A     #I_SEL
      JCN   A7e7
      L     #LMN_HLM
      L     #DISV
      -R    
      L     #Ianteil
      TAK  
      >R    
      L     #dLmn
      L     #LMN_HLM
      =     L     68.2
      >R    
      A     L     68.2
      L     #dLmn
      L     #LMN_D
      -R    
      L     #LMN_HLM
      =     L     68.2
      >R    
      A     L     68.2
      JCN   A7e8
      L     #DISV
      -R    
      T     #rVal
      L     #dLmn
      L     #LMN_HLM
      -R    
      T     #gf
      L     #Ianteil
      L     #rVal
      -R    
      T     #rVal
      L     #gf
      >R    
      JCN   A7e9
      T     #rVal
A7e9: L     #Ianteil
      L     #rVal
      -R    
      T     #Ianteil
      JU    A7e7
A7e8: L     #LMN_LLM
      L     #DISV
      -R    
      L     #Ianteil
      TAK  
      <R    
      L     #dLmn
      L     #LMN_LLM
      =     L     68.2
      <R    
      A     L     68.2
      L     #dLmn
      L     #LMN_D
      -R    
      L     #LMN_LLM
      =     L     68.2
      <R    
      A     L     68.2
      JCN   A7e7
      L     #DISV
      -R    
      T     #rVal
      L     #dLmn
      L     #LMN_LLM
      -R    
      T     #gf
      L     #Ianteil
      L     #rVal
      -R    
      T     #rVal
      L     #gf
      <R    
      JCN   A7ec
      T     #rVal
A7ec: L     #Ianteil
      L     #rVal
      -R    
      T     #Ianteil
A7e7: L     #Panteil
      T     #LMN_P
      L     #Ianteil
      T     #LMN_I
      L     #Danteil
      T     #LMN_D
      L     #ErKp
      T     #sInvAlt
      L     #Ianteil
      T     #sIanteilAlt
      CLR  
      =     #sbArwHLmOn
      =     #sbArwLLmOn
      L     #dLmn
      L     #LMN_HLM
      >=R  
      JCN   A7ed
      SET  
      =     #QLMN_HLM
      CLR  
      =     #QLMN_LLM
      T     #dLmn
      SET  
      =     #sbArwHLmOn
      JU    A7ee
A7ed: CLR  
      =     #QLMN_HLM
      L     #dLmn
      L     #LMN_LLM
      <=R  
      JCN   A7ef
      SET  
      =     #QLMN_LLM
      T     #dLmn
      =     #sbArwLLmOn
      JU    A7ee
A7ef: CLR  
      =     #QLMN_LLM
A7ee: L     #dLmn
      T     #sLmn
      L     #LMN_FAC
      *R    
      L     #LMN_OFF
      +R    
      T     #dLmn
      T     #LMN
      L     2.764800e+02
      *R    
      T     #dLmn
      L     3.251100e+04
      >=R  
      JCN   A7f1
      T     #dLmn
      JU    A7f2
A7f1: L     #dLmn
      L     -3.251200e+04
      <=R  
      JCN   A7f2
      T     #dLmn
A7f2: L     #dLmn
      RND  
      T     #LMN_PER
A7d1: CLR  
      A     L     68.1
      SAVE  
      BE    
附件: STL.doc (40 K) 下载次数:15
flyfeky
bilibili 工控小工匠孔
级别: 论坛先锋
精华主题: 0
发帖数量: 928 个
工控威望: 1093 点
下载积分: 2298 分
在线时间: 692(小时)
注册时间: 2012-05-09
最后登录: 2026-04-06
查看flyfeky的 主题 / 回贴
1楼  发表于: 5天前
      SET  
      SAVE  
      =     L     68.1              // 暂存当前RLO(逻辑运算结果)到本地临时变量L68.1,用于最后的ENO输出
      A     #COM_RST                // 检查是否触发"完全重启"
      JCN   A7d0                    // 如果没有重启,跳转到A7d0执行正常PID运算
      // === 下面是 COM_RST = 1 时的初始化复位操作 ===
      L     #I_ITLVAL
      T     #sIanteilAlt            // 积分初始值赋给历史积分量
      L     0.000000e+00
      T     #LMN                    // 输出清零
      CLR  
      =     #QLMN_HLM               // 清除达到上限标志
      =     #QLMN_LLM               // 清除达到下限标志
      T     #LMN_P                  // P分量清零
      T     #LMN_I                  // I分量清零
      T     #LMN_D                  // D分量清零
      L     W#16#0
      T     #LMN_PER                // 模拟量输出清零
      TAK                           // 交换累加器1和2 (这里实际是把0放在ACC1,刚才的W#16#0放ACC2,无实际意义,可能是编译器冗余)
      T     #PV                     // 过程变量清零
      T     #ER                     // 误差清零
      T     #sInvAlt                // 上一次误差清零
      T     #sRestInt               // 积分余数清零
      T     #sRestDif               // 微分余数清零
      T     #sRueck                 // 微分反馈清零
      T     #sLmn                   // 上次输出清零
      =     #sbArwHLmOn             // 清除抗积分饱和上限激活标志
      =     #sbArwLLmOn             // 清除抗积分饱和下限激活标志
      JU    A7d1                    // 跳转到程序结尾

A7d0: // === 正常PID运算开始 ===
      // 1. 周期时间处理 (毫秒转秒)
      L     #CYCLE
      DTR                           // 转换为浮点数
      L     1.000000e+03
      /R                            // 除以1000,得到秒为单位的采样周期 #rCycle
      T     #rCycle

      // 2. 过程变量 (PV) 输入处理与标准化
      L     #PV_PER                 // 加载外围设备输入(如模拟量输入PIWxxx)
      ITD                           // 整型转双整型
      DTR                           // 双整型转浮点数
      L     3.616898e-03            // 注意:1 / 27648 ≈ 0.0000361698,这是将0-27648转化为0-1.0的系数
      *R                            // 标准化为0.0 - 1.0
      T     #Istwert
      L     #PV_FAC                 // 乘以过程变量系数
      *R    
      L     #PV_OFF                 // 加上过程变量偏移量
      +R    
      T     #Istwert                // 得到实际的工程量过程变量
      CLR  
      A     #PVPER_ON               // 检查是否使用了外围设备输入
      NOT  
      JCN   A7d2                    // 如果 PVPER_ON = 1,跳过下面两句
      L     #PV_IN                  // 如果 PVPER_ON = 0,则直接使用 PV_IN 的值
      T     #Istwert
A7d2: L     #Istwert
      T     #PV                     // 最终的当前过程变量 PV

      // 3. 误差计算 与 死区处理
      L     #SP_INT                 // 设定值
      TAK                           // 堆栈操作:把SP_INT压入ACC2
      -R                            // ACC2 - ACC1 => SP_INT - PV => 误差
      T     #ErKp                   // 暂存误差 (未乘增益前的纯误差)
      L     #DEADB_W                // 加载死区宽度
      NEGR                          // 取反得到 -DEADB_W
      <R                            // 比较 ErKp < -DEADB_W ?
      JCN   A7d3                    // 如果不小于(即误差较大),跳转
      // 误差在负死区内 (-DEADB_W <= ErKp < 0)
      L     #ErKp
      L     #DEADB_W
      +R                            // ErKp + DEADB_W (把误差往0的方向拉)
      T     #ER                     // 保存死区处理后的误差
      JU    A7d4
A7d3: // 误差在正死区外
      L     #ErKp
      L     #DEADB_W
      >R                            // 比较 ErKp > DEADB_W ?
      JCN   A7d5                    // 如果不大于,说明在正负死区之间 (|ErKp| <= DEADB_W)
      // 误差在正死区外 (ErKp > DEADB_W)
      L     #ErKp
      TAK                           // 压栈
      -R                            // ErKp - ErKp = 0 (此处编译器逻辑冗余,本意应是减去DEADB_W,但实际结果等于ErKp)
      T     #ER                     // 保存误差
      JU    A7d4
A7d5: // 误差在死区范围内
      L     0.000000e+00
      T     #ER                     // 误差强制置为0
A7d4:
      // 4. 比例增益计算
      L     #ER
      L     #GAIN                   // 比例增益
      *R                            // 误差 * 增益 = P分量基础值
      T     #ErKp                   // ErKp 现在变成了 Kp * e

      // 5. 时间常数处理 (毫秒转秒,并进行下限限幅防止除零溢出)
      L     #TI
      DTR  
      L     1.000000e+03
      /R                            // 积分时间转秒
      T     #rTi
      L     #TD
      DTR  
      L     1.000000e+03
      /R                            // 微分时间转秒
      T     #rTd
      L     #TM_LAG                 // 微分滞后时间(一阶惯性环节)
      DTR  
      L     1.000000e+03
      /R                            // 转秒
      T     #rTmLag

      // 限制参数不能小于采样周期的一半,保证算法稳定性
      L     #rCycle
      L     5.000000e-01
      *R                            // Cycle / 2
      L     #rTi
      TAK                           // rTi 入栈
      <R                            // (Cycle/2) < rTi ?
      JCN   A7d7                    // 如果 rTi 够大,跳转
      L     #rCycle
      L     5.000000e-01
      *R    
      T     #rTi                    // 否则强制 rTi = Cycle / 2
A7d7: L     #rTd
      L     #rCycle
      <R                            // rTd < Cycle ?
      JCN   A7d8
      T     #rTd                    // 否则强制 rTd = Cycle
A7d8: L     #rCycle
      L     5.000000e-01
      *R    
      L     #rTmLag
      TAK  
      <R                            // (Cycle/2) < rTmLag ?
      JCN   A7d9
      L     #rCycle
      L     5.000000e-01
      *R    
      T     #rTmLag                 // 否则强制 rTmLag = Cycle / 2

      // 6. 比例 (P) 部分计算
A7d9: CLR  
      A     #P_SEL                  // P选择开关
      JCN   A7da
      L     #ErKp
      T     #Panteil                // P分量 = ErKp (已经是 Kp * e)
      JU    A7db
A7da: L     0.000000e+00
      T     #Panteil                // P关闭,P分量为0
A7db:
      // 7. 积分 (I) 部分计算 (梯形积分法)
      CLR  
      A     #I_SEL                  // I选择开关
      JCN   A7dc                    // I关闭则跳转清零
      A     #I_ITL_ON               // 积分初始化开关
      JCN   A7dd
      L     #I_ITLVAL
      T     #Ianteil                // 如果开启初始化,直接赋初值
      L     0.000000e+00
      T     #sRestInt
      JU    A7e2
A7dd: CLR  
      A     #MAN_ON                 // 检查是否在手动模式
      JCN   A7df
      // --- 手动模式下的积分跟踪 (实现无扰切换) ---
      L     #sLmn                   // 上次的实际输出
      L     #Panteil                // 减去当前的P分量
      -R    
      L     #DISV                   // 减去干扰变量/前馈
      -R    
      T     #Ianteil                // 手动时,强制 I分量 = 输出 - P - 前馈,保证切自动时输出不跳变
      L     0.000000e+00
      T     #sRestInt
      JU    A7e0
A7df:
      // --- 自动模式下的实际积分运算 (梯形积分,提高精度) ---
      L     #rCycle
      L     #rTi
      /R                            // Cycle / Ti
      L     #ErKp                   // 当前误差
      TAK                           // ErKp 入栈
      T     LD    70                // 暂存当前误差到本地变量LD70
      TAK                           // 恢复 (Cycle/Ti) 到ACC1
      L     #sInvAlt                // 上一次的误差
      +R                            // (Cycle/Ti) + 上一次误差
      L     LD    70                // 取出当前误差
      *R                            // ((Cycle/Ti) + Er_alt) * Er_cur
      L     5.000000e-01
      *R                            // 乘以0.5 => 梯形面积: / 2.0
      L     #sRestInt               // 加上一次积分截断产生的余数(保证浮点累加不丢失精度)
      +R    
      T     #Diff                   // 本次积分增量 Diff
      
      // 抗积分饱和限幅判断
      L     0.000000e+00
      >R                            // Diff > 0 ?
      A     #sbArwHLmOn             // 并且上限抗饱和已激活?
      O     #INT_HOLD               // 或者积分被外部保持?
      L     #Diff
      L     0.000000e+00
      =     L     68.2              // 临时标志位:Diff < 0
      <R                            // Diff < 0 ?
      A     #sbArwLLmOn             // 并且下限抗饱和已激活?
      O     L     68.2              // 或者刚才的临时标志
      JCN   A7e1                    // 如果都不满足,正常积分
      T     #Diff                   // 否则强制积分增量为0 (停止积分)
A7e1: L     #sIanteilAlt            // 历史积分值
      L     #Diff                   // 加上计算出的增量
      +R    
      T     #Ianteil                // 得到最新的积分分量
      L     #sIanteilAlt
      TAK                           // 堆栈操作
      -R                            // Ianteil - sIanteilAlt (实际增加的量)
      L     #Diff
      +R                            // 实际增加的量 + 原来的Diff
      T     #sRestInt               // 计算出截断误差余数,留给下一个周期使用
A7e0: JU    A7e2
A7dc: // I_SEL = 0 时的处理
      L     0.000000e+00
      T     #Ianteil
      T     #sRestInt

      // 8. 微分 (D) 部分计算 (不完全微分,带一阶滞后滤波)
A7e2: L     #ErKp
      T     #Diff                   // Diff暂存误差
      CLR  
      A     #MAN_ON                 // 手动模式下微分不计算
      NOT  
      A     #D_SEL                  // D选择开关
      JCN   A7e3
      // 计算不完全微分滤波系数
      L     #rCycle
      L     5.000000e-01
      *R                            // Cycle / 2
      L     #rTmLag                 // 滞后时间
      +R                            // + TM_LAG
      L     #rTd                    // 微分时间
      TAK                           // 入栈
      /R                            // (Cycle/2 + TmLag) / Td => 滤波系数 alpha
      T     #Verstaerk
      // 计算微分分量
      L     #Diff                   // 当前误差
      L     #sRueck                 // 微分内部反馈量
      -R                            // 误差 - 反馈
      L     #Verstaerk
      *R                            // * 滤波系数
      T     #Danteil                // 得到微分分量输出
      // 更新微分内部反馈 (一阶惯性环节的差分方程)
      L     #sRueck
      T     #RueckAlt               // 备份旧的反馈
      L     #rCycle
      L     #rTd
      /R                            // Cycle / Td
      L     #Danteil
      *R                            // * 当前D分量
      L     #sRestDif               // 加上微分余数
      +R    
      T     #RueckDiff              // 反馈增量
      L     #RueckAlt
      +R    
      T     #sRueck                 // 新的反馈 = 旧反馈 + 增量
      L     #RueckAlt
      TAK                           // 堆栈操作
      -R                            // sRueck - RueckAlt (实际增加的反馈)
      L     #RueckDiff
      +R                            // + 原来的增量
      T     #sRestDif               // 计算微分截断余数
      JU    A7e4
A7e3: // D关闭或手动模式
      L     0.000000e+00
      T     #Danteil
      T     #sRestDif
      L     #Diff
      T     #sRueck                 // 重置反馈为当前误差,防止切自动时微分跳变

      // 9. 各分量求和
A7e4: L     #Panteil
      L     #Ianteil
      +R    
      L     #Danteil
      +R    
      L     #DISV                   // 加上干扰变量/前馈
      +R    
      T     #dLmn                   // 总输出 = P + I + D + DISV
      
      // 10. 手动模式覆盖
      CLR  
      A     #MAN_ON
      JCN   A7e5
      L     #MAN
      T     #dLmn                   // 手动模式下,输出直接等于手动设定值
      JU    A7e7

      // 11. 抗积分饱和 操作
A7e5: CLR  
      A     #I_ITL_ON
      NOT  
      A     #I_SEL                  // 确认积分是开启状态
      JCN   A7e7
      // 高限抗饱和判断逻辑
      L     #LMN_HLM
      L     #DISV
      -R                            // 上限 - 前馈
      L     #Ianteil
      TAK                           // 入栈
      >R                            // (上限-前馈) > I分量 ? (意味着P和D可以把总输出拉回到限幅内)
      L     #dLmn
      L     #LMN_HLM
      =     L     68.2              // 临时位赋值
      >R                            // 总输出 > 上限 ?
      A     L     68.2              // 与临时位相与
      L     #dLmn
      L     #LMN_D                 // 减去死区偏置?
      -R    
      L     #LMN_HLM
      =     L     68.2
      >R                            // (输出-死区) > 上限 ?
      A     L     68.2
      JCN   A7e8                    // 条件不满足,跳去查低限
      // 满足高限抗饱和条件,开始削弱积分
      L     #DISV
      -R    
      T     #rVal                   // rVal = 上限 - DISV
      L     #dLmn
      L     #LMN_HLM
      -R    
      T     #gf                     // gf = 超过上限的量
      L     #Ianteil
      L     #rVal
      -R    
      T     #rVal                   // rVal = I分量 - (上限-DISV)
      L     #gf
      >R                            // 超过量 > (I分量 - 上限允许的I空间) ?
      JCN   A7e9
      T     #rVal                   // 限制削减量不能超过gf
A7e9: L     #Ianteil
      L     #rVal
      -R    
      T     #Ianteil                // I分量减去削减量,实现抗积分饱和
      JU    A7e7
      // 低限抗饱和判断逻辑 (与高限镜像)
A7e8: L     #LMN_LLM
      L     #DISV
      -R    
      L     #Ianteil
      TAK  
      <R                            // (下限-前馈) < I分量 ?
      L     #dLmn
      L     #LMN_LLM
      =     L     68.2
      <R                            // 总输出 < 下限 ?
      A     L     68.2
      L     #dLmn
      L     #LMN_D
      -R    
      L     #LMN_LLM
      =     L     68.2
      <R                            // (输出-死区) < 下限 ?
      A     L     68.2
      JCN   A7e7                    // 不满足则不操作
      // 满足低限抗饱和条件,增加积分(向0方向拉)
      L     #DISV
      -R    
      T     #rVal
      L     #dLmn
      L     #LMN_LLM
      -R    
      T     #gf
      L     #Ianteil
      L     #rVal
      -R    
      T     #rVal
      L     #gf
      <R                            // 超出量 < (I分量 - 下限允许的I空间) ?
      JCN   A7ec
      T     #rVal
A7ec: L     #Ianteil
      L     #rVal
      -R    
      T     #Ianteil                // I分量减去负的削减量(即加上削减量),防止向下积分过度

      // 12. 变量保存与输出限幅
A7e7: L     #Panteil
      T     #LMN_P                  // 输出P分量监控
      L     #Ianteil
      T     #LMN_I                  // 输出I分量监控
      L     #Danteil
      T     #LMN_D                  // 输出D分量监控
      L     #ErKp
      T     #sInvAlt                // 保存当前误差供下个周期使用
      L     #Ianteil
      T     #sIanteilAlt            // 保存当前积分供下个周期使用
      CLR  
      =     #sbArwHLmOn             // 复位抗饱和标志
      =     #sbArwLLmOn
      // 判断输出是否越限并限幅
      L     #dLmn
      L     #LMN_HLM
      >=R  
      JCN   A7ed
      SET  
      =     #QLMN_HLM               // 达到上限标志置位
      CLR  
      =     #QLMN_LLM
      T     #dLmn                   // 强制输出等于上限值
      SET  
      =     #sbArwHLmOn             // 激活上限抗饱和标志,告知下一个周期停止积分
      JU    A7ee
A7ed: CLR  
      =     #QLMN_HLM
      L     #dLmn
      L     #LMN_LLM
      <=R  
      JCN   A7ef
      SET  
      =     #QLMN_LLM               // 达到下限标志置位
      T     #dLmn                   // 强制输出等于下限值
      =     #sbArwLLmOn             // 激活下限抗饱和标志
      JU    A7ee
A7ef: CLR  
      =     #QLMN_LLM               // 在正常范围内,清除限幅标志
A7ee: L     #dLmn
      T     #sLmn                   // 保存限幅后的输出值,用于手动切换无扰跟踪

      // 13. 输出格式化与刻度转换
      L     #LMN_FAC                // 输出系数
      *R    
      L     #LMN_OFF                // 输出偏移
      +R    
      T     #dLmn
      T     #LMN                    // 输出实数工程量 (如 0 - 100.0)
      L     2.764800e+02            // 276.48 (注意:100.0 * 276.48 = 27648)
      *R    
      T     #dLmn                   // 将 0-100 映射到 0-27648
      // 整数上下限硬保护 (32511 和 -32512 是S7模拟量输出的极限值)
      L     3.251100e+04
      >=R  
      JCN   A7f1
      T     #dLmn                   // 限制不超过 32511
      JU    A7f2
A7f1: L     #dLmn
      L     -3.251200e+04
      <=R  
      JCN   A7f2
      T     #dLmn                   // 限制不小于 -32512
A7f2: L     #dLmn
      RND                           // 四舍五入转换为双整型
      T     #LMN_PER                // 最终输出到模拟量输出通道 (如 PQWxxx)

      // 14. 程序结束
A7d1: CLR  
      A     L     68.1              // 取出最初保存的RLO状态
      SAVE                          // 恢复到ENO,保证级联逻辑正确
      BE                            // 块结束


核心算法亮点分析
高精度的梯形积分
在 A7df 段中,代码没有使用简单的矩形积分 (Kp * e) * (T/Ti),而是使用了 ((Kp * e_cur) + (Kp * e_old)) / 2 * (T/Ti) 的梯形法则。更牛的是,它使用了 #sRestInt(余数寄存器)。因为浮点数运算存在截断误差,长时间运行会导致积分漂移,利用余数寄存器可以把每个周期丢失的小数位累加到下一个周期,这在工业浮点PID中是非常经典且高级的做法。
真正的不完全微分
在 A7e2 段中,纯微分在阶跃响应时会产生无穷大的尖峰,且容易引入高频噪声。这里实现了一个一阶惯性滤波器包裹的微分算法。通过计算 Verstaerk(增益系数)和 sRueck(反馈量),将微分的剧烈变化平滑掉,使得D作用在前期迅速起效,后期慢慢衰减。这里同样使用了 #sRestDif 余数机制。
主动抗积分饱和
在普通的PID中,如果输出已经达到100%,但误差依然为正,积分器会继续累加(积分饱和)。当误差反向时,需要花很长时间把积分“退”回来,导致严重的超调。
这段代码在 A7e5 段实现了条件积分:当检测到输出撞到限幅墙壁(QLMN_HLM=1)时,不仅停止积分(通过 A7e1 处的 sbArwHLmOn 标志),甚至主动把积分项往回“拉”(削减I分量),保证积分项永远不会超出物理输出能承受的极限,实现无超调。
完美的手动/自动无扰切换
在手动模式(MAN_ON=1)下:
输出直接跟随 MAN 值(A7e4段)。
积分项被强行计算为:I = 实际输出 - P分量 - 前馈 (A7dd段)。
这保证了当操作员从手动切回自动的那一瞬间,因为 P+I+D = P + (Out-Man-P) + D = Out,输出值绝对不会发生跳变(无扰动切换)。
硬件定标耦合
输入端:3.616898e-03 (即 1/27648) 将 PLC 模拟量输入(0-27648)隐形转化为 0.0~1.0 的标准化值,再乘以 PV_FAC 和 PV_OFF 转化为实际工程量(如温度0-400℃)。
输出端:先计算出 0.0~100.0 的百分比,然后乘以 276.48(2.764800e+02)还原为 0-27648 的整数量输出到模拟量通道,并硬性限幅在西门子S7模拟量允许的极限值 32511 / -32512 内。
西门子倍福WPF+C#数据库,槽式设备专家
flyfeky
bilibili 工控小工匠孔
级别: 论坛先锋
精华主题: 0
发帖数量: 928 个
工控威望: 1093 点
下载积分: 2298 分
在线时间: 692(小时)
注册时间: 2012-05-09
最后登录: 2026-04-06
查看flyfeky的 主题 / 回贴
2楼  发表于: 5天前
我直接让AI给你翻译的。
西门子倍福WPF+C#数据库,槽式设备专家