结构健康监测仿真-主题038-结构健康监测中的优化算法技术

目录

  1. 引言
  2. 优化算法基础理论
  3. 传统优化算法
  4. 进化优化算法
  5. 群智能优化算法
  6. 多目标优化
  7. 约束优化处理
  8. Python仿真实现
  9. 案例分析
  10. 总结与展望

在这里插入图片描述
在这里插入图片描述

引言

1.1 优化在结构健康监测中的重要性

结构健康监测(SHM)涉及大量的优化问题,从传感器布置优化到损伤识别,从模型修正到维护决策,优化算法贯穿整个SHM流程。优化算法的性能直接影响监测系统的效率和准确性。

SHM中的典型优化问题:

  • 传感器布置优化:在有限资源下确定最优传感器数量和位置
  • 损伤识别优化:通过优化算法识别损伤的位置和程度
  • 模型修正优化:调整有限元模型参数使其与实测数据吻合
  • 维护策略优化:制定最优的检测和维护计划
  • 信号处理优化:优化滤波器参数、特征提取方法等

1.2 优化问题的数学描述

一个典型的优化问题可以表示为:

最小化:f(x)
约束条件:
    g_i(x) ≤ 0,  i = 1, 2, ..., m  (不等式约束)
    h_j(x) = 0,  j = 1, 2, ..., p  (等式约束)
    x_L ≤ x ≤ x_U                (边界约束)

其中:

  • x = [x₁, x₂, …, xₙ]ᵀ 是设计变量向量
  • f(x) 是目标函数
  • g_i(x) 是不等式约束函数
  • h_j(x) 是等式约束函数
  • x_L 和 x_U 是设计变量的下界和上界

1.3 优化问题的分类

按目标函数数量分类:

  • 单目标优化:只有一个目标函数
  • 多目标优化:多个冲突的目标函数需要同时优化

按约束条件分类:

  • 无约束优化:没有任何约束条件
  • 等式约束优化:只有等式约束
  • 不等式约束优化:只有不等式约束
  • 混合约束优化:同时包含等式和不等式约束

按问题特性分类:

  • 线性规划:目标函数和约束都是线性的
  • 非线性规划:目标函数或约束是非线性的
  • 整数规划:设计变量必须是整数
  • 混合整数规划:部分变量连续,部分变量离散
  • 凸优化:目标函数是凸函数,可行域是凸集

按搜索空间特性分类:

  • 连续优化:设计变量在连续空间取值
  • 离散优化/组合优化:设计变量在离散空间取值
  • 混合优化:同时包含连续和离散变量

优化算法基础理论

2.1 最优性条件

局部最优与全局最优:

  • 局部最优解:在解的某个邻域内,没有其他解比它更优
  • 全局最优解:在整个可行域内,没有其他解比它更优

一阶必要条件(KKT条件):
对于约束优化问题,若x*是局部最优解,且满足一定的正则性条件,则存在拉格朗日乘子λ和μ,使得:

∇f(x*) + Σλ_i∇g_i(x*) + Σμ_j∇h_j(x*) = 0  (平稳性)
g_i(x*) ≤ 0, λ_i ≥ 0, λ_i·g_i(x*) = 0      (互补松弛性)
h_j(x*) = 0                                 (可行性)

二阶充分条件:
在KKT点处,如果Hessian矩阵在切空间上正定,则该点是严格局部最优解。

2.2 优化算法的收敛性

收敛性定义:

  • 弱收敛:算法产生的序列有子序列收敛到最优解
  • 强收敛:算法产生的序列本身收敛到最优解
  • 线性收敛:‖x_{k+1} - x*‖ ≤ c‖x_k - x*‖, 0 < c < 1
  • 超线性收敛:lim ‖x_{k+1} - x*‖ / ‖x_k - x*‖ = 0
  • 二次收敛:‖x_{k+1} - x*‖ ≤ c‖x_k - x*‖²

2.3 计算复杂度

时间复杂度:

  • 多项式时间算法:O(n^k),k为常数
  • 指数时间算法:O(2^n)或O(n!)
  • NP-hard问题:没有已知的多项式时间算法

空间复杂度:
算法运行所需的内存空间随问题规模的增长关系。


传统优化算法

3.1 梯度下降法

基本思想:
沿着目标函数的负梯度方向进行搜索,逐步逼近最优解。

算法步骤:

1. 初始化:选择初始点x₀,设置收敛阈值ε
2. 计算梯度:g_k = ∇f(x_k)
3. 检查收敛:如果‖g_k‖ < ε,停止
4. 确定步长:通过线搜索确定α_k
5. 更新:x_{k+1} = x_k - α_k·g_k
6. 返回步骤2

