离网/并网切换时序与上位机联锁实战:厂家标称“切换时间 <10ms”,可每次停电路由器照样重启——我把那 118ms 一层层拆开,发现 90 毫秒根本不在开关上

2026年10月17日 0 By admin

先说结论,免得看到一半才发现不是你要的:

标称“切换时间 <10ms”这句话,缺一个起止点。 绝大多数厂家的 10ms 指的是静态开关(STS/晶闸管)从“判定要切”到“主回路换向”的动作时间;而从“电网电压真的跌下去”到“负载端电压真的恢复”,中间还隔着有效值检测窗、去抖确认、继电器释放、逆变建压四段。这四段加起来,在一台没有做相位连续的机器上,轻松就是 100ms 以上。

我手上这台 5kW 户用储能,实测 118ms。分解下来:检测 7.2ms、去抖 40ms、继电器 8ms、建压 50ms、软件调度残差 13ms。真正花在“开关”上的只有 8ms,占 6.8%。

而 118ms 这个数,足够让一台路由器重启、让一台工控机掉盘、让一台服务器报电源故障。这就是为什么“标称 10ms”和“体验上每次都断”可以同时为真——它们说的不是同一件事。

这篇要解决的是三件事:

  1. 把切换时间拆成可以一段段核对的账,而不是接受一个笼统的标称值;
  2. 把“无缝”的物理条件说清楚——它不是“快”,是“相位不断”;
  3. 把上位机(EMS/监控)在这件事里的位置摆正——它不该做切换决策,它该做的是知情、联锁、事后重对齐。

第三点是我见过的现场里错得最多的一条。太多系统的架构是“EMS 每秒读一次状态,读到电网没了就下发离网命令”。这条链路的总延迟是秒级的,而切换窗口是百毫秒级的。用秒级的环去控百毫秒级的事,注定控不住。


一、先把两个“切换”分开

中文里都叫“并离网切换”,但这是两个方向相反、难点完全不同的动作。混着讲,参数一定串。

方向 触发 主要矛盾 时间量级 失败表现
并网 → 离网 电网故障,被动 不能断太久,负载要活 10~200ms 负载重启、掉盘、PLC 复位
离网 → 并网 电网恢复,主动 不能乱合,冲击要小 0.5~60s 冲击电流、断路器跳、反复并脱

并转离的核心指标是 断电时长(transfer time)。

离转并的核心指标是 同期质量(Δθ、Δf、ΔU)+ 确认时长。

这两件事共用的只有一套东西:状态机、状态字、写权联锁。所以固件里必须是一台机器两个方向,但判据参数、去抖时长、允许写入的寄存器集合,全都是两套。把它们写成一套“切换参数”,是后面所有毛病的起点。

这篇以“并转离”为主线(因为它有明确的毫秒账可算),第八节单独讲“离转并”。


二、七层时间轴:T0 到 T6

我把整条链路切成七层。分层的好处是:现场出问题,先定位到层,再往下查参数,不用满世界翻。

层 名字 这一层决定什么 典型量级
T0 事件源 谁最先知道电网没了 0 ~ 1000ms
T1 判据 什么条件下认定“真没了” 7 ~ 60ms
T2 断开 主回路什么时候真的断开 2 ~ 150ms
T3 建立 离网电压什么时候建起来 20 ~ 200ms
T4 观测 上位机什么时候能看到 100ms ~ 数秒
T5 联锁 这段窗口里谁不许写什么 —
T6 回切 什么时候允许再并回去 0.5 ~ 60s

T0 是最容易被忽略、但最致命的一层。 因为“谁先知道”直接决定了后面的账有没有意义。

  • PCS 自己采样:最快,7~20ms 就能拿到结论。这是唯一正确的切换决策源。
  • 外部电表 / STS 控制器:多一次通信,+10~100ms,且要看它是 Modbus 还是硬线。
  • 上位机 / EMS:多一个轮询周期,+200ms ~ 数秒。

我见过一个项目,切换逻辑写在 EMS 里:EMS 每 500ms 读一次 PCS 的电网电压,低于阈值就下发“转离网”命令。听起来没问题,实际是:

电网跌落 ──→ 最多 500ms 后才被读到 ──→ 下发命令 20ms ──→ PCS 执行 118ms
总延迟最坏 638ms,最好 138ms

这个系统在“最好的情况”下都比一台纯本机逻辑的机器慢一个数量级。 而且它的延迟是抖的——取决于电网跌落发生在轮询周期的哪个位置。这种抖动在现场表现为“有时候断一下没事,有时候设备全重启”,然后所有人开始怀疑是“电网质量问题”。

所以 T0 的第一条铁律:

切换决策必须在保护任务里做,周期 ≤1ms。上位机只能知情,不能决策。

上位机可以做的是:读状态、记事件、切换后重新下发指令、报警。这些都不需要毫秒级响应。


三、T1 判据:窗口长度就是你的延迟下限

这一节是整篇里最“反直觉”的部分,也是我最想让人记住的一段。

3.1 有效值窗不是“延迟 W”,而是“延迟多少取决于跌多深”

绝大多数固件用滑动窗口算 RMS。窗口宽度 W 通常取一个工频周期 20ms(全波),或者半个周期 10ms(半波)。

问“检测要多久”,很多人的回答是“一个周期,20ms”。这个答案是错的,而且错得很有规律。

设电网电压从 U_old 跌到 U_new(用比例 k = U_new/U_old 表示),跌落发生在 t=0。窗口里在 t 时刻有 (t/W) 的比例是新值,剩下 (1-t/W) 是旧值。于是:

RMS²(t) = (1 - t/W) × U_old² + (t/W) × U_new²

要让它跌到门限 thr(相对 Un)以下,解出:

t/W = (1 - thr²) / (1 - k²)

代入 U_old = 230V、门限 0.80Un、全波窗 W = 20ms:

