改进信号分解与机器学习的三电平逆变器混合驱动故障诊断【附代码】

✅ 博主简介:擅长数据搜集与处理、建模仿真、程序设计、仿真代码、论文写作与指导,毕业论文、期刊论文经验交流。
✅成品或者定制,扫描文章底部微信二维码。
(1) 基于混合逻辑动态模型的三电平逆变器故障残差生成方法
中点箝位型三电平逆变器由于其优越的输出波形质量和较低的开关损耗特性,在新能源发电和电力传动领域获得了广泛应用。然而相比于传统两电平拓扑结构,三电平逆变器每相桥臂包含四个功率开关管和两个箝位二极管,器件数量的增加使得系统的故障概率显著上升。功率开关管的开路故障是逆变器运行过程中最常见的故障类型之一,当开关管发生开路故障时,对应桥臂的电流导通路径被切断,导致输出相电流波形出现缺失或畸变,进而影响负载的正常工作。传统的故障诊断方法通常直接分析输出电流波形的异常特征,但由于不同故障位置和故障组合可能产生相似的电流畸变现象,仅依靠波形特征难以实现故障的精确定位。本研究提出采用混合逻辑动态建模方法来描述三电平逆变器在不同开关状态下的动态行为,该模型能够将离散的开关逻辑与连续的电气动态统一在同一数学框架下进行处理。具体而言,根据三电平逆变器的电路拓扑和调制策略,建立包含开关状态布尔变量、连续状态变量和输出变量的混合逻辑动态方程组。在正常运行状态下,利用该模型根据当前的开关指令和系统状态预测三相输出电流的期望值,然后将模型输出与实际测量的三相电流进行比较得到电流残差信号。正常情况下残差信号应在较小的范围内波动,当某个开关管发生开路故障时,实际电流流通路径与模型预测不符,残差信号会呈现出与故障位置相关的特定模式。通过分析残差信号在不同时间段的幅值、极性和持续时间等特征,构建能够区分各种单管故障和多管故障的诊断决策逻辑。
(2) 基于改进信号分解与机器学习的数据驱动故障诊断方法
尽管基于混合逻辑动态模型的故障诊断方法具有物理意义明确和可解释性强的优点,但其诊断性能依赖于模型参数的准确性,当逆变器运行工况变化或系统参数发生漂移时可能出现误判。为了提高故障诊断系统的鲁棒性和适应性,本研究进一步提出一种基于残差数据的机器学习故障诊断策略,将模型驱动方法生成的残差信号作为数据驱动方法的输入特征来源。首先在多种运行工况下采集正常状态和各类故障状态下的三相电流残差信号,构建覆盖全部故障类型的原始数据集。由于三电平逆变器开关故障导致的电流残差特征相对微弱且混杂在噪声和谐波成分中,直接使用原始残差信号难以取得理想的诊断效果,需要对信号进行预处理以增强故障特征的可分辨性。本研究采用变分模态分解算法对残差信号进行自适应分解,该算法能够将复杂信号分解为若干个频带有限的本征模态分量。考虑到变分模态分解的性能对分解层数和惩罚因子等参数较为敏感,引入改进的麻雀搜索算法对这些参数进行自动寻优。改进策略包括在种群初始化阶段采用混沌映射增加个体分布的多样性,在位置更新阶段引入自适应步长因子平衡全局探索和局部开发能力,在迭代后期采用高斯扰动机制帮助算法跳出局部最优陷阱。利用优化后的变分模态分解提取各模态分量的时频域统计特征构成高维特征向量,最后将特征向量输入到经过梯度优化器算法调参的随机森林分类模型中实现故障类型的自动识别。
(3) 故障诊断实验平台搭建与验证测试
为了验证所提故障诊断策略在实际系统中的有效性,基于半实物仿真技术搭建了三电平逆变器故障诊断实验平台。该平台采用实时仿真器模拟三电平逆变器的功率电路和负载系统,控制算法和故障诊断程序运行在快速原型控制器上,两者之间通过高速数字接口进行信号交互。相比于纯软件仿真,半实物仿真平台能够更真实地反映实际系统中的信号延时、测量噪声和离散化误差等因素对诊断算法的影响。实验中首先配置逆变器在额定工况下稳定运行,然后通过修改开关驱动信号的方式人为注入各种单管开路故障和双管开路故障,记录故障发生前后的三相电流波形和诊断系统的响应。
import numpy as np
from scipy.signal import hilbert, find_peaks
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
class ThreeLevelInverterModel:
def __init__(self, Vdc=600, Lf=3e-3, Rf=0.1, Cf=100e-6):
self.Vdc = Vdc
self.Lf = Lf
self.Rf = Rf
self.Cf = Cf
self.dt = 1e-5
def get_switching_states(self, modulation_index, angle):
ref_a = modulation_index * np.sin(angle)
ref_b = modulation_index * np.sin(angle - 2*np.pi/3)
ref_c = modulation_index * np.sin(angle + 2*np.pi/3)
state_a = 1 if ref_a > 0.5 else (-1 if ref_a < -0.5 else 0)
state_b = 1 if ref_b > 0.5 else (-1 if ref_b < -0.5 else 0)
state_c = 1 if ref_c > 0.5 else (-1 if ref_c < -0.5 else 0)
return state_a, state_b, state_c
def compute_phase_voltage(self, states):
sa, sb, sc = states
voltage_levels = {1: self.Vdc/2, 0: 0, -1: -self.Vdc/2}
va = voltage_levels[sa]
vb = voltage_levels[sb]
vc = voltage_levels[sc]
vcm = (va + vb + vc) / 3
return va - vcm, vb - vcm, vc - vcm
def simulate_current(self, duration, modulation_index=0.8, frequency=50):
steps = int(duration / self.dt)
currents = np.zeros((steps, 3))
i_abc = np.zeros(3)
for k in range(steps):
t = k * self.dt
angle = 2 * np.pi * frequency * t
states = self.get_switching_states(modulation_index, angle)
voltages = self.compute_phase_voltage(states)
for phase in range(3):
di = (voltages[phase] - self.Rf * i_abc[phase]) / self.Lf * self.dt
i_abc[phase] += di
currents[k] = i_abc.copy()
return currents
class MLDFaultDiagnoser:
def __init__(self, inverter_model):
self.model = inverter_model
self.fault_codes = self._init_fault_codes()
def _init_fault_codes(self):
return {
0: 'Normal', 1: 'S1_Open', 2: 'S2_Open', 3: 'S3_Open', 4: 'S4_Open',
5: 'S5_Open', 6: 'S6_Open', 7: 'S7_Open', 8: 'S8_Open',
9: 'S9_Open', 10: 'S10_Open', 11: 'S11_Open', 12: 'S12_Open'
}
def compute_residual(self, measured_current, estimated_current):
return measured_current - estimated_current
def extract_residual_features(self, residual):
features = {}
for phase in range(3):
phase_residual = residual[:, phase]
features[f'phase_{phase}_mean'] = np.mean(phase_residual)
features[f'phase_{phase}_std'] = np.std(phase_residual)
features[f'phase_{phase}_max'] = np.max(np.abs(phase_residual))
features[f'phase_{phase}_rms'] = np.sqrt(np.mean(phase_residual**2))
positive_half = phase_residual[phase_residual > 0]
negative_half = phase_residual[phase_residual < 0]
features[f'phase_{phase}_pos_area'] = np.sum(positive_half) if len(positive_half) > 0 else 0
features[f'phase_{phase}_neg_area'] = np.abs(np.sum(negative_half)) if len(negative_half) > 0 else 0
return features
def construct_fault_indicator(self, residual_features):
indicators = np.zeros(3)
for phase in range(3):
pos_area = residual_features[f'phase_{phase}_pos_area']
neg_area = residual_features[f'phase_{phase}_neg_area']
asymmetry = abs(pos_area - neg_area) / (pos_area + neg_area + 1e-10)
amplitude = residual_features[f'phase_{phase}_max']
indicators[phase] = asymmetry * amplitude
return indicators
def diagnose_fault(self, measured_current, estimated_current, threshold=5.0):
residual = self.compute_residual(measured_current, estimated_current)
features = self.extract_residual_features(residual)
indicators = self.construct_fault_indicator(features)
fault_detected = np.any(indicators > threshold)
if not fault_detected:
return 0, 'Normal'
faulty_phase = np.argmax(indicators)
phase_residual = residual[:, faulty_phase]
pos_dominant = np.sum(phase_residual > 0) > np.sum(phase_residual < 0)
fault_code = faulty_phase * 4 + (1 if pos_dominant else 3)
return fault_code, self.fault_codes.get(fault_code, 'Unknown')
class VMDOptimizer:
def __init__(self, signal, K_range=(2, 10), alpha_range=(100, 5000)):
self.signal = signal
self.K_range = K_range
self.alpha_range = alpha_range
def vmd_decompose(self, K, alpha, tau=0, tol=1e-7, max_iter=500):
N = len(self.signal)
t = np.arange(N) / N
freqs = np.fft.fftfreq(N)
f_hat = np.fft.fft(self.signal)
u_hat = np.zeros((K, N), dtype=complex)
omega = np.linspace(0, 0.5, K)
lambda_hat = np.zeros(N, dtype=complex)
for _ in range(max_iter):
u_hat_old = u_hat.copy()
for k in range(K):
sum_uk = np.sum(u_hat, axis=0) - u_hat[k]
numerator = f_hat - sum_uk + lambda_hat / 2
denominator = 1 + 2 * alpha * (freqs - omega[k])**2
u_hat[k] = numerator / denominator
if np.sum(np.abs(u_hat[k])**2) > 0:
omega[k] = np.sum(freqs * np.abs(u_hat[k])**2) / np.sum(np.abs(u_hat[k])**2)
lambda_hat = lambda_hat + tau * (f_hat - np.sum(u_hat, axis=0))
if np.sum(np.abs(u_hat - u_hat_old)**2) / np.sum(np.abs(u_hat_old)**2 + 1e-10) < tol:
break
u = np.real(np.fft.ifft(u_hat, axis=1))
return u, omega
def evaluate_decomposition(self, modes):
reconstruction = np.sum(modes, axis=0)
reconstruction_error = np.sum((self.signal - reconstruction)**2)
correlation_penalty = 0
for i in range(len(modes)):
for j in range(i + 1, len(modes)):
corr = np.abs(np.corrcoef(modes[i], modes[j])[0, 1])
correlation_penalty += corr
return reconstruction_error + 0.5 * correlation_penalty
class ISSAOptimizer:
def __init__(self, objective_func, dim, bounds, pop_size=30, max_iter=100):
self.objective = objective_func
self.dim = dim
self.bounds = bounds
self.pop_size = pop_size
self.max_iter = max_iter
def initialize_population(self):
population = np.zeros((self.pop_size, self.dim))
for i in range(self.dim):
lb, ub = self.bounds[i]
x = np.random.random()
for j in range(self.pop_size):
x = 4 * x * (1 - x)
population[j, i] = lb + x * (ub - lb)
return population
def optimize(self):
population = self.initialize_population()
fitness = np.array([self.objective(ind) for ind in population])
best_idx = np.argmin(fitness)
best_solution = population[best_idx].copy()
best_fitness = fitness[best_idx]
for iteration in range(self.max_iter):
sorted_idx = np.argsort(fitness)
n_producers = int(0.2 * self.pop_size)
n_scouts = int(0.1 * self.pop_size)
alpha = 1 - iteration / self.max_iter
for i in sorted_idx[:n_producers]:
if np.random.random() < 0.8:
step = alpha * np.random.randn(self.dim)
population[i] = population[i] + step * (best_solution - population[i])
else:
population[i] = population[i] + np.random.randn(self.dim)
for i in sorted_idx[n_producers:-n_scouts]:
if i > self.pop_size // 2:
Q = np.random.randn(self.dim)
population[i] = Q * np.exp((population[sorted_idx[-1]] - population[i]) / (i**2 + 1e-10))
else:
A = np.random.randint(0, 2, self.dim) * 2 - 1
population[i] = best_solution + np.abs(population[i] - best_solution) * A
for i in sorted_idx[-n_scouts:]:
if fitness[i] > best_fitness:
population[i] = best_solution + np.random.randn(self.dim) * np.abs(population[i] - best_solution)
else:
sigma = np.exp((fitness[i] - best_fitness) / (np.abs(best_fitness) + 1e-10))
population[i] = population[i] + np.random.randn(self.dim) * sigma
for i in range(self.pop_size):
for j in range(self.dim):
population[i, j] = np.clip(population[i, j], self.bounds[j][0], self.bounds[j][1])
fitness = np.array([self.objective(ind) for ind in population])
current_best_idx = np.argmin(fitness)
if fitness[current_best_idx] < best_fitness:
best_fitness = fitness[current_best_idx]
best_solution = population[current_best_idx].copy()
return best_solution, best_fitness
class GBORandomForest:
def __init__(self, n_estimators_range=(50, 300), max_depth_range=(3, 20)):
self.n_estimators_range = n_estimators_range
self.max_depth_range = max_depth_range
self.best_model = None
def optimize_and_train(self, X, y, n_iterations=50):
best_score = 0
best_params = None
for _ in range(n_iterations):
n_estimators = np.random.randint(self.n_estimators_range[0], self.n_estimators_range[1])
max_depth = np.random.randint(self.max_depth_range[0], self.max_depth_range[1])
model = RandomForestClassifier(n_estimators=n_estimators, max_depth=max_depth, random_state=42)
scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
mean_score = np.mean(scores)
if mean_score > best_score:
best_score = mean_score
best_params = {'n_estimators': n_estimators, 'max_depth': max_depth}
self.best_model = RandomForestClassifier(**best_params, random_state=42)
self.best_model.fit(X, y)
return best_params, best_score
def predict(self, X):
return self.best_model.predict(X)
class InverterFaultDiagnosisSystem:
def __init__(self):
self.inverter = ThreeLevelInverterModel()
self.mld_diagnoser = MLDFaultDiagnoser(self.inverter)
self.scaler = StandardScaler()
self.classifier = GBORandomForest()
def extract_vmd_features(self, residual_signal, K=5, alpha=2000):
all_features = []
for phase in range(3):
vmd = VMDOptimizer(residual_signal[:, phase])
modes, _ = vmd.vmd_decompose(K, alpha)
phase_features = []
for mode in modes:
phase_features.extend([
np.mean(mode), np.std(mode), np.max(np.abs(mode)),
np.sqrt(np.mean(mode**2)),
np.sum(np.abs(np.diff(mode)))
])
all_features.extend(phase_features)
return np.array(all_features)
def build_dataset(self, n_samples_per_class=100):
X, y = [], []
duration = 0.1
for fault_code in range(13):
for _ in range(n_samples_per_class):
estimated = self.inverter.simulate_current(duration)
if fault_code == 0:
measured = estimated + np.random.randn(*estimated.shape) * 0.5
else:
measured = self._inject_fault(estimated, fault_code)
residual = measured - estimated
features = self.extract_vmd_features(residual)
X.append(features)
y.append(fault_code)
return np.array(X), np.array(y)
def _inject_fault(self, current, fault_code):
faulty_current = current.copy()
phase = (fault_code - 1) // 4
fault_type = (fault_code - 1) % 4
if fault_type in [0, 1]:
mask = faulty_current[:, phase] > 0
else:
mask = faulty_current[:, phase] < 0
faulty_current[mask, phase] *= 0.1
return faulty_current
def train(self, X, y):
X_scaled = self.scaler.fit_transform(X)
params, score = self.classifier.optimize_and_train(X_scaled, y)
return params, score
def diagnose(self, measured_current, estimated_current):
residual = measured_current - estimated_current
features = self.extract_vmd_features(residual)
features_scaled = self.scaler.transform(features.reshape(1, -1))
prediction = self.classifier.predict(features_scaled)
return prediction[0], self.mld_diagnoser.fault_codes.get(prediction[0], 'Unknown')
如有问题,可以直接沟通
👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇
更多推荐
所有评论(0)