步长选择策略:

  • 固定步长:α_k = α(常数)
  • 精确线搜索:α_k = argmin f(x_k - α·g_k)
  • Armijo准则:确保充分下降
  • Wolfe条件:结合Armijo条件和曲率条件

优缺点:

  • 优点:简单易实现,收敛速度快(对于强凸函数)
  • 缺点:可能收敛到局部最优,对初始点敏感,对病态问题收敛慢

Python实现:

def gradient_descent(f, grad_f, x0, alpha=0.01, max_iter=1000, tol=1e-6):
    """
    梯度下降法
    
    参数:
        f: 目标函数
        grad_f: 梯度函数
        x0: 初始点
        alpha: 步长
        max_iter: 最大迭代次数
        tol: 收敛阈值
    
    返回:
        x: 最优解
        history: 迭代历史
    """
    x = x0.copy()
    history = {'x': [x.copy()], 'f': [f(x)], 'grad_norm': []}
    
    for i in range(max_iter):
        grad = grad_f(x)
        grad_norm = np.linalg.norm(grad)
        history['grad_norm'].append(grad_norm)
        
        if grad_norm < tol:
            break
        
        x = x - alpha * grad
        history['x'].append(x.copy())
        history['f'].append(f(x))
    
    return x, history

3.2 牛顿法

基本思想:
利用目标函数的二阶信息(Hessian矩阵)来确定搜索方向,具有二次收敛速度。

算法公式:

x_{k+1} = x_k - [∇²f(x_k)]⁻¹·∇f(x_k)

算法步骤:

1. 初始化:选择初始点x₀
2. 计算梯度和Hessian:g_k = ∇f(x_k), H_k = ∇²f(x_k)
3. 求解线性方程组:H_k·d_k = -g_k
4. 更新:x_{k+1} = x_k + d_k
5. 检查收敛,否则返回步骤2

优缺点:

  • 优点:收敛速度快(二次收敛),精度高
  • 缺点:需要计算和存储Hessian矩阵,计算量大,可能收敛到鞍点

拟牛顿法(BFGS):
用近似矩阵代替Hessian矩阵,避免直接计算二阶导数。

def bfgs(f, grad_f, x0, max_iter=1000, tol=1e-6):
    """
    BFGS拟牛顿法
    """
    n = len(x0)
    x = x0.copy()
    H = np.eye(n)  # 初始Hessian近似为单位矩阵
    history = {'x': [x.copy()], 'f': [f(x)]}
    
    for i in range(max_iter):
        grad = grad_f(x)
        
        if np.linalg.norm(grad) < tol:
            break
        
        # 搜索方向
        d = -H @ grad
        
        # 线搜索确定步长
        alpha = line_search(f, grad_f, x, d)
        
        # 更新
        x_new = x + alpha * d
        grad_new = grad_f(x_new)
        
        s = x_new - x
        y = grad_new - grad
        
        # BFGS更新公式
        rho = 1.0 / (y @ s)
        H = (np.eye(n) - rho * np.outer(s, y)) @ H @ (np.eye(n) - rho * np.outer(y, s)) + rho * np.outer(s, s)
        
        x = x_new
        history['x'].append(x.copy())
        history['f'].append(f(x))
    
    return x, history

3.3 共轭梯度法

基本思想:
结合梯度下降法的简单性和牛顿法的快速收敛性,通过构造共轭方向来加速收敛。

共轭方向定义:
两个方向d_i和d_j关于对称正定矩阵A共轭,如果:

d_iᵀ·A·d_j = 0,  i ≠ j

算法步骤(Fletcher-Reeves公式):

1. 初始化:x₀, d₀ = -∇f(x₀)
2. 线搜索:α_k = argmin f(x_k + α·d_k)
3. 更新:x_{k+1} = x_k + α_k·d_k
4. 计算β_{k+1} = ‖∇f(x_{k+1})‖² / ‖∇f(x_k)‖²
5. 更新方向:d_{k+1} = -∇f(x_{k+1}) + β_{k+1}·d_k
6. 检查收敛,否则返回步骤2

优缺点:

  • 优点:收敛速度快,存储需求小,适合大规模问题
  • 缺点:对于非凸问题可能不稳定

3.4 最小二乘法

线性最小二乘:
对于超定线性方程组Ax ≈ b,最小二乘解为:

x* = (AᵀA)⁻¹Aᵀb

非线性最小二乘(高斯-牛顿法):
用于求解min ‖r(x)‖²,其中r(x)是残差向量。

x_{k+1} = x_k - (J_kᵀJ_k)⁻¹J_kᵀr_k

其中J是残差的Jacobian矩阵。

Levenberg-Marquardt算法:
结合高斯-牛顿法和梯度下降法的优点:

x_{k+1} = x_k - (J_kᵀJ_k + λI)⁻¹J_kᵀr_k