跌到 k 残余电压 t/W 检测延迟 性质
0.00 0 V 0.360 7.2 ms 深跌,最快
0.10 23 V 0.364 7.3 ms 深跌
0.30 69 V 0.396 7.9 ms 深跌
0.50 115 V 0.480 9.6 ms 中跌
0.60 138 V 0.562 11.3 ms 中跌
0.70 161 V 0.706 14.1 ms 浅跌,明显变慢
0.75 172 V 0.823 16.5 ms 浅跌
0.79 182 V 0.958 19.2 ms 浅跌,接近满窗
≥0.80 ≥184 V — 永不越限 残压高于门限

跌到 0,只要 7.2ms 就越限;跌到 0.79Un,要 19.2ms。 差了 2.7 倍。

这条规律的意义在于:“我的检测只要 5ms”这种说法,必须带上“跌到多少”才有意义。 厂商宣传的快速检测,测的是深度跌落(往往是直接断开,跌到 0)。而现场真正难处理的,是那种“跌到 0.7~0.8Un、持续几十毫秒”的浅跌——它慢、它不触发、它让设备在临界电压上跑。

顺带解释了一个常见现象:同一台机器,在电网强的现场切换很利索,在电网弱的现场切换很肉。 因为弱电网的跌落往往更浅、更“软”,正好落在上表最慢的那一段。

3.2 半波窗的诱惑与代价

半波窗(10ms)能把上表所有延迟减半。听起来很香。

代价是:半波 RMS 对直流分量和偶次谐波不敏感。当负载里有半波整流、或者变压器有直流偏磁时,波形正负半周不对称,半波 RMS 会长期偏移——轻则门限漂移(本来 0.8Un 的动作点变成 0.83Un),重则稳态误判(电压明明正常,读出来偏低)。

离网切换判据建议一律用全波窗。 想快,从别的地方省:去抖次数、建压斜坡,这两个是可以谈的;窗长是不能谈的,它是精度的物理下限。

3.3 去抖:这才是 40ms 的去处

检测只花 7.2ms,那 118ms 里的第二个大头在哪?在去抖。

“去抖”的意思是:检测到一次越限不算数,要连续 N 次/连续 N 个周期都越限才算数。原因是电网里真实存在重合闸(recloser)——配电网故障后,断路器会在 0.3~1s 内自动重合一次,电压会短暂恢复;还有大负载投切造成的瞬时凹陷。

如果不去抖,机器会在一次重合闸里切两次,负载被折腾两遍。

我这台机器的参数是:连续 3 次判定(每次间隔 5ms)+ 再等 2 个工频周期确认 = 40ms。这个数在行业里算比较保守的,激进一点的做到 20ms,代价是误切率上升。

关键点:去抖时长必须和“负载能扛多久”一起定。 如果负载只能扛 40ms(见第六节),那 40ms 的去抖就是自杀——等确认完,负载已经死了。

这就引出一个设计约束,我把它写成不等式:

T_detect + T_debounce  <  T_load_holdup

左边是“确认电网真没了”的最短时间,右边是“负载自己能顶多久”。这条不等式不成立的时候,你只有两条路:要么换无缝方案(相位连续,根本不断电),要么接受负载会重启。


四、T2 断开:器件时间表,以及“10ms”到底指什么

把开关器件的真实时间摆出来,就知道 10ms 是从哪来的了。1 个工频周期 = 20ms,半个周期 = 10ms。

器件 动作 释放 备注
电磁继电器(小型功率) 5 ms 8 ms 无灭弧,直流侧要注意拉弧
接触器(AC-1) 10 ms 20 ms 吸合 10~20ms,释放 5~15ms
ATS 机械转换开关 80 ms 150 ms 含机械联锁与灭弧,总时间 100~300ms
STS 晶闸管(半控) 4 ms 4 ms ≤ 半个工频周期,靠自然过零关断
STS 晶闸管(全控/混合) 2 ms 2 ms 可强制换流,代价是损耗与散热
固态继电器 SSR(过零型) 10 ms 10 ms 最多等半个周期才导通

“<10ms”这个数字,来源就是晶闸管能在半个工频周期内完成换向。 这是真的,但它只描述 T2 这一层。

于是问题就变成了:T2 = 4ms,其他五层加起来是多少?

层 我这台 能不能压
T1 检测 7.2 ms 不能(窗长决定)
T1 去抖 40 ms 能,但误切率上升
T2 释放 8 ms 能(换 STS 到 2~4ms)
T3 建压 50 ms 能,但伤负载
T4 软件残差 13 ms 能,收紧任务周期
合计 118.2 ms

反解一下:假设器件已经是理想 STS(0ms),要把总时间做到 10ms,留给“检测 + 去抖 + 建压”的预算只有 10ms。而检测一项就要 7.2ms。

所以,凡是号称 10ms 又没做相位连续的机器,标称值一定不含检测和建压。 这不是骗人,是行业里没有统一口径。问的时候只需要问一句:

“你们这个切换时间,起点是电网电压跌到多少伏,终点是负载端电压恢复到多少伏?”

问出这句话,对方要么给你一份实测波形,要么开始含糊。含糊的就按 100ms 估。


五、T3 建立:无缝的本质是“相位不断”,不是“快”

这一段是很多人绕不过去的弯。

5.1 为什么大多数机器要“重新建压”

常规流程是这样:检测到电网没了 → 封锁 PWM → 断开主继电器 → 切换控制模式(电流源 → 电压源)→ 重新启动电压环 → 输出电压从 0 斜坡升到 230V。

那个“斜坡”就是 50ms 的来源。为什么要斜坡?因为突然给一个感性负载(变压器、电机)加满电压,会产生很大的励磁涌流;给容性负载加满电压,会产生充电尖峰。斜坡是把冲击摊平。

问题是:斜坡一开,负载端的电压就从 230V 掉到 0 再爬回 230V。这中间是真空的。

5.2 相位连续:把“断电”变成“换源”

并网运行时,逆变器的输出电流是锁相于电网的——它内部一定有一个正在跟踪的相位角 θ(t),从 0 到 360° 循环。

电网消失的那一刻,θ 恰好等于某个值 θ0。

如果此时不让相位归零,而是让内部振荡器从 θ0 继续积分,那么逆变器切到电压源模式的瞬间,输出电压的相位就是连续的——负载看到的是一个“相位没有跳变、幅值没有跌落”的电源。

