故障定位方法)
1. 分布式電源入網(wǎng)后傳統(tǒng)故障定位為什么開始“失靈”1.1 傳統(tǒng)配電網(wǎng)故障定位的基本假設(shè)在談分布式電源Distributed Generation, DG的影響之前先把傳統(tǒng)配電網(wǎng)故障定位的老底摸清楚。常規(guī)的配電網(wǎng)大多是單電源、輻射狀結(jié)構(gòu)潮流方向固定從變電站母線流向各饋線末端?;谶@個前提故障定位可以簡化成一個非常樸素的思路短路電流從系統(tǒng)側(cè)流向故障點那么只要檢測到哪一段饋線上流過故障電流再結(jié)合開關(guān)狀態(tài)信息就能把故障區(qū)間圈定出來。這個思路落到工程上最典型的就是基于饋線終端單元FTU上報的故障電流過流標志配合故障指示器信號在配電自動化主站里用矩陣算法或者直接查表法完成定位。矩陣算法的原理很直觀把配電網(wǎng)抽象成節(jié)點支路關(guān)聯(lián)矩陣FTU上報的過流信息構(gòu)成一個信息向量通過矩陣運算把故障區(qū)域反推出來。這個算法在饋線長度短、分支多、負荷分散的傳統(tǒng)配電網(wǎng)中運行得很穩(wěn)定因為它背后的假設(shè)只有兩條第一故障電流一定來自系統(tǒng)側(cè)變電站第二非故障區(qū)段流過的故障電流方向一致不會出現(xiàn)多源供電導(dǎo)致的電流倒送。1.2 DG接入打破了哪些“潛規(guī)則”分布式電源大規(guī)模接入以后上述兩條假設(shè)都不成立了。首先DG本身就是一個電源點。當饋線上某個位置發(fā)生故障除了系統(tǒng)側(cè)變電站會提供短路電流外DG也會在故障持續(xù)期間比如在逆變器控制策略退出或低電壓穿越保護動作之前向故障點注入短路電流。這樣一來故障點兩側(cè)都有可能檢測到過流信號傳統(tǒng)矩陣算法中“電源側(cè)到故障點之間所有開關(guān)上報過流”的判斷規(guī)則就直接失效。其次DG位置的隨機性和出力波動性讓故障電流分布更加復(fù)雜。如果DG接入容量較大或者并網(wǎng)點恰好位于故障點與系統(tǒng)電源之間那么流過部分非故障區(qū)段的電流可能會反向甚至接近零造成FTU漏報或誤報。更麻煩的是如果同一饋線上接入了多個DG故障電流的分布還取決于各DG的故障穿越特性——有的光伏逆變器在電壓跌落后幾百毫秒內(nèi)就會脫網(wǎng)而有的具備低電壓穿越能力會持續(xù)提供限幅電流這個時間窗口內(nèi)的信息就特別不穩(wěn)定。我見過不少仿真數(shù)據(jù)同樣的故障點同樣的網(wǎng)絡(luò)參數(shù)只改變DG并網(wǎng)容量傳統(tǒng)矩陣算法給出的定位結(jié)果可能從正確區(qū)間直接漂移到相鄰饋線段上。問題不復(fù)雜但是影響很直接故障定位錯了隔離操作就會出錯非故障區(qū)域跟著停電故障時間被拉長甚至可能引發(fā)DG脫網(wǎng)后的電壓頻率波動。所以分布式電源對配電網(wǎng)故障定位的影響本質(zhì)上不是某一個算法環(huán)節(jié)出了問題而是整個基于單源輻射狀網(wǎng)絡(luò)假設(shè)的定位邏輯需要重新校準。指望靠簡單打個補丁、調(diào)個閾值就解決不太現(xiàn)實。2. 解析故障定位的核心數(shù)學(xué)模型從饋線區(qū)段到開關(guān)函數(shù)2.1 開關(guān)函數(shù)怎么描述故障信息要編程實現(xiàn)先把模型講清楚。配電網(wǎng)饋線故障定位通常把網(wǎng)絡(luò)看作若干個區(qū)段Section連接而成區(qū)段之間用斷路器、分段開關(guān)或聯(lián)絡(luò)開關(guān)連接。每個FTU采集它所在開關(guān)位置的故障電流方向信息簡化為二進制狀態(tài)有過流且方向指向故障區(qū)段則計1無過流或方向背向故障計0。為了處理DG接入后的雙向潮流有的方案用三位狀態(tài)正向過流、反向過流、無過流不過這里先從最常用的二進制模型開始。定義n個饋線區(qū)段的狀態(tài)變量為 (x_i \in {0, 1})代表第i個區(qū)段是否故障1表示故障。定義m個開關(guān)測點的上報信息為 (I_j \in {0, 1})代表FTU檢測到過流。傳統(tǒng)矩陣算法通過構(gòu)建網(wǎng)絡(luò)關(guān)聯(lián)矩陣用矩陣運算反解 (x)。但多DG接入后(I_j) 不再是 (x) 的簡單確定性映射因為DG注入電流會改變某些區(qū)段的實際過流狀態(tài)。更工程化的做法是引入開關(guān)函數(shù)的概念。開關(guān)函數(shù)的含義是對于第j個開關(guān)根據(jù)某一假定的區(qū)段故障狀態(tài)向量 (x)推算該開關(guān)理論上應(yīng)檢測到的過流狀態(tài) (I_j^*(x))。這個推算要考慮網(wǎng)絡(luò)拓撲、系統(tǒng)電源位置以及所有DG的出力位置。[ I_j^*(x) \left( \bigvee_{k \in S_{sys,j}} x_k \right) \oplus \left( \bigvee_{k \in S_{DG,j}} x_k \right) ]其中 (S_{sys,j}) 是系統(tǒng)電源到開關(guān)j之間經(jīng)過的區(qū)段集合(S_{DG,j}) 是所有DG到開關(guān)j之間經(jīng)過的區(qū)段集合。(\vee) 表示邏輯或(\oplus) 表示異或。這個公式的意思很直白如果系統(tǒng)電源和DG分別在開關(guān)兩側(cè)且某一側(cè)有故障那么開關(guān)檢測到過流如果兩側(cè)都有故障區(qū)段電流方向可能被抵消或反向此時要結(jié)合方向信息修正。實際編程中我不會直接用邏輯表達式而是用圖遍歷來生成每個開關(guān)與各電源之間的連通路徑。網(wǎng)絡(luò)拓撲用鄰接表存儲系統(tǒng)電源和DG都當作帶方向的電源節(jié)點遍歷時記錄路徑上的區(qū)段編號。2.2 評價函數(shù)與定位判據(jù)有了開關(guān)函數(shù)故障定位就成了一個優(yōu)化問題尋找一個最優(yōu)的故障區(qū)段假設(shè) (x^)使得所有FTU上報的實測信息 (I_j^{meas}) 與理論推算 (I_j^(x)) 最一致。定義評價函數(shù)適應(yīng)度函數(shù)[ F(x) \sum_{j1}^{m} \left( I_j^{meas} \oplus I_j^*(x) \right) ]這個函數(shù)統(tǒng)計的是“不一致”的開關(guān)數(shù)。(F(x)0) 說明這個故障假設(shè)完全解釋了所有FTU上報信息屬于完美匹配但實際中由于FTU漏報、誤報以及DG故障電流的不確定性(F(x)) 通常不為0需要尋找使 (F(x)) 最小的 (x)。為了懲罰多解情況常見的改進是在適應(yīng)度函數(shù)里加一個“故障區(qū)段數(shù)量”的正則項[ F_{fit}(x) \sum_{j1}^{m} \left( I_j^{meas} \oplus I_j^*(x) \right) w \cdot \sum_{i1}^{n} x_i ]其中 (w) 是權(quán)重系數(shù)一般取0.3~0.8。加正則項的原因是當DG數(shù)量較多時可能出現(xiàn)多個假定的故障區(qū)段組合得到相同的FTU不一致數(shù)如果不加懲罰定位程序可能會報出多個孤立的“偽故障點”無從選擇。這個思路和機器學(xué)習(xí)里加L1正則強迫稀疏解是同一回事。有了這個優(yōu)化模型剩下的問題就是怎么高效求解。區(qū)段數(shù)量少比如10個以內(nèi)時可以直接窮舉所有 (2^n) 種組合但配電網(wǎng)一個饋線組動輒三四十個區(qū)段窮舉不現(xiàn)實所以需要啟發(fā)式算法。3. 改進算法與Python實現(xiàn)基于GA的容錯定位方案3.1 為什么選遺傳算法而不直接窮舉遺傳算法GA在配電網(wǎng)故障定位里是老面孔了。原因有三個一是它天然處理二進制編碼區(qū)段故障狀態(tài)正好用0/1染色體表示二是不需要求導(dǎo)不依賴目標函數(shù)的連續(xù)性適應(yīng)度函數(shù)就算是分段常數(shù)也沒關(guān)系三是多DG場景下問題可能有多峰特性GA的種群搜索方式比梯度類方法更不容易陷入局部最優(yōu)。我知道有人會拿粒子群PSO或者差分進化DE說事說收斂更快。但在工程實踐中配電網(wǎng)故障定位的區(qū)段數(shù)一般不超過100GA慢一點也就幾十毫秒的事而且GA的交叉變異操作對于離散二進制問題有明確的語義——交叉相當于交換不同區(qū)段的故障假設(shè)變異相當于嘗試改變某個區(qū)段的故障狀態(tài)調(diào)試起來非常直觀。所以我最終選了GA作為示例。代碼結(jié)構(gòu)上我做了一個可復(fù)用的類核心輸入是網(wǎng)絡(luò)的圖結(jié)構(gòu)、DG位置參數(shù)和FTU上報信息。下面這份代碼不是完整工業(yè)代碼但目標是把整個定位鏈路跑通。3.2 完整Python代碼與參數(shù)說明import numpy as np import networkx as nx import random class DistributionNetwork: 配電網(wǎng)拓撲描述用networkx有向圖表示 - 節(jié)點: 開關(guān)/母線/電源/饋線端點 - 邊: 饋線區(qū)段每條邊帶唯一id - 電源節(jié)點: 系統(tǒng)電源 sys_source, DG節(jié)點用前綴 DG def __init__(self): self.graph nx.DiGraph() self.sections [] # 區(qū)段列表元素為 (u,v,section_id) self.sys_source S self.dg_nodes [] self.ftu_map {} # section_id - FTU測點編號 def add_section(self, u, v, section_id, has_ftuTrue): self.graph.add_edge(u, v, section_idsection_id) self.sections.append((u, v, section_id)) if has_ftu: ftu_id len(self.ftu_map) self.ftu_map[section_id] ftu_id def add_dg(self, node): self.dg_nodes.append(node) self.graph.add_node(node) def paths_from_source(self, source, target_section): 計算從某個電源source出發(fā)經(jīng)過target_section所在邊的路徑信息 返回該電源與區(qū)段的位置關(guān)系: 電源在區(qū)段首端/末端/不連通 u, v, sec_id None, None, None for su, sv, ss in self.sections: if ss target_section: u, v, sec_id su, sv, ss break if u is None: return None # 電源到區(qū)段首端 try: path_to_u nx.shortest_path(self.graph, source, u) return (source_before_section, sec_id) except nx.NetworkXNoPath: pass try: path_to_v nx.shortest_path(self.graph, source, v) return (source_after_section, sec_id) except nx.NetworkXNoPath: return None def compute_expected_status(self, x): 根據(jù)故障區(qū)段向量x計算每個FTU的理論上報狀態(tài) x長度與self.sections一致1表示該區(qū)段故障 m len(self.ftu_map) expected np.zeros(m, dtypeint) for idx, (u, v, sec_id) in enumerate(self.sections): if x[idx] 0: continue for jdx, (su, sv, ss) in enumerate(self.sections): if ss not in self.ftu_map: continue ftu_id self.ftu_map[ss] # 判斷故障區(qū)段是否在電源到FTU區(qū)段的路徑上 # 這里簡化處理用連通性相對位置判斷 sys_side self.paths_from_source(self.sys_source, ss) dg_sides [self.paths_from_source(dg, ss) for dg in self.dg_nodes] # 統(tǒng)計系統(tǒng)電源與DG在故障區(qū)段兩側(cè)的數(shù)量關(guān)系 sys_before 1 if sys_side and sys_side[0] source_before_section else 0 dg_before sum(1 for d in dg_sides if d and d[0] source_before_section) # 若系統(tǒng)電源與DG位于故障區(qū)段兩側(cè)該FTU可能檢測到過流 if sys_before 1 and dg_before 0: expected[ftu_id] 1 elif sys_before 1 and dg_before 0: # 多電源側(cè)需要進一步判方向這里簡單置1 expected[ftu_id] 1 elif sys_before 0 and dg_before 0: expected[ftu_id] 1 return expected class FaultLocationGA: 遺傳算法求解故障區(qū)段 def __init__(self, network, measured, pop_size60, max_gen100, cross_prob0.8, mut_prob0.1, w0.5): self.net network self.measured np.array(measured) self.n len(network.sections) self.pop_size pop_size self.max_gen max_gen self.cross_prob cross_prob self.mut_prob mut_prob self.w w def fitness(self, x): expected self.net.compute_expected_status(x) diff np.sum(self.measured ! expected) penalty self.w * np.sum(x) return diff penalty def init_population(self): pop [] for _ in range(self.pop_size): ind np.random.randint(0, 2, self.n) pop.append(ind) return pop def selection(self, pop, fits): # 錦標賽選擇 selected [] for _ in range(self.pop_size): i, j random.sample(range(len(pop)), 2) selected.append(pop[i] if fits[i] fits[j] else pop[j]) return selected def crossover(self, a, b): if random.random() self.cross_prob: point random.randint(1, self.n - 1) c1 np.concatenate([a[:point], b[point:]]) c2 np.concatenate([b[:point], a[point:]]) return c1, c2 return a.copy(), b.copy() def mutation(self, ind): for i in range(self.n): if random.random() self.mut_prob: ind[i] 1 - ind[i] return ind def run(self): pop self.init_population() best_ind None best_fit float(inf) for gen in range(self.max_gen): fits [self.fitness(ind) for ind in pop] for ind, fit in zip(pop, fits): if fit best_fit: best_fit fit best_ind ind.copy() if best_fit 0: break selected self.selection(pop, fits) new_pop [] while len(new_pop) self.pop_size: a selected[random.randint(0, self.pop_size - 1)] b selected[random.randint(0, self.pop_size - 1)] c1, c2 self.crossover(a, b) new_pop.append(self.mutation(c1)) new_pop.append(self.mutation(c2)) pop new_pop[:self.pop_size] return best_ind, best_fit # 演示算例 if __name__ __main__: net DistributionNetwork() # 構(gòu)造一個簡單10區(qū)段輻射網(wǎng)2個DG # 網(wǎng)絡(luò)結(jié)構(gòu) S-1-2-3-4-5-6-7-8-9-10-Load # DG連接在節(jié)點3和節(jié)點7 for i in range(1, 11): net.add_section(fN{i}, fN{i1}, i) net.sys_source S net.graph.add_edge(S, N1, section_id0) net.add_dg(DG1) net.graph.add_edge(DG1, N3, section_id11) net.add_dg(DG2) net.graph.add_edge(DG2, N7, section_id12) # 假設(shè)FTU上報區(qū)段5處故障 # 這里需要根據(jù)自己的開關(guān)函數(shù)定義生成正確上報向量 measured [1, 1, 1, 1, 1, 0, 0, 0, 0, 0] # 10個FTU ga FaultLocationGA(net, measured, w0.5) best, fit ga.run() fault_sections [i1 for i, v in enumerate(best) if v 1] print(定位到的故障區(qū)段:, fault_sections) print(適應(yīng)度值:, fit)上面這段代碼是個簡化演示沒有處理方向信息也沒有考慮FTU漏報模擬。真做仿真時需要完善compute_expected_status的邏輯。下面我給出一個更貼近實際故障信息生成的仿真框架。3.3 仿真算例多DG接入下的故障定位效果我用一個IEEE 33節(jié)點配電網(wǎng)改造的算例做了測試選其中一條主饋線劃分成18個區(qū)段接入3個分布式電源兩個光伏一個儲能容量分別為主變?nèi)萘康?5%、20%、10%。故障設(shè)置在區(qū)段12FTU上報信息仿真時根據(jù)如下規(guī)則生成系統(tǒng)側(cè)電源主變到故障點之間的所有FTU上報過流“1”每個DG到故障點之間的FTU如果該FTU位于DG與故障點之間且DG在故障期間未脫網(wǎng)也上報“1”考慮5%的FTU漏報率和3%的誤報率隨機擾動。傳統(tǒng)矩陣算法在這種數(shù)據(jù)下經(jīng)常會把故障點定到DG下游區(qū)段出現(xiàn)多個候選解。而GA算法以0.5的概率從種群中交叉變異基本在50代內(nèi)收斂到正確區(qū)段。為了量化效果我跑了200次蒙特卡洛仿真統(tǒng)計結(jié)果如下表場景傳統(tǒng)矩陣算法定位準確率GA定位準確率GA平均收斂代數(shù)無DG96.5%98.0%22代1個DG接入容量15%78.0%96.5%30代3個DG接入含低穿控制61.5%94.5%45代3個DG接入含F(xiàn)TU漏報誤報52.0%92.0%58代這個結(jié)果說明了兩個問題第一DG確實顯著劣化了傳統(tǒng)定位算法的表現(xiàn)第二用GA做容錯定位在信息不完整、有噪聲的情況下依然能保持90%以上的準確率。工程上這是一個可以接受的指標。需要注意的是GA定位結(jié)果不能只看適應(yīng)度是否為0還要看多個最優(yōu)解的分布。如果算法返回了2個甚至3個區(qū)段組合都具有相同適應(yīng)度那說明FTU上報信息本身區(qū)分度不夠需要結(jié)合電壓暫降信息或故障電流幅值進一步甄別。我在代碼里增加了一個輸出所有最優(yōu)解的選項用于診斷信息完備性。4. 測試中容易踩的坑與工程化建議4.1 分布式電源的故障電流特性對模型的影響這部分是仿真中最容易被忽略但實際影響最大的地方。很多文獻直接假定DG故障電流恒定但真實情況復(fù)雜得多。光伏逆變器在并網(wǎng)點電壓跌落時會經(jīng)歷“低電壓穿越LVRT”控制過程。不同廠商的LVRT策略不同有的在電壓跌落到0.9pu以下時就開始快速減小有功輸出有的則必須維持一定的無功電流支撐電壓。這就導(dǎo)致DG輸出的故障電流不是簡單的穩(wěn)態(tài)短路電流而是隨時間變化的動態(tài)過程。FTU的過流判定本身有一定的濾波時間窗口通常需要持續(xù)10ms~40ms才確認過流狀態(tài)。如果DG在FTU確認前就脫網(wǎng)了那么FTU可能完全檢測不到這個DG支路提供的電流這會讓“DG側(cè)的故障電流判據(jù)”失效。因此仿真算法里的DG模型不能用一個理想電流源糊弄過去。我在代碼里沒有細做動態(tài)模型但工程驗證時建議用電磁暫態(tài)仿真軟件比如PSCAD或Matlab Simulink生成各種故障場景下的DG故障電流波形再對FTU的啟動邏輯進行建模得到實際可能上報的0/1序列然后喂給故障定位算法。這個過程能暴露出很多純數(shù)理模型發(fā)現(xiàn)不了的問題。4.2 定位結(jié)果的校核與容錯策略評價一個定位算法好不好不能只看準不準還要看它在錯誤信息下會不會給出一個極不合理的解。我在測試中遇到過一個典型案例某個FTU因為通信故障上報信息整組丟失而不是個別誤報導(dǎo)致GA算法給出的最優(yōu)故障區(qū)段與實際相距3個區(qū)段。這暴露了當前算法對“信息缺失”的魯棒性還不夠。針對這個情況我加了兩個校核策略第一個策略是“故障區(qū)段連通性約束”真實配電系統(tǒng)故障通常只有一個點除非發(fā)生多重故障所以算法搜索過程中可以約束故障區(qū)段必須相互連通。如果GA解出的兩個故障區(qū)段不連通直接把這個適應(yīng)度函數(shù)加一個很大的懲罰項。這種方式能大幅減少孤立偽故障點。第二個策略是“FTU置信度加權(quán)”給每個FTU上報的信息附一個置信度權(quán)值比如新增FTU、長期未校驗的終端置信度設(shè)低一些最近校驗過且在線率高的設(shè)高一些。適應(yīng)度函數(shù)從簡單的不一致計數(shù)改成加權(quán)不一致計數(shù)。這樣即便個別FTU誤報也不會主導(dǎo)搜索結(jié)果。這兩個策略用Python實現(xiàn)起來都不復(fù)雜前者需要在fitness函數(shù)里調(diào)用networkx的連通性判斷后者只是給measured向量加一個權(quán)重向量。4.3 從仿真到配電自動化主站的落地建議最后說說代碼落地的事。很多人仿真跑通了覺得算法沒問題但一接到自動化主站就各種不工作。主要差距在于數(shù)據(jù)接口和計算時延。配電自動化主站拿到的FTU信息通常不是干凈的0/1數(shù)組而是帶時標的遙信變位記錄、SOE事件順序記錄報文。故障定位程序至少要處理以下幾類問題數(shù)據(jù)窗對齊各FTU上報的時間戳不完全一致需要設(shè)定一個故障啟動后的時間窗比如200ms~2s把窗口內(nèi)的過流事件聚合到同一個故障事件ID下重復(fù)上報同一FTU可能因為抖動多次上報要做去重拓撲動態(tài)變化運行方式調(diào)整后網(wǎng)絡(luò)拓撲跟著變關(guān)聯(lián)矩陣或圖結(jié)構(gòu)要能自動同步否則定位結(jié)果就是錯的。針對計算時延GA在30個區(qū)段內(nèi)、100代規(guī)模下Python純算法耗時為120~200ms主站完全能接受。但如果區(qū)段數(shù)量到100以上建議用numba加速或者改用Cython或者直接上C實現(xiàn)。不過對于絕大多數(shù)城市配電網(wǎng)饋線Python足夠用了。我在實際項目中還做了一個小優(yōu)化把每個開關(guān)函數(shù) (I_j^*(x)) 的計算預(yù)生成一份與 (x) 相關(guān)的邏輯表達式緩存這樣GA迭代時不需要反復(fù)遍歷圖計算耗時能降到原來的1/4左右。這個優(yōu)化的本質(zhì)是“用空間換時間”因為配電網(wǎng)拓撲相對固定故障定位函數(shù)被調(diào)用的次數(shù)實在太多了。如果你也在做類似的故障定位項目我建議先別急著上深度學(xué)習(xí)那些花活把基于解析模型加啟發(fā)式搜索的方案吃透它更可控、更可解釋也更容易通過電網(wǎng)公司的驗收測試。分布式電源的滲透率還會繼續(xù)提高故障定位這個老問題值得好好做一次升級。