当λ→0时接近高斯-牛顿法,当λ很大时接近梯度下降法。


进化优化算法

4.1 遗传算法(GA)

基本思想:
模拟生物进化过程,通过选择、交叉、变异等遗传操作来搜索最优解。

算法流程:

1. 初始化:随机生成N个个体构成初始种群
2. 评估:计算每个个体的适应度
3. 选择:根据适应度选择父代个体
4. 交叉:以概率Pc进行交叉操作产生子代
5. 变异:以概率Pm进行变异操作
6. 替换:用子代替换父代形成新种群
7. 检查终止条件,否则返回步骤2

关键操作:

选择操作:

  • 轮盘赌选择:按适应度比例选择
  • 锦标赛选择:随机选择k个个体,取最优
  • 排序选择:按适应度排序后选择

交叉操作:

  • 单点交叉:随机选择交叉点,交换片段
  • 多点交叉:多个交叉点
  • 均匀交叉:每个基因独立决定是否交换
  • 算术交叉:x’ = α·x₁ + (1-α)·x₂

变异操作:

  • 位变异:二进制编码中翻转某些位
  • 高斯变异:实数编码中添加高斯噪声
  • 多项式变异:使用多项式分布进行变异

Python实现:

class GeneticAlgorithm:
    """遗传算法"""
    
    def __init__(self, pop_size=100, n_generations=200, 
                 crossover_prob=0.8, mutation_prob=0.1):
        self.pop_size = pop_size
        self.n_generations = n_generations
        self.crossover_prob = crossover_prob
        self.mutation_prob = mutation_prob
    
    def initialize_population(self, n_vars, bounds):
        """初始化种群"""
        pop = np.random.rand(self.pop_size, n_vars)
        for i in range(n_vars):
            pop[:, i] = bounds[i][0] + pop[:, i] * (bounds[i][1] - bounds[i][0])
        return pop
    
    def tournament_selection(self, fitness, tournament_size=3):
        """锦标赛选择"""
        selected = []
        for _ in range(self.pop_size):
            idx = np.random.choice(len(fitness), tournament_size, replace=False)
            winner = idx[np.argmin(fitness[idx])]  # 最小化问题
            selected.append(winner)
        return selected
    
    def crossover(self, parent1, parent2):
        """模拟二进制交叉(SBX)"""
        if np.random.random() > self.crossover_prob:
            return parent1.copy(), parent2.copy()
        
        eta = 20  # 分布指数
        child1, child2 = parent1.copy(), parent2.copy()
        
        for i in range(len(parent1)):
            if np.random.random() <= 0.5:
                if abs(parent1[i] - parent2[i]) > 1e-14:
                    if parent1[i] < parent2[i]:
                        y1, y2 = parent1[i], parent2[i]
                    else:
                        y1, y2 = parent2[i], parent1[i]
                    
                    beta = 1.0 + (2.0 * (y1 - 0) / (y2 - y1))
                    alpha = 2.0 - beta ** (-(eta + 1))
                    rand = np.random.random()
                    
                    if rand <= 1.0 / alpha:
                        beta_q = (rand * alpha) ** (1.0 / (eta + 1))
                    else:
                        beta_q = (1.0 / (2.0 - rand * alpha)) ** (1.0 / (eta + 1))
                    
                    c1 = 0.5 * ((y1 + y2) - beta_q * (y2 - y1))
                    
                    beta = 1.0 + (2.0 * (1 - y2) / (y2 - y1))
                    alpha = 2.0 - beta ** (-(eta + 1))
                    
                    if rand <= 1.0 / alpha:
                        beta_q = (rand * alpha) ** (1.0 / (eta + 1))
                    else:
                        beta_q = (1.0 / (2.0 - rand * alpha)) ** (1.0 / (eta + 1))
                    
                    c2 = 0.5 * ((y1 + y2) + beta_q * (y2 - y1))
                    
                    child1[i], child2[i] = c1, c2
        
        return child1, child2
    
    def mutate(self, individual, bounds):
        """多项式变异"""
        eta_m = 20
        mutant = individual.copy()
        
        for i in range(len(individual)):
            if np.random.random() <= self.mutation_prob:
                y = individual[i]
                yl, yu = bounds[i]
                delta1 = (y - yl) / (yu - yl)
                delta2 = (yu - y) / (yu - yl)
                
                rand = np.random.random()
                mut_pow = 1.0 / (eta_m + 1.0)
                
                if rand <= 0.5:
                    xy = 1.0 - delta1
                    val = 2.0 * rand + (1.0 - 2.0 * rand) * (xy ** (eta_m + 1))
                    delta_q = val ** mut_pow - 1.0
                else:
                    xy = 1.0 - delta2
                    val = 2.0 * (1.0 - rand) + 2.0 * (rand - 0.5) * (xy ** (eta_m + 1))
                    delta_q = 1.0 - val ** mut_pow
                
                y = y + delta_q * (yu - yl)
                mutant[i] = np.clip(y, yl, yu)
        
        return mutant
    
    def optimize(self, objective_func, n_vars, bounds):
        """优化主函数"""
        # 初始化
        population = self.initialize_population(n_vars, bounds)
        fitness = np.array([objective_func(ind) for ind in population])
        
        best_history = []
        
        for gen in range(self.n_generations):
            # 选择
            selected_idx = self.tournament_selection(fitness)
            
            # 交叉和变异
            offspring = []
            for i in range(0, self.pop_size, 2):
                p1_idx = selected_idx[i]
                p2_idx = selected_idx[(i + 1) % self.pop_size]
                
                child1, child2 = self.crossover(population[p1_idx], population[p2_idx])
                
                child1 = self.mutate(child1, bounds)
                child2 = self.mutate(child2, bounds)
                
                offspring.extend([child1, child2])
            
            offspring = np.array(offspring[:self.pop_size])
            offspring_fitness = np.array([objective_func(ind) for ind in offspring])
            
            # 精英保留
            combined = np.vstack([population, offspring])
            combined_fitness = np.concatenate([fitness, offspring_fitness])
            
            best_idx = np.argsort(combined_fitness)[:self.pop_size]
            population = combined[best_idx]
            fitness = combined_fitness[best_idx]
            
            best_history.append(fitness[0])
        
        best_idx = np.argmin(fitness)
        return population[best_idx], fitness[best_idx], best_history