并网:  u_out = U_grid × sin(θ_grid)     θ 跟踪电网
       ↓ 电网消失,θ = θ0
离网:  u_out = 230 × sin(θ_free)        θ_free 从 θ0 继续积分

这么做的话,T3 的“建压”时间就消失了——因为电压从来没掉下去过,只是参考源从 PLL 换成了本地振荡器。整条链路只剩检测 + 去抖 + 换向,可以做到 10~20ms 量级。

但这里有个前提:切换必须在电流过零点附近完成(或者至少要让电感电流有续流路径),否则储能元件里的能量没处去,会顶出一个电压尖峰。工程上的做法是:封锁 PWM 后等一个 PWM 周期让电流续流完毕,再切模式;这个等待通常 50~200μs,可以忽略。

5.3 相位不连续会怎样

如果相位归零重来(θ_free 从 0 开始),而断开瞬间 θ0 = 180°,那么负载端看到的是一个从负半周突然跳到正半周的电压。

对纯阻性负载,就是一次电压跳变,没事。

对变压器,是一次直流偏磁——铁芯饱和,励磁电流飙升到额定电流的 5~10 倍,持续几个周期。现场表现是:切换瞬间变压器“嗡”一声巨响,有时连带空开跳闸。

对电机,是一次转矩冲击,机械上听得见。

这就是为什么有些现场“切换成功、负载也没重启,但变压器开始发热”——相位跳变的后遗症,往往几天后才显现。

5.4 固件骨架

/* 相位连续切换的核心:只换参考源,不重置积分器 */
typedef struct {
    float theta;        /* 当前相位,弧度,0~2pi */
    float omega;        /* 角频率,2*pi*50 */
    float u_amp;        /* 幅值 */
    uint8_t mode;       /* 0=并网(电流源) 1=离网(电压源) */
} inv_ctrl_t;

static inv_ctrl_t g;

/* 并网:theta 由 PLL 拉动;离网:theta 自积分 */
void ctrl_tick_1ms(void)
{
    if (g.mode == MODE_GRID) {
        g.theta = pll_get_theta();          /* 跟着电网走 */
    } else {
        g.theta += g.omega * 0.001f;        /* 自己走,不清零 */
        if (g.theta >= 6.2831853f) g.theta -= 6.2831853f;
    }
}

/* 切换入口:只改 mode,不动 theta */
void transfer_to_island(void)
{
    pwm_disable();                 /* 封锁,等续流 */
    delay_us(150);
    g.mode = MODE_ISLAND;          /* theta 保持连续 */
    vloop_reset_but_keep_theta();  /* 积分器复位,相位保留 */
    pwm_enable();
}

注意 vloop_reset_but_keep_theta() 这个函数名——它就是这个设计的全部要点。电压环的积分器要清(否则带着并网时的稳态误差进场),相位不能清。


六、T4 负载侧:电容储能除以功率,这是负载的全部底牌

前面一直在算“电源能多快恢复”。但真正决定“会不会重启”的,是负载自己能顶多久。

开关电源的输入侧一定有一个整流后的高压电容。AC 掉电后,负载靠这个电容里的能量继续工作,直到母线电压跌到稳压器的下限。

E = 0.5 × C × (V1² - V2²)
t = E / P

V1 是正常时的母线电压(230Vrms 整流后 ≈ 325V),V2 是开关电源还能稳压的最低母线电压(通常 180~250V,取决于拓扑和输入范围)。

按 118ms 的断电缺口,算几个真实负载:

负载 C V1 V2 P hold-up 118ms 缺口
路由器 + 光猫 33 μF 325 V 180 V 30 W 40.3 ms 重启
工控机(ATX) 330 μF 325 V 250 V 120 W 59.3 ms 重启
服务器(冗余电源) 660 μF 325 V 250 V 500 W 28.5 ms 重启
PLC(24V 开关电源) 47 μF 325 V 200 V 20 W 77.1 ms 重启
变频器控制板(取自直流母线) 100 μF 530 V 400 V 15 W 403 ms 扛得住

这张表解释了两件事。

第一,为什么“10ms 的 UPS 级”是个真实存在的品类。 不是营销话术,是这批电容真的只能顶 30~80ms。10ms 对它们是“够用且便宜”的设计点。而 118ms 对它们全是死。

第二,为什么变频器/伺服这类设备反而不怕。 它们的控制板从直流母线取电,母线电容大、电压高,能量按 V² 走,顶几百毫秒很轻松。真正娇气的是那些用小反激、母线电容几十微法的通信设备和工控机。

所以选型的时候有个很实用的判据:

问负载厂家一句:“你的电源 hold-up 时间是多少毫秒?”

答不上来的,按 20ms 估——那是个只够扛半个工频周期的数。

算下来,要把 118ms 做到“所有负载都不重启”,唯一的路是让断电时间小于最娇气那个负载的 hold-up。按 20ms 算,就是 T1+T2+T3 ≤ 20ms。这又回到了相位连续——只有它能把 T3 抹掉。


七、T5 联锁:Modbus 侧到底要做几件事

现在换到上位机这一侧。前面全是功率电子,这一节是通信与协议。

7.1 状态字必须分层,不能只有一个“运行”

最常见的错误设计:状态字只有一个 bit,“0 = 停机,1 = 运行”。于是“并网运行”和“离网运行”在协议上无法区分,EMS 只能靠“电网电压是否为零”去猜,而这个判断本身就有 20ms 以上的延迟和一堆边界情况。

正确的做法是状态分层 + 切换态单列:

位 名称 含义
bit0 GRID_TIED 并网运行(电流源,跟随电网)
bit1 ISLAND 离网运行(电压源,自建网)
bit2 TRANSFERRING 切换中(两个方向都置这一位)
bit3 FAULT 故障停机
bit4 ISLAND_LOCKOUT 孤岛保护动作后锁定,需人工/延时复位
bit5 STANDBY 待机(无电网、无负载、等指令)
bit6 LOCAL 本地模式(远程写被禁止)
bit7 DERATING 降额运行(温度/电池限流导致)

规则三条:

  1. bit0 与 bit1 互斥,同一时刻最多一个为 1;
  2. bit2 为 1 时,bit0/bit1 必须都是 0——切换中就是“既不在并网也不在离网”;
  3. bit2 的持续时间 = 实际切换时间,这是自证标称值的最好证据。

我特别推荐第 3 条。固件里记一个 transfer_ms,每次切换结束时把实测值写进寄存器。这样标称 10ms 的机器,跑一个月,寄存器里的最大值会诚实地告诉你它是 10 还是 118。

7.2 切换窗口内,哪些寄存器必须禁写

这是联锁的核心。原则很简单:凡是“只在并网下有意义”的量,离网时必须拒绝;凡是“只在离网下有意义”的量,并网时必须拒绝。

寄存器类 并网 离网 切换中 拒绝时回什么码
并网有功指令 允许 拒绝 拒绝 0x06
功率因数 / 无功指令 允许 拒绝 拒绝 0x06
充放电限流(电池侧) 允许 允许(但按离网限值) 拒绝 0x06
离网输出电压设定 拒绝 允许 拒绝 0x06
离网频率设定 拒绝 允许(下垂曲线参数) 拒绝 0x06
开/关机 允许 允许 拒绝 0x06
故障复位 允许 允许 拒绝 0x04
参数保存(落 NVM) 允许 允许 拒绝 0x06
只读量(电压/电流/SOC) 允许 允许 允许 —

注意“充放电限流”这一行的差别:并网时它是“我允许多大功率进出电网”,离网时它是“我允许电池放多大电流给负载”。同一个寄存器,两个模式下语义完全不同。 如果 EMS 不知道当前模式,这个值写下去就是错的。

7.3 回哪个异常码,是有讲究的

Modbus 的异常码不是装饰,它承载了“为什么不让你写”这个信息。可惜现场 90% 的 EMS 只看“成功/失败”。异常码的基础语义在同站那篇 Modbus 协议在工业物联网中的实现 里铺过,这里只讲“切换联锁”这个特定场景该选哪个。

码 名称 含义 上位机该怎么处置
0x01 非法功能 从站不支持该功能码 查点表功能码列
0x02 非法数据地址 寄存器不存在或越界 多半是 4xxxx 换 0 基算错
0x03 非法数据值 值超范围 / 数量超上限 范围拒绝不要 clamp,直接回 03
0x04 从站设备故障 设备处于不可执行态 最容易被误当“通信故障”,其实设备活着
0x05 确认(长任务) 已受理,需轮询结果 切换、自检、写 NVM 建议用它
0x06 从站设备忙 正在执行,请稍后重试 切换窗口内最贴切的码
0x08 存储奇偶差错 写入校验失败 重试并计数,连续失败升级为 04
0x0A 网关路径不可用 网关后目标不可达 串口服务器/网关场景
0x0B 网关目标无响应 目标设备超时 与 0A 的区别:网关在、下游不在

三条实务建议:

第一,切换中回 0x06。 这不是“协议错误”,是“现在不行、等会再来”,语义最准。比回 0x04(设备故障)更轻,比回 0x03(值非法)更诚实——值没错。

第二,写权互锁导致的拒绝回 0x04。 地址对、值也对,只是设备当前状态不允许(本地模式、停机、故障锁定)。这时候回 0x02 会让人去查地址,白查半天。

第三,异常码必须原样进日志。 只记“写入失败”的 EMS,等于把 90% 的诊断信息扔了。我在现场排查“半夜充放电限流莫名其妙变成 30A”这个问题,最后就是靠 EMS 日志里一条 EXC=06 定位到“那次写入正好撞上切换窗口,被正确拒绝了,但 EMS 重试逻辑把它当成了瞬时错误,三秒后又写了一次,那次写进去了但值是上一次的缓存值”。

这条链路如果 EMS 不记异常码,永远查不出来。

7.4 事件计数器:为什么必须有

这是我最想强调的一条设计。

切换只持续 118ms,而 EMS 的轮询周期是 1s。 也就是说:

命中概率 = 118 / 1000 = 11.8%
漏掉概率 = 88.2%

88% 的切换,EMS 完全看不见。 它看到的只是“上一次是并网,这一次是离网”,中间发生了什么一概不知。于是日志里全是“并网 → 离网”,没有“切换中”,看起来像是瞬间完成的——这恰恰印证了那个错误的标称值。

解决办法不是加快轮询(1s 已经占了不少带宽,提到 100ms 只是把命中率提到 100%,但代价是总线负载和从站 CPU),而是加一个单调递增的事件计数器:

寄存器 名称 类型 说明
0x0100 状态字 U16 见 7.1
0x0101 模式 U16 0 停机 / 1 并网 / 2 离网 / 3 切换中
0x0102 切换事件计数 U16 每次切换结束 +1,模 65536 回绕
0x0103 上次切换原因 U16 1 欠压 / 2 过频 / 3 孤岛 / 4 手动 / 5 远程
0x0104 上次切换耗时 U16 ms
0x0105 最大切换耗时 U16 ms,掉电保持

EMS 每次轮询比对 切换事件计数,变了就说明中间发生过切换(可能不止一次),然后去读原因和耗时。这样即使 100 次切换里有 88 次没被“看见过程”,也能被“看见结果”。

最大切换耗时 这个寄存器是给验收用的。装完之后跑一个月,读一次这个值,就知道厂家的标称是真是假。

7.5 撕裂:一次读多个寄存器时的假数据

EMS 一次读 20 个寄存器(0x03 功能码),这 20 个寄存器不是同一时刻的快照,而是一帧一帧搬过来的。如果状态正好在搬运过程中变化,就会读到“前半段是旧状态、后半段是新状态”的混合体。

撕裂概率 = 读帧占空比 = 帧时长 / 轮询周期:

波特率 一帧(20 寄存器)时长 撕裂概率
9600 55.6 ms 5.56%
19200 27.8 ms 2.78%
115200 4.6 ms 0.46%

注意这个概率和“切换持续多久”无关——只和“读帧在时间轴上占多大比例”有关。这是个很容易算错的点,我第一版把它和切换时长乘在一起了,算出来 0.66%,实际是 5.56%,差了 8 倍。