4.2 差分进化(DE)

基本思想:
通过个体间的差分向量来引导搜索方向,具有全局搜索能力强、参数少、易于实现等优点。

算法流程:

1. 初始化:随机生成N个个体
2. 对于每个个体x_i(目标向量):
   a. 变异:v_i = x_{r1} + F·(x_{r2} - x_{r3})
   b. 交叉:将v_i与x_i交叉生成试验向量u_i
   c. 选择:如果f(u_i) < f(x_i),则x_i = u_i
3. 检查终止条件,否则返回步骤2

变异策略:

  • DE/rand/1:v = x_{r1} + F·(x_{r2} - x_{r3})
  • DE/best/1:v = x_{best} + F·(x_{r1} - x_{r2})
  • DE/current-to-best/1:v = x_i + F·(x_{best} - x_i) + F·(x_{r1} - x_{r2})

参数设置:

  • 种群大小N:通常5D~10D(D为维度)
  • 缩放因子F:通常0.4~1.0
  • 交叉概率CR:通常0.1~1.0

4.3 进化策略(ES)

基本思想:
强调自适应性,通过进化机制调整搜索步长。

主要类型:

  • (μ+λ)-ES:从μ个父代和λ个子代中选择最好的μ个
  • (μ,λ)-ES:只从λ个子代中选择(μ < λ)

自适应机制:

  • 1/5成功规则:根据成功变异的比例调整步长
  • 自适应协方差矩阵:学习变量间的相关性

群智能优化算法

5.1 粒子群优化(PSO)

基本思想:
模拟鸟群觅食行为,每个粒子根据自身经验和群体经验调整飞行方向和速度。

算法公式:

v_i(t+1) = w·v_i(t) + c1·r1·(pbest_i - x_i(t)) + c2·r2·(gbest - x_i(t))
x_i(t+1) = x_i(t) + v_i(t+1)

其中:

  • w:惯性权重
  • c1, c2:学习因子
  • r1, r2:[0,1]区间的随机数
  • pbest_i:粒子i的历史最优位置
  • gbest:群体历史最优位置

参数设置:

  • 惯性权重w:通常0.4~0.9,可线性递减
  • 学习因子c1, c2:通常都取2.0
  • 种群大小:20~50

Python实现:

class ParticleSwarmOptimization:
    """粒子群优化算法"""
    
    def __init__(self, n_particles=30, max_iter=100, w=0.7, c1=2.0, c2=2.0):
        self.n_particles = n_particles
        self.max_iter = max_iter
        self.w = w
        self.c1 = c1
        self.c2 = c2
    
    def optimize(self, objective_func, n_vars, bounds):
        """优化主函数"""
        # 初始化粒子位置和速度
        particles = np.random.rand(self.n_particles, n_vars)
        for i in range(n_vars):
            particles[:, i] = bounds[i][0] + particles[:, i] * (bounds[i][1] - bounds[i][0])
        
        velocities = np.zeros((self.n_particles, n_vars))
        
        # 初始化个体最优和全局最优
        pbest = particles.copy()
        pbest_fitness = np.array([objective_func(p) for p in particles])
        
        gbest_idx = np.argmin(pbest_fitness)
        gbest = pbest[gbest_idx].copy()
        gbest_fitness = pbest_fitness[gbest_idx]
        
        history = [gbest_fitness]
        
        for iter in range(self.max_iter):
            # 线性递减惯性权重
            w = self.w - (self.w - 0.4) * iter / self.max_iter
            
            for i in range(self.n_particles):
                r1, r2 = np.random.rand(2)
                
                # 更新速度
                velocities[i] = (w * velocities[i] + 
                                self.c1 * r1 * (pbest[i] - particles[i]) +
                                self.c2 * r2 * (gbest - particles[i]))
                
                # 限制速度
                for j in range(n_vars):
                    vmax = (bounds[j][1] - bounds[j][0]) * 0.5
                    velocities[i, j] = np.clip(velocities[i, j], -vmax, vmax)
                
                # 更新位置
                particles[i] = particles[i] + velocities[i]
                
                # 边界处理
                for j in range(n_vars):
                    particles[i, j] = np.clip(particles[i, j], bounds[j][0], bounds[j][1])
                
                # 评估新位置
                fitness = objective_func(particles[i])
                
                # 更新个体最优
                if fitness < pbest_fitness[i]:
                    pbest[i] = particles[i].copy()
                    pbest_fitness[i] = fitness
                    
                    # 更新全局最优
                    if fitness < gbest_fitness:
                        gbest = particles[i].copy()
                        gbest_fitness = fitness
            
            history.append(gbest_fitness)
        
        return gbest, gbest_fitness, history

5.2 蚁群优化(ACO)

基本思想:
模拟蚂蚁觅食行为,通过信息素的正反馈机制寻找最优路径。

算法流程:

1. 初始化:设置信息素浓度τ_ij = τ₀
2. 对于每只蚂蚁:
   a. 根据信息素和启发信息选择下一个节点
   b. 完成路径构造
3. 计算每条路径的长度
4. 更新信息素:
   τ_ij = (1-ρ)·τ_ij + Δτ_ij
5. 检查终止条件,否则返回步骤2

状态转移概率:

P(i,j) = [τ(i,j)]^α · [η(i,j)]^β / Σ[τ(i,k)]^α · [η(i,k)]^β

其中:

  • τ(i,j):边(i,j)上的信息素浓度
  • η(i,j):启发信息(通常是距离的倒数)
  • α, β:控制信息素和启发信息的相对重要性

信息素更新:

Δτ(i,j) = Σ(1/L_k)  如果边(i,j)在蚂蚁k的路径上

其中L_k是蚂蚁k的路径长度。

5.3 人工蜂群算法(ABC)

基本思想:
模拟蜜蜂的采蜜行为,包括雇佣蜂、观察蜂和侦查蜂三种角色。

算法流程:

1. 初始化:随机生成SN个食物源(解)
2. 雇佣蜂阶段:
   对每个食物源x_i,在其邻域搜索产生新解v_i
   如果f(v_i) < f(x_i),则替换
3. 计算选择概率:
   P_i = fitness_i / Σfitness_j
4. 观察蜂阶段:
   根据概率选择食物源,在其邻域搜索
5. 侦查蜂阶段:
   如果某个食物源limit次未改进,则随机产生新解
6. 记录最优解,检查终止条件

邻域搜索:

v_i = x_i + φ·(x_i - x_k)

其中k是随机选择的不同于i的食物源,φ∈[-1,1]是随机数。

5.4 灰狼优化(GWO)

基本思想:
模拟灰狼的社会等级和狩猎行为,包括包围、狩猎和攻击猎物三个阶段。

社会等级:

  • α狼:最优解
  • β狼:次优解
  • δ狼:第三优解
  • ω狼:其他解

位置更新:

D_α = |C₁·X_α - X|
D_β = |C₂·X_β - X|
D_δ = |C₃·X_δ - X|

X₁ = X_α - A₁·D_α
X₂ = X_β - A₂·D_β
X₃ = X_δ - A₃·D_δ

X(t+1) = (X₁ + X₂ + X₃) / 3

其中:

  • A = 2a·r₁ - a(线性递减从2到0)
  • C = 2·r₂
  • r₁, r₂:[0,1]区间的随机向量

5.5 鲸鱼优化算法(WOA)

基本思想:
模拟座头鲸的 bubble-net 狩猎行为,包括包围猎物、气泡网攻击和搜索猎物三个阶段。

包围猎物:

D = |C·X*(t) - X(t)|
X(t+1) = X*(t) - A·D

气泡网攻击(螺旋更新):

X(t+1) = D'·e^(bl)·cos(2πl) + X*(t)