撕裂的后果很具体:状态字读到“并网”,功率读到“离网值”,两个数各自都对,组合起来是假的。 EMS 拿这个组合去做功率平衡计算,就会得出一个不存在的结论。

防御办法是一致性校验,不要试图用协议层解决。这一条和 逆变器 Modbus 点表与上位机对接实战 里讲的“32 位量撕裂”是同一个物理事实:多寄存器读不是快照。 只是那次撕裂发生在两个寄存器之间,这次发生在“状态字”和“功率值”之间。

读到的状态是 GRID_TIED 时:
    并网有功 与 离网输出电压 不可能同时有效
    若 离网输出电压 > 50V,则本次读数整帧作废,下一轮重读
读到的状态是 ISLAND 时:
    并网功率因数 必须是无效值/哨兵值

最省事的做法:状态字和它管辖的量放在同一帧里读,并且在固件里保证“状态切换时,同一帧内的相关量要么全是旧值、要么全是新值”。这需要固件提供一个“快照锁”——中断里改状态时,同时把整组影子寄存器原子更新。


八、T6 回切:难点不在同期,在“确认电网真的回来了”

并网转离网是被动的,越快越好。离网转并网是主动的,越稳越好。

8.1 同期三条件

合闸瞬间,逆变器输出电压与电网电压之间如果有差异,就会产生环流。三个差异各自对应一种环流:

相角差 Δθ → 有功冲击。 两个同幅值、相差 Δθ 的正弦,其瞬时差仍是一个正弦,有效值为:

ΔU = 2 × U × sin(Δθ / 2)

代入 U = 230V、回路阻抗 Z = 0.4Ω(电网短路阻抗 + 电缆 + 变压器漏抗折算):

Δθ ΔU_rms 冲击电流 判定(按 130A 额定)
0.5° 2.01 V 5.0 A 可接受
1.0° 4.01 V 10.0 A 可接受
2.0° 8.03 V 20.1 A 偏大
3.0° 12.04 V 30.1 A 偏大
5.0° 20.06 V 50.2 A 偏大(38.5% 额定)
8.0° 32.09 V 80.2 A 不可接受
10.0° 40.09 V 100.2 A 不可接受
20.0° 79.88 V 199.7 A 严重

5° 就是 50A。 这个数字解释了很多现场现象:同期整定写成“±10° 允许合闸”,合闸瞬间 100A 冲击,空开跳,然后系统以为是“并网失败”,重试,再跳。

压差 ΔU → 无功环流。 ΔU = 2%(4.6V)在 Z=0.4Ω 下就是 11.5A,全是无功。这个量级通常能接受,但如果变压器容量小、Z 大,压差的影响会被放大。

频差 Δf → 相角滑移。 这是最微妙的一条:

滑移率 = 360 × Δf   (度/秒)
Δf 滑移率 跨 ±5° 用时 跨 ±10° 用时
0.005 Hz 1.8 °/s 5.56 s 11.11 s
0.010 Hz 3.6 °/s 2.78 s 5.56 s
0.020 Hz 7.2 °/s 1.39 s 2.78 s
0.050 Hz 18.0 °/s 0.56 s 1.11 s
0.100 Hz 36.0 °/s 0.28 s 0.56 s
0.200 Hz 72.0 °/s 0.14 s 0.28 s
0.500 Hz 180.0 °/s 0.06 s 0.11 s

这张表是“合闸窗口”的全部。 窗口宽度 T = 2θ_max / (360 × Δf)。

窗口内还得走完:相角测量(1 个工频周期 20ms)+ 出口继电器(8ms)+ 断路器合闸(40~80ms),合计约 88ms。

反解一下“要多小的 Δf 才够”:

允许窗口 Δf 上限
100 ms 0.278 Hz
200 ms 0.139 Hz
500 ms 0.056 Hz
1000 ms 0.028 Hz
2000 ms 0.014 Hz

好消息是:这条通常不是瓶颈。 离网时逆变器频率由本机晶振决定(50.00Hz ±0.01Hz),电网也在 50.00Hz 附近,Δf 天然就在 0.01~0.05Hz 量级,窗口 0.5~2.8s,够用。

坏消息是:如果离网时带了 70% 负载、还开了 5% 的 P-f 下垂,频率是 48.1Hz。 Δf = 1.9Hz,滑移率 684°/s,窗口 15ms。这时候同期合闸物理上不可能成功——执行链要 88ms,窗口只有 15ms。

这个坑非常隐蔽,因为它在空载测试时完全正常。第九节专门讲下垂。

8.2 真正的难点:确认电网“真的”回来了

电网恢复的瞬间,电压会先出现一个不稳定的过渡过程——配电网上游的重合闸、其他并网机组的再同步、大负载的重新投入,都会让电压在几百毫秒内反复波动。

如果“看到电压在窗口内”就合闸,会发生什么:合上 → 电网又跌 → 触发孤岛保护 → 断开 → 电网又恢复 → 再合。这个循环在现场叫“打摆子”,能持续几分钟,把断路器和继电器折磨到寿命耗尽。它的结构与 储能 PCS 绝缘监测与漏电流检测实战 里那条“误报来自读数生成、漏报来自判据结构”完全同源:这里不是判据错了,是判据的“确认时间”被省掉了。

所以回切的判据必须是:

电压在合格窗口内,且频率在合格窗口内,连续保持 T_confirm(通常 0.5~60s,可设)

T_confirm 怎么定?经验值:

  • 户用/小型(<10kW):0.5~5s
  • 工商业(10~100kW):5~30s
  • 电网弱、历史上打摆子过的现场:60s
  • 有柴油机/其他电源并联的微网:按最慢的那台来

这个参数必须可配,而且要能写进 NVM 掉电保持。 出厂默认 5s 是个比较安全的起点。

8.3 回切状态机

ISLAND
  ├─ 检测电网电压/频率进入窗口
  ↓