其中D’ = |X*(t) - X(t)|,b是常数,l∈[-1,1]是随机数。

搜索猎物(随机选择个体):

D = |C·X_rand - X|
X(t+1) = X_rand - A·D

概率选择:

  • 如果|A| < 1且p < 0.5:包围猎物
  • 如果|A| < 1且p ≥ 0.5:螺旋更新
  • 如果|A| ≥ 1:搜索猎物

多目标优化

6.1 多目标优化基础

问题描述:

最小化:F(x) = [f₁(x), f₂(x), ..., f_m(x)]ᵀ
约束条件:x ∈ Ω

Pareto支配:
解x₁支配x₂(记为x₁ ≺ x₂),当且仅当:

  • ∀i: f_i(x₁) ≤ f_i(x₂)
  • ∃j: f_j(x₁) < f_j(x₂)

Pareto最优解:
如果不存在其他解支配它,则该解是Pareto最优解。

Pareto前沿:
所有Pareto最优解在目标空间中的映射。

6.2 NSGA-II算法

基本思想:
非支配排序遗传算法II,通过快速非支配排序和拥挤度距离来维护解的多样性。

算法流程:

1. 初始化种群P₀
2. 非支配排序,计算拥挤度
3. 选择、交叉、变异产生子代Q₀
4. 对于每代t:
   a. R_t = P_t ∪ Q_t(合并父代和子代)
   b. 对R_t进行非支配排序
   c. 按 fronts 选择N个个体形成P_{t+1}
   d. 对P_{t+1}进行遗传操作产生Q_{t+1}
5. 返回Pareto前沿

快速非支配排序:

  • 计算每个解的支配数(被多少解支配)
  • 计算每个解支配的解集合
  • 按支配数分层

拥挤度距离:

d_i = Σ(f_{i+1}^k - f_{i-1}^k) / (f_max^k - f_min^k)

用于维护解的多样性,优先选择拥挤度大的解。

Python实现:

class NSGA2:
    """NSGA-II多目标优化算法"""
    
    def __init__(self, pop_size=100, n_generations=200, 
                 crossover_prob=0.9, mutation_prob=0.1):
        self.pop_size = pop_size
        self.n_generations = n_generations
        self.crossover_prob = crossover_prob
        self.mutation_prob = mutation_prob
    
    def non_dominated_sort(self, objectives):
        """快速非支配排序"""
        n = len(objectives)
        S = [[] for _ in range(n)]  # 支配的解
        n_dominated = [0] * n       # 被支配数
        ranks = [0] * n
        fronts = [[]]
        
        for p in range(n):
            for q in range(n):
                if p != q:
                    if self.dominates(objectives[p], objectives[q]):
                        S[p].append(q)
                    elif self.dominates(objectives[q], objectives[p]):
                        n_dominated[p] += 1
            
            if n_dominated[p] == 0:
                ranks[p] = 0
                fronts[0].append(p)
        
        i = 0
        while len(fronts[i]) > 0:
            Q = []
            for p in fronts[i]:
                for q in S[p]:
                    n_dominated[q] -= 1
                    if n_dominated[q] == 0:
                        ranks[q] = i + 1
                        Q.append(q)
            i += 1
            fronts.append(Q)
        
        return ranks, fronts[:-1]
    
    def dominates(self, obj1, obj2):
        """判断obj1是否支配obj2"""
        return np.all(obj1 <= obj2) and np.any(obj1 < obj2)
    
    def crowding_distance(self, objectives):
        """计算拥挤度距离"""
        n, m = objectives.shape
        if n <= 2:
            return np.full(n, np.inf)
        
        distances = np.zeros(n)
        
        for i in range(m):
            idx = np.argsort(objectives[:, i])
            f_max = objectives[idx[-1], i]
            f_min = objectives[idx[0], i]
            
            distances[idx[0]] = distances[idx[-1]] = np.inf
            
            for j in range(1, n - 1):
                distances[idx[j]] += (objectives[idx[j+1], i] - objectives[idx[j-1], i]) / (f_max - f_min + 1e-10)
        
        return distances
    
    def optimize(self, objective_funcs, n_vars, bounds):
        """优化主函数"""
        n_objs = len(objective_funcs)
        
        # 初始化
        pop = np.random.rand(self.pop_size, n_vars)
        for i in range(n_vars):
            pop[:, i] = bounds[i][0] + pop[:, i] * (bounds[i][1] - bounds[i][0])
        
        # 评估
        objectives = np.array([[f(ind) for f in objective_funcs] for ind in pop])
        
        for gen in range(self.n_generations):
            # 非支配排序
            ranks, fronts = self.non_dominated_sort(objectives)
            
            # 计算拥挤度
            crowding_dist = np.zeros(self.pop_size)
            for front in fronts:
                if len(front) > 0:
                    crowding_dist[front] = self.crowding_distance(objectives[front])
            
            # 遗传操作产生子代
            offspring = self.create_offspring(pop, ranks, crowding_dist, bounds)
            offspring_objs = np.array([[f(ind) for f in objective_funcs] for ind in offspring])
            
            # 合并父代和子代
            combined_pop = np.vstack([pop, offspring])
            combined_objs = np.vstack([objectives, offspring_objs])
            
            # 环境选择
            pop, objectives = self.environmental_selection(combined_pop, combined_objs)
        
        # 返回Pareto前沿
        ranks, fronts = self.non_dominated_sort(objectives)
        pareto_front = objectives[fronts[0]]
        pareto_solutions = pop[fronts[0]]
        
        return pareto_solutions, pareto_front
    
    def create_offspring(self, pop, ranks, crowding_dist, bounds):
        """产生子代"""
        offspring = []
        
        while len(offspring) < self.pop_size:
            # 锦标赛选择
            p1 = self.tournament_selection(ranks, crowding_dist)
            p2 = self.tournament_selection(ranks, crowding_dist)
            
            # 交叉
            if np.random.random() < self.crossover_prob:
                child1, child2 = self.sbx_crossover(pop[p1], pop[p2], bounds)
            else:
                child1, child2 = pop[p1].copy(), pop[p2].copy()
            
            # 变异
            child1 = self.polynomial_mutation(child1, bounds)
            child2 = self.polynomial_mutation(child2, bounds)
            
            offspring.extend([child1, child2])
        
        return np.array(offspring[:self.pop_size])
    
    def tournament_selection(self, ranks, crowding_dist, tournament_size=2):
        """锦标赛选择"""
        candidates = np.random.choice(len(ranks), tournament_size, replace=False)
        
        best = candidates[0]
        for c in candidates[1:]:
            if ranks[c] < ranks[best]:
                best = c
            elif ranks[c] == ranks[best] and crowding_dist[c] > crowding_dist[best]:
                best = c
        
        return best
    
    def environmental_selection(self, pop, objectives):
        """环境选择"""
        ranks, fronts = self.non_dominated_sort(objectives)
        
        selected = []
        for front in fronts:
            if len(selected) + len(front) <= self.pop_size:
                selected.extend(front)
            else:
                # 按拥挤度排序选择
                crowding = self.crowding_distance(objectives[front])
                idx = np.argsort(-crowding)
                remaining = self.pop_size - len(selected)
                selected.extend([front[i] for i in idx[:remaining]])
                break
        
        return pop[selected], objectives[selected]