GRID_RETURN_CONFIRM(持续计时 T_confirm,任何一次越限立刻归零重来)
  ├─ 计时未到 → 留在本状态
  ├─ 计时到 → 且 Δθ、Δf、ΔU 都在同期窗口内 → 允许合闸
  ↓
SYNCING(相位跟踪,把本地振荡器拉到电网上,PLL 缓慢收敛)
  ├─ 收敛完成(Δθ < 阈值)→ 合闸
  ├─ 超时(如 3s 未收敛)→ 回 ISLAND,记一次失败,延时后重试
  ↓
GRID_TIED

注意 SYNCING 这一步不能省。直接从 ISLAND 合闸是靠“碰窗口”,而 SYNCING 是先把本地相位拉到电网上(用 PLL 小带宽慢慢收敛,避免频率突变造成负载扰动),等 Δθ 稳定在阈值内再合。前者是打靶,后者是并轨。


九、下垂:为什么离网时的频率读数不是 50.00

离网运行时,逆变器是电压源,频率由它自己定。但为了多机并联时能自动分配负载,通常要加下垂控制(droop):

f = f0 × (1 - droop_p × P/P_rated)
U = U0 × (1 - droop_q × Q/Q_rated)

按 5% 的 P-f 下垂、5kW 机器:

负载率 有功 频率 电压
0% 0 W 50.00 Hz 230.0 V
25% 1250 W 49.38 Hz 228.6 V
50% 2500 W 48.75 Hz 227.1 V
75% 3750 W 48.12 Hz 225.7 V
100% 5000 W 47.50 Hz 224.2 V

“离网频率 50.00Hz”这个读数只在空载成立。 带 70% 负载读到 48.2Hz 是设计如此,不是故障。

这里有三个连锁问题:

第一,EMS 用并网口径的频率窗口去判离网机,必然误报。 并网侧常见的频率保护窗口是 49.5~50.5Hz(甚至更窄),拿它去判一台带载 70% 的离网机,直接报“频率异常”。我在三个现场见过这个误报,其中两个的操作员因此以为机器坏了,手动关机。

正确做法:状态字是 ISLAND 时,EMS 切换到离网口径(频率 47.5~50.5Hz,且要看负载率),并且这条规则要写进 EMS 的点表配置里,不能靠现场改代码。

第二,下垂会导致回切时同期失败(第 8.1 节算过:1.9Hz 的 Δf,窗口 15ms)。

解决办法是在回切前先卸下垂:进入 SYNCING 之前,把下垂系数临时置 0,让频率回到 50.00Hz,再拉 PLL。这个动作要快(几百毫秒),且期间负载会有轻微扰动,但比合闸失败好得多。

第三,多机并联时,下垂系数是负载分配的唯一依据。 两台机器下垂系数不一致,就会出现“一台满载、一台空转”。这个不是切换问题,但会在切换后暴露出来——因为切换前是并网,功率由电网决定;切换后才看下垂。


十、电池侧:切换瞬间那 102.8A

前面全是交流侧。但离网切换对电池是一次实打实的冲击。

并网时,负载由电网供电,电池可能在充电(电流为负)。切换瞬间,负载全部转由电池承担,电流从负数跳到正数,跳变量是两者的和。

算一下最基本的:

I_bat = P_load / (η × U_bat)

5kW 负载、51.2V 电池(16S LFP 标称)、95% 效率:

I = 5000 / (0.95 × 51.2) = 102.8 A

如果 BMS 的放电限流 DCL 是 100A——不够,差 2.8%。

负载 电池侧电流 对 100A DCL 的占比
2000 W 41.1 A 41.1%
3000 W 61.7 A 61.7%
5000 W 102.8 A 102.8%
6000 W 123.4 A 123.4%
8000 W 164.5 A 164.5%
10000 W 205.6 A 205.6%

5kW 机器配 100A DCL 的电池,余量只有 -2.8%。 这在户储里极其常见——电池按“5kW 持续”标称,但那个 5kW 是在 25℃、SOC 50%、短时口径下测的;到了冬天、SOC 低、又要连续放电,实际能力打折。

现场表现就是:并网好好的,一停电就跳。 因为并网时电池不出力,一切正常;离网时电池要出全部功率,BMS 判定过流,限流甚至关断,母线塌,负载二次掉电。

更麻烦的是 di/dt。假设切换前电池在充电 -30A,切换后要放电 +102.8A,变化量 132.8A。BMS 的过流判据通常是“1.2~1.5 倍 DCL 持续 100ms”,这个跳变会直接触发它。

所以切换逻辑里必须加一条预判:

/* 切换前先算这笔账,算不过就不许切 */
bool island_feasible(float p_load_est)
{
    float i_need = p_load_est / (0.95f * bat_voltage());
    float i_avail = bms_dcl_get();          /* 从 BMS 实时读,不是铭牌 */
    float margin  = i_avail - i_need;
    if (margin < 0.10f * i_avail) {         /* 留 10% 余量 */
        return false;                        /* 不切,或先降载 */
    }
    return true;
}

算不过怎么办?三个选项,按优先级:

  1. 降载:切换前先把非关键负载切掉(如果有负载管理),或限制放电功率;
  2. 切但报警:接受短暂的限流,把“离网负载超出电池能力”作为事件上报;
  3. 不切:这在储能里几乎不可接受(离网的意义就是保供电),所以实际上只有前两条。

另外,bms_dcl_get() 必须读实时值而不是铭牌值。BMS 会根据温度、SOC、SOH 动态调整 DCL——冬天 SOC 20% 时的 DCL 可能只有铭牌的一半。这个值走 CAN 还是 Modbus 取决于 BMS,上一轮那篇 储能 BMS 与 PCS 的 CAN 对接实战 里讲的 0x351 报文,第三个字段(字节 4~5)就是放电电流限值 DCL,单位 0.1A(其中字节 2~3 是充电限流 CCL,别对错位置)。


十一、多机并联:选主黑洞

单机切换的账算清楚了,多机还有一层。

并联运行的离网系统需要一个“主机”来建立电压参考,其他机器跟随。并网运行时大家都不用建网(电网在),所以主机身份是“闲置”的;一旦转离网,必须有人立刻接管建网。