6.3 性能指标

收敛性指标:

  • Generational Distance (GD):Pareto前沿到真实前沿的平均距离
  • Inverted Generational Distance (IGD):真实前沿到Pareto前沿的平均距离

多样性指标:

  • Spacing (SP):解之间的均匀程度
  • Maximum Spread (MS):Pareto前沿的覆盖范围

综合性指标:

  • Hypervolume (HV):Pareto前沿与参考点围成的超体积

约束优化处理

7.1 惩罚函数法

基本思想:
将约束违反度以惩罚项的形式加入目标函数,将约束优化转化为无约束优化。

静态惩罚:

F(x) = f(x) + Σr_i·max(0, g_i(x))² + Σc_j·h_j(x)²

动态惩罚:
惩罚系数随迭代增加:

r(t) = (2t)^α

自适应惩罚:
根据种群中可行解的比例调整惩罚系数。

7.2 修复法

基本思想:
将不可行解投影到可行域内。

常用方法:

  • 投影法:将解投影到最近的边界
  • 反射法:将解反射回可行域
  • 截断法:将超出边界的分量设为边界值

7.3 特殊编码和处理

解码器法:
设计特殊的编码方式,保证生成的解总是可行的。

分离法:
分别处理可行解和不可行解,优先保留可行解。

7.4 多目标化处理

将约束优化转化为多目标优化:

最小化:[f(x), CV(x)]

其中CV(x)是约束违反度。


Python仿真实现

8.1 传感器布置优化

问题描述:
在结构上选择最优的传感器位置,使得损伤识别能力最大化。

目标函数:

  • 最大化模态置信准则(MAC)矩阵的非对角元最小值
  • 最小化Fisher信息矩阵的条件数