如果主机正好是那台检测到故障、正在重启的机器,其他机器就要等它——或者重新选主。

选主的过程通常是:检测到电压参考消失 → 等待 T_wait(100~500ms)→ 按优先级/地址竞选 → 胜出者建压 → 其他机器锁相。

这个 T_wait 就是第二段断电。 单机 118ms 的切换,在两机并联的系统里可能变成 118 + 300 = 418ms。

改善办法:

  • 静态指定主机(优先级写死),不做动态竞选。简单可靠,代价是主机故障时整个系统瘫。
  • 主机心跳 + 硬线:主机通过一根硬线(或 CAN 心跳)持续广播“我在”,从机检测到心跳丢失立刻接管,延迟可以做到 20ms 级。代价是多一根线。
  • 预选主:并网时就让备用主机同步跟踪主机的相位,随时可接管。这是最好的方案,也是实现最复杂的。

现场调试时,如果发现“单机测试切换 100ms,两机并联变成 400ms”,八成就是选主在吃时间。


十二、固件侧:状态机与三个必须掉电保持的量

12.1 状态机

        ┌──────────┐
        │  STANDBY │
        └────┬─────┘
             │ 上电自检通过 + 电网合格
             ↓
        ┌──────────┐  电网越限 + 去抖通过
        │ GRID_TIED├──────────────┐
        └────┬─────┘              │
             │                    ↓
             │            ┌───────────────┐
             │            │  TRANSFERRING │
             │            └───────┬───────┘
             │                    │ 断开完成 + 建压完成
             │                    ↓
             │            ┌──────────┐
             │            │  ISLAND  │
             │            └────┬─────┘
             │                 │ 电网恢复 + T_confirm
             │                 ↓
             │            ┌──────────┐
             │            │ SYNCING  │
             │            └────┬─────┘
             │                 │ Δθ 收敛
             └─────────────────┘

注意 TRANSFERRING 是双向共用的(回切时也经过它),所以状态字 bit2 的含义是“正在切换”,不分方向,方向由 上次切换原因 和前后状态推断。

12.2 三个必须掉电保持的量

  1. 切换事件计数(U16)——EMS 靠它发现“发生过切换”。丢了会让 EMS 漏事件。
  2. 最大切换耗时(U16,ms)——验收证据。丢了等于放弃了自证能力。
  3. T_confirm 等可配参数——现场调过的值,掉电回默认会导致行为变化。

写入策略沿用上一轮那篇 逆变器 Modbus 写控制与参数掉电保持实战 的结论:A/B 双区 + seq + CRC 原子提交,且只在值真正变化时写。切换计数每次切换都变,一天切 5 次的话一年 1800 次,EEPROM 寿命完全够;但别在切换过程中写(那时候电源最不稳),要在切换结束后延时 5s 再写。

12.3 保护任务的时间预算

1ms 任务:采样、过流/过压硬件比较器轮询、PLL 更新
5ms 任务:有效值计算(滑窗)、状态机 tick
20ms 任务:Modbus 从站、LED、风扇
100ms 任务:通信心跳、温度

状态机必须在 5ms 任务里跑,不能在 20ms 任务里。 20ms 的周期意味着最坏情况多 20ms 的判定延迟——这在 118ms 的总账里占 17%,白送的。这条约束和 LIN 总线主从调度与报文超时实战 里那条“时隙必须大于帧最大时长 + 抖动”是同一条纪律:周期一旦小于你要处理的事件时长,事件就会被截断。


十三、上位机侧:轮询黑洞、撕裂、重对齐

13.1 三个不要

不要用上位机做切换决策。 前面算过:500ms 轮询 + 118ms 执行 = 最坏 638ms。用秒级的环控百毫秒级的事,控不住。

不要用“电网电压是否为 0”判断模式。 电压为 0 可能是采样故障、可能是通信中断(读到 0 而不是超时)、可能是真的停电。必须用状态字。

不要在切换后沿用切换前的指令。 这是最隐蔽的一条——切换前 EMS 下发的是“并网充电 40kW”,切换后 PCS 变成离网电压源,这个指令要么被拒绝(正确),要么被错误地映射成“放电 40kW”(灾难)。

13.2 切换后的“重对齐”流程

1. 轮询发现 切换事件计数 变化
2. 读 上次切换原因 / 上次切换耗时 / 当前模式
3. 若当前模式 = ISLAND:
     a. 停止下发所有并网类指令(有功/无功/PF)
     b. 若有离网类指令(输出电压/频率/下垂),重新下发当前期望值
     c. 若配置了负载管理,按优先级切除非关键负载
     d. 上报事件(原因、耗时、当前 SOC、当前负载率)
4. 若当前模式 = GRID_TIED(回切完成):
     a. 并网类指令从 0 开始斜坡恢复,不要直接跳到切换前的值
     b. 校验电池是否已从“放电”转回“充电/待机”
     c. 清除离网期间的告警(但保留记录)
5. 记录本次事件的完整快照到日志

第 4a 条特别重要。回切后直接把功率指令恢复到切换前的值,会造成一次功率阶跃——电网侧看到的是几百毫秒内功率从 0 跳到 40kW,可能触发上级的功率变化率限制(很多园区的需量控制或电网的爬坡率要求)。

正确做法是斜坡:0 → 目标值,用时 5~30s,斜率可配。这也顺带解决了另一个问题:切换后电池 SOC 可能已经变化,原来的功率指令不再合适。

13.3 点表配置

{
  "version": "2026-10-17",
  "device": "PCS-5K",
  "status": {
    "reg": 256,
    "bits": {
      "grid_tied": 0,
      "island": 1,
      "transferring": 2,
      "fault": 3,
      "island_lockout": 4,
      "standby": 5,
      "local": 6,
      "derating": 7
    }
  },
  "event": {
    "counter": 258,
    "last_reason": 259,
    "last_duration_ms": 260,
    "max_duration_ms": 261
  },
  "write_guard": {
    "grid_only": [512, 513, 514],
    "island_only": [520, 521, 522],
    "never_during_transfer": [512, 513, 514, 520, 521, 528, 529],
    "reject_code_transfer": 6,
    "reject_code_mode": 4
  },
  "ramp": {
    "grid_power_recover_s": 10,
    "grid_power_step_kw": 4
  }
}

write_guard 这段是给 EMS 用的声明式联锁:不是靠代码里 if-else 硬编码,而是配置驱动。换一台 PCS 只改配置,不改代码。


十四、现场五招

第一招:先量“断电缺口”,别信标称值。

负载端接一个示波器(差分探头,或者一台带录波的万用表),触发条件设“电压跌到 200V 以下”,抓一次真实停电。量两个时间点:电压开始跌、电压恢复到 90%。这个差值就是你要的答案。没有示波器就用一个带事件记录的 USB 电压记录仪(几百块),采样率 1kHz 就够。

第二招:问清起止点,问不出就按 100ms 估。

“你们这个切换时间,起点是电网电压跌到多少伏,终点是负载端电压恢复到多少伏?”答不上来的,按 100ms 设计下游。

第三招:读“最大切换耗时”寄存器,跑一个月。

如果固件提供了这个寄存器(7.4 节),装完跑一个月读一次。这是唯一能长期自证的数据。没提供的话,建议加上——对厂家是自证,对用户是验收。

第四招:切换后立刻读“上次切换原因”。

原因码能立刻区分“电网真没了”和“我的判据太敏感”。前者要查电网,后者要调参数。没有原因码的机器,只能靠猜。

第五招:并联系统单独测“单机”和“双机”。

单机 118ms、双机 418ms,说明选主在吃时间。先解决选主,再谈切换指标。


十五、联锁自查清单

装机和联调时逐条过,一条不落。

时间账

  • ☐ 量过真实断电缺口(示波器/记录仪),不是抄标称值
  • ☐ 标称值的起止点已向厂家确认(起:电网跌到多少伏;终:负载恢复到多少伏)
  • ☐ 检测窗长度、去抖次数、建压时长在固件里可查(不是黑盒)
  • ☐ 满足不等式:T_detect + T_debounce < 最娇气负载的 hold-up
  • ☐ 最娇气负载的 hold-up 已向负载厂家确认(问不到按 20ms 估)

状态与协议

  • ☐ 状态字区分 GRID_TIED / ISLAND / TRANSFERRING,且三者互斥
  • ☐ TRANSFERRING 期间 bit0、bit1 都为 0
  • ☐ 有切换事件计数器(U16,模回绕)
  • ☐ 有上次切换原因、上次切换耗时、最大切换耗时
  • ☐ 最大切换耗时掉电保持
  • ☐ 切换中拒绝写并网类寄存器,回 0x06
  • ☐ 模式不允许时回 0x04,不是 0x02
  • ☐ EMS 日志记录原始异常码,不只记“失败”
  • ☐ 状态字与功率类量在同一帧读,且有跨状态一致性校验

回切

  • ☐ 电网恢复确认时间 T_confirm 可配、掉电保持,默认 ≥5s
  • ☐ T_confirm 期间任何一次越限都归零重来
  • ☐ 同期三条件(Δθ / Δf / ΔU)都参与判断,不是只看相位
  • ☐ 合闸窗口按 Δf 算过,执行链(测量+继电器+断路器)时长 < 窗口
  • ☐ 回切前卸下垂,把频率拉回 50.00Hz 再进 SYNCING
  • ☐ SYNCING 有超时退出(3s 未收敛回 ISLAND 并记录)

电池与并联

  • ☐ 切换前按 P/(η×U) 算电池侧电流,与实时 DCL 比对
  • ☐ DCL 从 BMS 实时读,不是铭牌值
  • ☐ 留 10% 电流余量
  • ☐ 并联系统测过单机/双机切换时间差,定位过选主延迟

小结

回到开头那个问题:为什么标称 10ms,负载还是每次都重启?

因为 10ms 是开关的动作时间,不是负载的断电时间。 后者是 118ms,中间隔着检测、去抖、建压,以及 13ms 我说不清但确实存在的软件残差。

三个可以直接拿走的结论:

一、切换时间的账必须自己算,且要算到每一层。

7.2 + 40 + 8 + 50 + 13 = 118.2ms,占大头的不是开关(8ms,6.8%),是去抖(40ms,33.8%)和建压(50ms,42.3%)。想优化,先别动开关,动这两项。

二、无缝的本质是相位连续,不是快。

记住 θ0、让振荡器从 θ0 继续积分、电压环复位但相位不复位——这三件事做到了,T3 就从 50ms 变成 0。做不到,换再快的开关也只能做到 60ms 级。

三、上位机的位置是“知情 + 联锁 + 重对齐”,不是“控制”。

88% 的切换它看不见(118ms vs 1s 轮询),5.56% 的读取会撕裂(9600 波特率、20 寄存器),回切后直接恢复旧指令会造成功率阶跃。这三件事都有解,但解法都不在“加快轮询”上——在事件计数器、一致性校验、斜坡恢复上。

最后一个我想留在这儿的观察:

这个行业里最贵的成本,不是器件选错了,是口径没说清。

“切换时间”没说起止点,“SOC”没说是哪个 SOC,“效率”没说是满载还是峰值,“寿命”没说是循环还是日历。

每一次口径模糊,都会在半年后变成一个现场故障。

下一篇写离网备电时长。一块 10kWh 的电池,铭牌写“备电 4 小时”,实际 1 小时 40 分就关机——中间少掉的那 2 小时 20 分,藏在放电深度、效率曲线、待机自耗、温度降额和“负载不是恒定功率”这五件事里。正好接在这篇后面:这篇讲“切换那 118ms”,那篇讲“切换之后能撑多久”。SOC 这一层的底子在 电池管理与 SOC 估算实战 里讲过,那篇只讲“读数准不准”,新篇讲“能放多少”。


本文的所有时间、电流、能量数字均由随文脚本 grid_transfer_calc.py 计算并可复现(--self-test 40 项全过)。其中标准档位类数值(并网电压异常响应区间)为工程常见口径,落地前请以项目适用的并网导则原文为准;器件动作时间为典型范围,具体以所选型号数据手册为准。