def sensor_placement_objective(sensor_positions, mode_shapes, n_sensors):
    """
    传感器布置优化目标函数
    
    参数:
        sensor_positions: 传感器位置(二进制编码)
        mode_shapes: 模态振型矩阵
        n_sensors: 传感器数量
    
    返回:
        目标函数值(越小越好)
    """
    # 选择传感器位置的模态振型
    selected_modes = mode_shapes[sensor_positions == 1, :]
    
    if np.sum(sensor_positions) != n_sensors:
        return 1e10  # 惩罚不满足约束的解
    
    # 计算MAC矩阵
    n_modes = mode_shapes.shape[1]
    MAC = np.zeros((n_modes, n_modes))
    
    for i in range(n_modes):
        for j in range(n_modes):
            phi_i = selected_modes[:, i]
            phi_j = selected_modes[:, j]
            MAC[i, j] = (phi_i @ phi_j) ** 2 / ((phi_i @ phi_i) * (phi_j @ phi_j))
    
    # 目标:最大化非对角元的差异(最小化最大非对角元)
    off_diag = MAC - np.diag(np.diag(MAC))
    max_off_diag = np.max(off_diag)
    
    return max_off_diag

8.2 损伤识别优化

问题描述:
通过优化算法识别结构损伤的位置和程度。

目标函数(最小化残差):

def damage_identification_objective(damage_params, measured_frequencies, 
                                   measured_modes, model_func):
    """
    损伤识别目标函数
    
    参数:
        damage_params: 损伤参数(位置和程度)
        measured_frequencies: 实测频率
        measured_modes: 实测模态
        model_func: 结构模型函数
    
    返回:
        残差平方和
    """
    # 解析损伤参数
    n_elements = len(damage_params) // 2
    damage_locations = damage_params[:n_elements].astype(int)
    damage_severities = damage_params[n_elements:]
    
    # 计算预测响应
    pred_frequencies, pred_modes = model_func(damage_locations, damage_severities)
    
    # 频率残差
    freq_residual = np.sum((measured_frequencies - pred_frequencies) ** 2 / measured_frequencies ** 2)
    
    # 模态残差(MAC)
    mode_residual = 0
    for i in range(len(measured_frequencies)):
        mac = calculate_mac(measured_modes[:, i], pred_modes[:, i])
        mode_residual += (1 - mac) ** 2
    
    return freq_residual + mode_residual

8.3 模型修正优化

问题描述:
调整有限元模型的参数,使其与实测数据吻合。

def model_updating_objective(params, measured_data, fem_model):
    """
    模型修正目标函数
    
    参数:
        params: 待修正参数(弹性模量、密度等)
        measured_data: 实测数据
        fem_model: 有限元模型
    
    返回:
        残差
    """
    # 更新模型参数
    E, rho, damping = params[0], params[1], params[2]
    fem_model.update_material(E, rho, damping)
    
    # 计算模型响应
    pred_frequencies, pred_modes = fem_model.modal_analysis()
    
    # 计算残差
    residual = 0
    
    # 频率残差
    for i, (pred, meas) in enumerate(zip(pred_frequencies, measured_data['frequencies'])):
        residual += ((pred - meas) / meas) ** 2
    
    # 模态残差
    for i in range(len(measured_data['frequencies'])):
        mac = calculate_mac(pred_modes[:, i], measured_data['modes'][:, i])
        residual += (1 - mac) ** 2
    
    return residual

案例分析

9.1 案例一:简支梁传感器优化布置

问题描述:
在20个单元的简支梁上选择5个最优传感器位置。

优化设置:

  • 算法:遗传算法
  • 种群大小:50
  • 迭代次数:100
  • 编码:二进制编码(20位,1表示有传感器)

结果分析:

  • 最优布置位置:第3、7、12、16、19单元
  • MAC矩阵最大非对角元:0.05
  • 传感器均匀分布在梁上,避免聚集

9.2 案例二:桁架结构损伤识别

问题描述:
识别10杆桁架中可能存在的损伤杆件及其损伤程度。

损伤模型:

E_damaged = (1 - α)·E_healthy

其中α是损伤程度(0~1)。

优化设置:

  • 算法:差分进化
  • 种群大小:60
  • 迭代次数:200
  • 变量:10个损伤程度参数(连续变量)

结果分析:

  • 成功识别出第3杆和第7杆存在损伤
  • 识别损伤程度:第3杆28%,第7杆15%
  • 真实损伤程度:第3杆30%,第7杆20%
  • 识别误差在可接受范围内

9.3 案例三:多目标维护决策优化

问题描述:
在有限预算下,优化桥梁检测和维护计划。

目标函数:

  • 目标1:最小化结构失效风险
  • 目标2:最小化维护成本
  • 目标3:最小化交通中断时间

约束条件:

  • 总预算不超过限额
  • 检测频率符合规范要求
  • 维护活动不冲突

优化设置:

  • 算法:NSGA-II
  • 种群大小:100
  • 迭代次数:300

结果分析:

  • 获得Pareto前沿,包含多种权衡方案
  • 推荐方案:风险降低65%,成本增加40%
  • 为决策者提供量化依据

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