用AI写天线阵列排布算法

这里用了元宝的编程功能,我整理了需求,代码由DEEPSEEK完成。

MIMO天线阵列优化

基于粒子群优化算法(PSO)的77GHz MIMO天线阵列布局优化。

项目概述

这个项目使用粒子群优化算法(PSO)来优化77GHz MIMO天线阵列的布局。优化目标是在给定的物理约束下,使阵列的辐射方向图具有4°的半功率波束宽度(HPBW)和尽可能低的副瓣电平(SLL)。

主要特性

  • 工作频率: 77GHz (波长 λ = 3.896mm)

  • 天线配置: 3个发射天线,2个接收天线

  • 物理约束:

    • 天线高度: 3个波长 (11.688mm)

    • 最小间距: 3.5个波长 (13.64mm)

    • 总高度: ≤ 6cm

    • 水平尺寸: ≤ 8mm

  • 优化目标:

    • 半功率波束宽度(HPBW): 接近4°

    • 副瓣电平(SLL): 尽可能低

    • 无天线重叠

      • 充分利用可用高度

详细代码

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
from typing import List, Tuple
import time

class MIMOAntennaArray:
    """
    MIMO天线阵列优化类
    工作频率: 77GHz,波长λ=3.896mm
    天线物理高度: 3个波长(3λ=11.688mm)
    发射天线: 3个,纵向排列在同一列
    接收天线: 2个,纵向排列在同一列
    总高度: ≤6cm
    阵列水平尺寸: ≤8mm
    天线最小间距: 3.5个波长(更新)
    天线间距: 半波长的整数倍
    要求: 天线不能重叠,尽可能分散利用60mm高度
    """
    
    def __init__(self):
        # 物理常数
        self.freq = 77e9  # 77GHz
        self.c = 3e8  # 光速
        self.wavelength = self.c / self.freq  # 波长
        self.half_wavelength = self.wavelength / 2  # 半波长
        
        # 天线物理尺寸
        self.antenna_height = 3 * self.wavelength  # 单个天线高度3个波长
        self.antenna_half_height = self.antenna_height / 2  # 天线半高
        
        # 最小间距:更新为3.5个波长
        self.min_spacing_wavelengths = 3.5  # 最小间距(以波长为单位)
        self.min_spacing_half_wavelengths = int(np.ceil(2 * self.min_spacing_wavelengths))  # 最小间距(以半波长为单位)
        
        # 阵列约束
        self.max_horizontal = 8e-3  # 最大水平尺寸8mm
        self.max_vertical_total = 60e-3  # 最大总高度6cm
        
        # 天线数量(发射3个,接收2个)
        self.num_tx = 3
        self.num_rx = 2
        
        # 方向图计算参数
        self.theta_range = np.linspace(-90, 90, 3601)  # -90°到90°,0.05°间隔
        self.target_hpbw = 4.0  # 目标半功率波束宽度4°
        
    def calculate_array_positions(self, tx_spacings: List[int], rx_spacings: List[int], 
                                  horizontal_spacing: int, tx_start_offset: int = 0, 
                                  rx_start_offset: int = 0) -> Tuple[np.ndarray, np.ndarray, np.ndarray, bool]:
        """
        计算发射、接收和虚拟阵列位置,检查是否重叠
        
        参数:
            tx_spacings: 发射天线间距列表(单位: 半波长),长度应为2
            rx_spacings: 接收天线间距列表(单位: 半波长),长度应为1
            horizontal_spacing: 水平间距(单位: 半波长)
            tx_start_offset: 发射天线起始偏移(单位: 半波长)
            rx_start_offset: 接收天线起始偏移(单位: 半波长)
            
        返回:
            tx_positions: 发射天线位置(水平, 垂直) 单位: 米
            rx_positions: 接收天线位置(水平, 垂直) 单位: 米
            virtual_positions: 虚拟阵列位置(仅垂直) 单位: 米
            no_overlap: 是否无重叠
        """
        # 验证输入
        if len(tx_spacings) != self.num_tx - 1:
            raise ValueError(f"发射天线间距数量不正确,期望{self.num_tx-1}个,得到{len(tx_spacings)}个")
        if len(rx_spacings) != self.num_rx - 1:
            raise ValueError(f"接收天线间距数量不正确,期望{self.num_rx-1}个,得到{len(rx_spacings)}个")
        
        # 检查最小间距约束
        for spacing in tx_spacings:
            if spacing < self.min_spacing_half_wavelengths:
                raise ValueError(f"发射天线间距必须至少为{self.min_spacing_half_wavelengths}个半波长")
        for spacing in rx_spacings:
            if spacing < self.min_spacing_half_wavelengths:
                raise ValueError(f"接收天线间距必须至少为{self.min_spacing_half_wavelengths}个半波长")
        
        # 计算发射天线垂直位置(从tx_start_offset开始)
        tx_vertical = [tx_start_offset * self.half_wavelength]
        current_pos = tx_vertical[0]
        for spacing in tx_spacings:
            current_pos += spacing * self.half_wavelength
            tx_vertical.append(current_pos)
        
        # 计算接收天线垂直位置(从rx_start_offset开始)
        rx_vertical = [rx_start_offset * self.half_wavelength]
        current_pos = rx_vertical[0]
        for spacing in rx_spacings:
            current_pos += spacing * self.half_wavelength
            rx_vertical.append(current_pos)
        
        # 转换为位置数组
        tx_positions = np.zeros((self.num_tx, 2))
        rx_positions = np.zeros((self.num_rx, 2))
        
        # 发射天线: 水平位置为0,垂直位置如上计算
        for i, pos in enumerate(tx_vertical):
            tx_positions[i] = [0, pos]
        
        # 接收天线: 水平位置为horizontal_spacing * 半波长
        horizontal_pos = horizontal_spacing * self.half_wavelength
        for i, pos in enumerate(rx_vertical):
            rx_positions[i] = [horizontal_pos, pos]
        
        # 检查天线是否重叠(考虑物理尺寸)
        no_overlap = self.check_antenna_overlap(tx_positions, rx_positions)
        
        # 计算虚拟阵列位置(发射和接收位置的和)
        virtual_positions = []
        for tx_pos in tx_positions:
            for rx_pos in rx_positions:
                # 虚拟位置 = 发射位置 + 接收位置
                virtual_pos = tx_pos + rx_pos
                virtual_positions.append(virtual_pos[1])  # 只取垂直分量
        
        # 去重并排序
        virtual_positions = np.unique(np.sort(virtual_positions))
        
        return tx_positions, rx_positions, virtual_positions, no_overlap
    
    def check_antenna_overlap(self, tx_positions: np.ndarray, rx_positions: np.ndarray) -> bool:
        """
        检查天线是否重叠(考虑3个波长的物理尺寸)
        
        参数:
            tx_positions: 发射天线位置
            rx_positions: 接收天线位置
            
        返回:
            True表示无重叠,False表示有重叠
        """
        all_positions = np.vstack([tx_positions, rx_positions])
        
        # 检查每对天线
        for i in range(len(all_positions)):
            for j in range(i+1, len(all_positions)):
                pos1 = all_positions[i]
                pos2 = all_positions[j]
                
                # 计算垂直距离
                vertical_distance = abs(pos1[1] - pos2[1])
                
                # 如果垂直距离小于天线高度,则可能重叠
                if vertical_distance < self.antenna_height:
                    # 如果水平位置相同且垂直距离小于天线高度,则肯定重叠
                    if abs(pos1[0] - pos2[0]) < 1e-6:  # 水平位置相同
                        return False
                    # 如果水平位置不同,但垂直距离很小,也可能有部分重叠
                    elif vertical_distance < 0.8 * self.antenna_height:
                        return False
        
        return True
    
    def calculate_array_factor(self, positions: np.ndarray, theta_deg: float) -> complex:
        """
        计算给定角度的阵列因子
        
        参数:
            positions: 天线位置数组(垂直方向,单位: 米)
            theta_deg: 角度(度)
            
        返回:
            阵列因子(复数)
        """
        theta_rad = np.deg2rad(theta_deg)
        k = 2 * np.pi / self.wavelength  # 波数
        
        # 阵列因子 = Σ exp(j * k * d * sinθ)
        af = 0j
        for d in positions:
            af += np.exp(1j * k * d * np.sin(theta_rad))
        
        return af
    
    def calculate_radiation_pattern(self, virtual_positions: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
        """
        计算辐射方向图
        
        参数:
            virtual_positions: 虚拟阵列位置(垂直)
            
        返回:
            theta_range: 角度范围(度)
            pattern_db: 辐射方向图(dB)
        """
        pattern = []
        
        for theta in self.theta_range:
            af = self.calculate_array_factor(virtual_positions, theta)
            pattern.append(np.abs(af))
        
        pattern = np.array(pattern)
        pattern_db = 20 * np.log10(pattern / np.max(pattern))  # 转换为dB
        
        return self.theta_range, pattern_db
    
    def calculate_hpbw_and_sll(self, pattern_db: np.ndarray) -> Tuple[float, float, int, int]:
        """
        计算半功率波束宽度和峰值副瓣电平
        
        参数:
            pattern_db: 辐射方向图(dB)
            
        返回:
            hpbw: 半功率波束宽度(度)
            sll: 峰值副瓣电平(dB) - 主瓣外的最高电平
            left_zero_idx: 主瓣左侧零点索引
            right_zero_idx: 主瓣右侧零点索引
        """
        # 找到主瓣最大值位置
        main_lobe_idx = np.argmax(pattern_db)
        
        # 找到-3dB点
        threshold = pattern_db[main_lobe_idx] - 3
        
        # 向左搜索-3dB点
        left_idx = main_lobe_idx
        while left_idx > 0 and pattern_db[left_idx] > threshold:
            left_idx -= 1
        
        # 向右搜索-3dB点
        right_idx = main_lobe_idx
        while right_idx < len(pattern_db) - 1 and pattern_db[right_idx] > threshold:
            right_idx += 1
        
        # 计算半功率波束宽度
        hpbw = self.theta_range[right_idx] - self.theta_range[left_idx]
        
        # 找到主瓣的第一个零点(寻找主瓣两侧的第一个最小值)
        # 向左搜索第一个最小值
        left_min_idx = main_lobe_idx
        while left_min_idx > 1 and pattern_db[left_min_idx-1] <= pattern_db[left_min_idx]:
            left_min_idx -= 1
        
        # 向右搜索第一个最小值
        right_min_idx = main_lobe_idx
        while right_min_idx < len(pattern_db)-2 and pattern_db[right_min_idx+1] <= pattern_db[right_min_idx]:
            right_min_idx += 1
        
        # 进一步确认零点:从第一个最小值点继续向两侧搜索,直到找到更低的点
        # 向左搜索
        left_zero_idx = left_min_idx
        for i in range(left_min_idx-1, 0, -1):
            if pattern_db[i] < pattern_db[left_zero_idx]:
                left_zero_idx = i
            else:
                break
        
        # 向右搜索
        right_zero_idx = right_min_idx
        for i in range(right_min_idx+1, len(pattern_db)-1):
            if pattern_db[i] < pattern_db[right_zero_idx]:
                right_zero_idx = i
            else:
                break
        
        # 定义主瓣区域为第一个零点之间的区域
        main_lobe_region = np.zeros(len(pattern_db), dtype=bool)
        if left_zero_idx < right_zero_idx:
            main_lobe_region[left_zero_idx:right_zero_idx+1] = True
        
        # 计算副瓣区域(主瓣区域以外的部分)
        side_lobe_pattern = pattern_db[~main_lobe_region]
        
        if len(side_lobe_pattern) > 0:
            sll = np.max(side_lobe_pattern)
        else:
            sll = -100  # 如果没有副瓣
        
        return hpbw, sll, left_zero_idx, right_zero_idx
    
    def fitness_function(self, params: List[int]) -> float:
        """
        适应度函数: 最小化副瓣电平,使HPBW接近4°,鼓励天线分散
        
        参数:
            params: [tx_spacing1, tx_spacing2, rx_spacing1, horizontal_spacing, tx_start_offset, rx_start_offset]
            
        返回:
            适应度值(越小越好)
        """
        # 解析参数
        tx_spacing1, tx_spacing2, rx_spacing1, horizontal_spacing, tx_start_offset, rx_start_offset = params
        
        # 检查最小间距约束
        if (tx_spacing1 < self.min_spacing_half_wavelengths or 
            tx_spacing2 < self.min_spacing_half_wavelengths or
            rx_spacing1 < self.min_spacing_half_wavelengths):
            return 1e6  # 返回很大的惩罚值
        
        # 检查约束条件
        tx_center_to_center = (tx_spacing1 + tx_spacing2) * self.half_wavelength
        rx_center_to_center = rx_spacing1 * self.half_wavelength
        
        # 计算起始位置
        tx_start_pos = tx_start_offset * self.half_wavelength
        rx_start_pos = rx_start_offset * self.half_wavelength
        
        # 计算总高度(从最低点到最高点 + 天线高度)
        tx_min_y = tx_start_pos
        tx_max_y = tx_start_pos + tx_center_to_center
        rx_min_y = rx_start_pos
        rx_max_y = rx_start_pos + rx_center_to_center
        
        min_y = min(tx_min_y, rx_min_y)
        max_y = max(tx_max_y, rx_max_y)
        total_height = (max_y - min_y) + self.antenna_height
        
        # 垂直方向总高度不超过6cm
        if total_height > self.max_vertical_total:
            return 1e6
        
        # 水平方向总长度不超过8mm
        horizontal_total = horizontal_spacing * self.half_wavelength
        if horizontal_total > self.max_horizontal:
            return 1e6
        
        # 计算阵列位置并检查重叠
        tx_spacings = [tx_spacing1, tx_spacing2]
        rx_spacings = [rx_spacing1]
        
        try:
            tx_positions, rx_positions, virtual_positions, no_overlap = self.calculate_array_positions(
                tx_spacings, rx_spacings, horizontal_spacing, tx_start_offset, rx_start_offset
            )
        except ValueError as e:
            return 1e6
        except:
            return 1e6
        
        # 如果有重叠,给予惩罚
        if not no_overlap:
            return 1e5
        
        # 计算辐射方向图
        theta_range, pattern_db = self.calculate_radiation_pattern(virtual_positions)
        
        # 计算HPBW和SLL
        hpbw, sll, _, _ = self.calculate_hpbw_and_sll(pattern_db)
        
        # 计算天线分散度奖励
        height_utilization = total_height / self.max_vertical_total
        dispersion_reward = 10 * (1 - height_utilization)
        
        # 计算阵列孔径
        if len(virtual_positions) > 1:
            array_aperture = virtual_positions[-1] - virtual_positions[0]
            aperture_reward = 20 * (1 - array_aperture / (self.max_vertical_total - self.antenna_height))
        else:
            aperture_reward = 100
        
        # 适应度 = HPBW误差权重 + SLL + 分散度惩罚
        hpbw_error = abs(hpbw - self.target_hpbw)
        
        if hpbw_error > 2.0:
            fitness = 1e3 + hpbw_error * 100 + aperture_reward
        else:
            fitness = abs(sll) + hpbw_error * 10 + dispersion_reward + aperture_reward
        
        return fitness
    
    def plot_results(self, best_params: List[int], save_path: str = None):
        """
        绘制优化结果
        
        参数:
            best_params: 最优参数
            save_path: 保存路径
        """
        # 解析参数
        tx_spacing1, tx_spacing2, rx_spacing1, horizontal_spacing, tx_start_offset, rx_start_offset = best_params
        
        tx_spacings = [tx_spacing1, tx_spacing2]
        rx_spacings = [rx_spacing1]
        
        # 计算阵列位置
        tx_positions, rx_positions, virtual_positions, no_overlap = self.calculate_array_positions(
            tx_spacings, rx_spacings, horizontal_spacing, tx_start_offset, rx_start_offset
        )
        
        # 计算辐射方向图
        theta_range, pattern_db = self.calculate_radiation_pattern(virtual_positions)
        
        # 计算HPBW和SLL
        hpbw, sll, left_zero_idx, right_zero_idx = self.calculate_hpbw_and_sll(pattern_db)
        
        # 计算物理尺寸
        tx_min_y = np.min(tx_positions[:, 1])
        tx_max_y = np.max(tx_positions[:, 1])
        rx_min_y = np.min(rx_positions[:, 1])
        rx_max_y = np.max(rx_positions[:, 1])
        
        min_y = min(tx_min_y, rx_min_y)
        max_y = max(tx_max_y, rx_max_y)
        total_height = (max_y - min_y) + self.antenna_height
        
        # 创建图形
        fig, axes = plt.subplots(2, 2, figsize=(14, 12))
        
        # 1. 阵列布局
        ax1 = axes[0, 0]
        
        # 绘制发射天线
        for i, pos in enumerate(tx_positions):
            ax1.add_patch(plt.Rectangle(
                (pos[0]*1000 - 1, pos[1]*1000 - self.antenna_half_height*1000),
                2, self.antenna_height*1000,
                color='r', alpha=0.5, label='发射天线' if i==0 else None
            ))
            ax1.scatter(pos[0]*1000, pos[1]*1000, s=100, c='r', marker='^', edgecolors='black', linewidth=1)
            ax1.text(pos[0]*1000, pos[1]*1000, f'T{i+1}', ha='center', va='bottom', fontsize=9, fontweight='bold')
        
        # 绘制接收天线
        for i, pos in enumerate(rx_positions):
            ax1.add_patch(plt.Rectangle(
                (pos[0]*1000 - 1, pos[1]*1000 - self.antenna_half_height*1000),
                2, self.antenna_height*1000,
                color='b', alpha=0.5, label='接收天线' if i==0 else None
            ))
            ax1.scatter(pos[0]*1000, pos[1]*1000, s=100, c='b', marker='v', edgecolors='black', linewidth=1)
            ax1.text(pos[0]*1000, pos[1]*1000, f'R{i+1}', ha='center', va='bottom', fontsize=9, fontweight='bold')
        
        # 标记天线间距
        for i in range(len(tx_positions)-1):
            y_mid = (tx_positions[i, 1] + tx_positions[i+1, 1]) / 2 * 1000
            ax1.annotate('', xy=(0, tx_positions[i+1, 1]*1000), xytext=(0, tx_positions[i, 1]*1000),
                        arrowprops=dict(arrowstyle='<->', color='green', lw=1.5))
            ax1.text(1, y_mid, f'{tx_spacings[i]}λ/2', ha='left', va='center', fontsize=8, color='green')
        
        # 接收天线间距
        if len(rx_positions) > 1:
            y_mid = (rx_positions[0, 1] + rx_positions[1, 1]) / 2 * 1000
            ax1.annotate('', xy=(horizontal_spacing*self.half_wavelength*1000, rx_positions[1, 1]*1000),
                        xytext=(horizontal_spacing*self.half_wavelength*1000, rx_positions[0, 1]*1000),
                        arrowprops=dict(arrowstyle='<->', color='green', lw=1.5))
            ax1.text(horizontal_spacing*self.half_wavelength*1000+1, y_mid, f'{rx_spacings[0]}λ/2', 
                    ha='left', va='center', fontsize=8, color='green')
        
        ax1.set_xlabel('水平位置 (mm)')
        ax1.set_ylabel('垂直位置 (mm)')
        ax1.set_title('天线阵列布局(含天线物理尺寸)')
        ax1.legend()
        ax1.grid(True, alpha=0.3)
        ax1.set_aspect('equal')
        ax1.set_xlim(-2, horizontal_spacing * self.half_wavelength * 1000 + 2)
        
        # 2. 虚拟阵列布局
        ax2 = axes[0, 1]
        ax2.scatter([0] * len(virtual_positions), virtual_positions * 1000, 
                   s=80, c='g', marker='o', edgecolors='black', linewidth=1)
        
        # 标记虚拟天线间距
        for i in range(len(virtual_positions)-1):
            y_mid = (virtual_positions[i] + virtual_positions[i+1]) / 2 * 1000
            ax2.annotate('', xy=(0, virtual_positions[i+1]*1000), xytext=(0, virtual_positions[i]*1000),
                        arrowprops=dict(arrowstyle='<->', color='orange', lw=1.5))
            spacing_mm = (virtual_positions[i+1] - virtual_positions[i]) * 1000
            ax2.text(0.5, y_mid, f'{spacing_mm:.1f}mm', ha='left', va='center', fontsize=8, color='orange')
        
        ax2.set_xlabel('水平位置 (mm)')
        ax2.set_ylabel('垂直位置 (mm)')
        ax2.set_title(f'虚拟阵列布局 ({len(virtual_positions)}个虚拟天线)')
        ax2.grid(True, alpha=0.3)
        ax2.set_xlim(-1, 1)
        
        # 3. 辐射方向图
        ax3 = axes[1, 0]
        ax3.plot(theta_range, pattern_db, 'b-', linewidth=2)
        ax3.axhline(y=-3, color='r', linestyle='--', alpha=0.7, label='-3dB')
        
        # 标记HPBW
        main_lobe_idx = np.argmax(pattern_db)
        threshold = pattern_db[main_lobe_idx] - 3
        left_3db_idx = main_lobe_idx
        while left_3db_idx > 0 and pattern_db[left_3db_idx] > threshold:
            left_3db_idx -= 1
        right_3db_idx = main_lobe_idx
        while right_3db_idx < len(pattern_db) - 1 and pattern_db[right_3db_idx] > threshold:
            right_3db_idx += 1
        
        hpbw_start = theta_range[left_3db_idx]
        hpbw_end = theta_range[right_3db_idx]
        ax3.axvline(x=hpbw_start, color='g', linestyle=':', alpha=0.7, label='HPBW边界')
        ax3.axvline(x=hpbw_end, color='g', linestyle=':', alpha=0.7)
        
        # 标记主瓣零点
        left_zero = theta_range[left_zero_idx]
        right_zero = theta_range[right_zero_idx]
        ax3.axvline(x=left_zero, color='orange', linestyle='--', alpha=0.7, label='主瓣零点')
        ax3.axvline(x=right_zero, color='orange', linestyle='--', alpha=0.7)
        
        # 标记SLL区域
        main_lobe_region = np.zeros(len(pattern_db), dtype=bool)
        if left_zero_idx < right_zero_idx:
            main_lobe_region[left_zero_idx:right_zero_idx+1] = True
        side_lobe_pattern = pattern_db[~main_lobe_region]
        
        if len(side_lobe_pattern) > 0:
            sll_value = np.max(side_lobe_pattern)
            sll_idx = np.where(pattern_db == sll_value)[0][0]
            ax3.plot(theta_range[sll_idx], sll_value, 'ro', markersize=8, label=f'SLL: {sll_value:.2f}dB')
        
        ax3.set_xlabel('角度 (度)')
        ax3.set_ylabel('增益 (dB)')
        ax3.set_title(f'辐射方向图\nHPBW: {hpbw:.2f}°, SLL: {sll:.2f}dB')
        ax3.grid(True, alpha=0.3)
        ax3.set_xlim(-30, 30)
        ax3.set_ylim(-50, 5)
        ax3.legend(loc='upper right')
        
        # 4. 参数显示
        ax4 = axes[1, 1]
        ax4.axis('off')
        
        horizontal_total = horizontal_spacing * self.half_wavelength
        
        info_text = (
            f'优化参数:\n'
            f'发射天线间距: [{tx_spacing1}, {tx_spacing2}] × λ/2\n'
            f'接收天线间距: [{rx_spacing1}] × λ/2\n'
            f'水平间距: {horizontal_spacing} × λ/2\n'
            f'发射起始偏移: {tx_start_offset} × λ/2\n'
            f'接收起始偏移: {rx_start_offset} × λ/2\n\n'
            f'物理参数:\n'
            f'波长 λ = {self.wavelength*1000:.3f} mm\n'
            f'半波长 λ/2 = {self.half_wavelength*1000:.3f} mm\n'
            f'天线高度: 3λ = {self.antenna_height*1000:.3f} mm\n'
            f'最小间距: 3.5λ = {self.min_spacing_wavelengths*self.wavelength*1000:.3f} mm\n\n'
            f'物理尺寸:\n'
            f'发射列高度范围: {tx_min_y*1000:.1f} - {tx_max_y*1000:.1f} mm\n'
            f'接收列高度范围: {rx_min_y*1000:.1f} - {rx_max_y*1000:.1f} mm\n'
            f'阵列总高度: {total_height*1000:.1f} mm\n'
            f'水平总宽度: {horizontal_total*1000:.1f} mm\n\n'
            f'性能指标:\n'
            f'目标HPBW: {self.target_hpbw}°\n'
            f'实际HPBW: {hpbw:.2f}°\n'
            f'峰值副瓣(SLL): {sll:.2f} dB\n'
            f'虚拟天线数: {len(virtual_positions)}\n'
            f'阵列孔径: {(virtual_positions[-1] - virtual_positions[0])*1000:.1f} mm\n'
            f'无重叠: {no_overlap}\n'
        )
        
        ax4.text(0.1, 0.95, info_text, fontsize=8, verticalalignment='top',
                bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
        
        plt.tight_layout()
        
        if save_path:
            plt.savefig(save_path, dpi=300, bbox_inches='tight')
        
        plt.show()


class ParticleSwarmOptimizer:
    """粒子群优化算法实现"""
    
    def __init__(self, fitness_func, bounds, num_particles=40, max_iter=100, 
                 w=0.8, c1=1.5, c2=1.5):
        """
        初始化PSO
        
        参数:
            fitness_func: 适应度函数
            bounds: 变量边界 [(min, max), ...]
            num_particles: 粒子数量
            max_iter: 最大迭代次数
            w: 惯性权重
            c1: 个体学习因子
            c2: 社会学习因子
        """
        self.fitness_func = fitness_func
        self.bounds = bounds
        self.num_particles = num_particles
        self.max_iter = max_iter
        self.w = w
        self.c1 = c1
        self.c2 = c2
        
        self.num_dimensions = len(bounds)
        
        # 初始化粒子位置和速度
        self.positions = np.random.randint(
            [b[0] for b in bounds],
            [b[1]+1 for b in bounds],
            size=(num_particles, self.num_dimensions)
        )
        
        self.velocities = np.random.uniform(-1, 1, size=(num_particles, self.num_dimensions))
        
        # 初始化个体最佳
        self.pbest_positions = self.positions.copy()
        self.pbest_scores = np.array([fitness_func(pos) for pos in self.positions])
        
        # 初始化全局最佳
        self.gbest_idx = np.argmin(self.pbest_scores)
        self.gbest_position = self.pbest_positions[self.gbest_idx].copy()
        self.gbest_score = self.pbest_scores[self.gbest_idx]
        
        # 记录历史
        self.history = {
            'gbest_scores': [],
            'avg_scores': [],
            'positions': []
        }
    
    def optimize(self) -> Tuple[np.ndarray, float]:
        """执行优化"""
        print("开始粒子群优化...")
        start_time = time.time()
        
        for iteration in range(self.max_iter):
            for i in range(self.num_particles):
                # 更新速度
                r1, r2 = np.random.random(2)
                cognitive = self.c1 * r1 * (self.pbest_positions[i] - self.positions[i])
                social = self.c2 * r2 * (self.gbest_position - self.positions[i])
                self.velocities[i] = self.w * self.velocities[i] + cognitive + social
                
                # 更新位置(取整)
                new_position = self.positions[i] + np.round(self.velocities[i]).astype(int)
                
                # 确保位置在边界内
                for d in range(self.num_dimensions):
                    min_val, max_val = self.bounds[d]
                    new_position[d] = np.clip(new_position[d], min_val, max_val)
                
                # 计算适应度
                new_fitness = self.fitness_func(new_position)
                
                # 更新个体最佳
                if new_fitness < self.pbest_scores[i]:
                    self.pbest_positions[i] = new_position.copy()
                    self.pbest_scores[i] = new_fitness
                    
                    # 更新全局最佳
                    if new_fitness < self.gbest_score:
                        self.gbest_position = new_position.copy()
                        self.gbest_score = new_fitness
            
            # 记录历史
            self.history['gbest_scores'].append(self.gbest_score)
            self.history['avg_scores'].append(np.mean(self.pbest_scores))
            self.history['positions'].append(self.positions.copy())
            
            # 每10次迭代打印进度
            if (iteration + 1) % 10 == 0 or iteration == 0:
                print(f"迭代 {iteration+1}/{self.max_iter}, "
                      f"最佳适应度: {self.gbest_score:.4f}, "
                      f"平均适应度: {np.mean(self.pbest_scores):.4f}")
        
        elapsed_time = time.time() - start_time
        print(f"\n优化完成!")
        print(f"总时间: {elapsed_time:.2f}秒")
        print(f"最优解: {self.gbest_position}")
        print(f"最优适应度: {self.gbest_score:.4f}")
        
        return self.gbest_position, self.gbest_score
    
    def plot_convergence(self, save_path: str = None):
        """绘制收敛曲线"""
        fig, ax = plt.subplots(figsize=(10, 6))
        
        iterations = range(1, len(self.history['gbest_scores']) + 1)
        
        ax.plot(iterations, self.history['gbest_scores'], 
                'b-', linewidth=2, label='全局最佳适应度')
        ax.plot(iterations, self.history['avg_scores'], 
                'r--', linewidth=2, label='平均适应度')
        
        ax.set_xlabel('迭代次数')
        ax.set_ylabel('适应度值')
        ax.set_title('粒子群优化收敛曲线')
        ax.grid(True, alpha=0.3)
        ax.legend()
        
        if save_path:
            plt.savefig(save_path, dpi=300, bbox_inches='tight')
        
        plt.show()


def main():
    """主函数"""
    # 创建天线阵列对象
    antenna = MIMOAntennaArray()
    
    # 计算最大半波长数
    available_vertical_space = antenna.max_vertical_total - antenna.antenna_height
    max_half_wavelength_vertical = int(np.floor(available_vertical_space / antenna.half_wavelength))
    max_half_wavelength_horizontal = int(np.floor(antenna.max_horizontal / antenna.half_wavelength))
    
    print(f"天线参数:")
    print(f"波长 λ = {antenna.wavelength*1000:.3f} mm")
    print(f"半波长 λ/2 = {antenna.half_wavelength*1000:.3f} mm")
    print(f"天线高度: 3λ = {antenna.antenna_height*1000:.3f} mm")
    print(f"最小间距: 3.5λ = {antenna.min_spacing_wavelengths*antenna.wavelength*1000:.3f} mm")
    print(f"最小间距(半波长数): {antenna.min_spacing_half_wavelengths} λ/2")
    
    print(f"\n可用垂直空间(扣除天线高度): {available_vertical_space*1000:.1f} mm")
    print(f"垂直方向最大半波长数: {max_half_wavelength_vertical}")
    print(f"水平方向最大半波长数: {max_half_wavelength_horizontal}")
    
    # 定义优化问题的边界
    max_offset = max_half_wavelength_vertical // 3
    
    bounds = [
        (antenna.min_spacing_half_wavelengths, max_half_wavelength_vertical // 2),
        (antenna.min_spacing_half_wavelengths, max_half_wavelength_vertical // 2),
        (antenna.min_spacing_half_wavelengths, max_half_wavelength_vertical // 2),
        (1, max_half_wavelength_horizontal),
        (0, max_offset),
        (0, max_offset)
    ]
    
    print("\n优化边界条件:")
    print(f"发射天线间距: {antenna.min_spacing_half_wavelengths}-{max_half_wavelength_vertical//2} 个半波长")
    print(f"接收天线间距: {antenna.min_spacing_half_wavelengths}-{max_half_wavelength_vertical//2} 个半波长")
    print(f"水平间距: 1-{max_half_wavelength_horizontal} 个半波长")
    print(f"起始偏移: 0-{max_offset} 个半波长")
    print(f"优化参数总数: {len(bounds)}")
    
    # 创建并运行PSO优化器
    pso = ParticleSwarmOptimizer(
        fitness_func=antenna.fitness_function,
        bounds=bounds,
        num_particles=50,
        max_iter=120,
        w=0.8,
        c1=1.5,
        c2=1.5
    )
    
    # 执行优化
    best_params, best_score = pso.optimize()
    
    # 绘制收敛曲线
    pso.plot_convergence()
    
    # 显示最优结果
    print("\n" + "="*60)
    print("最优天线排布参数:")
    print("="*60)
    print(f"发射天线间距: [{best_params[0]}, {best_params[1]}] × λ/2")
    print(f"接收天线间距: [{best_params[2]}] × λ/2")
    print(f"水平间距: {best_params[3]} × λ/2")
    print(f"发射起始偏移: {best_params[4]} × λ/2")
    print(f"接收起始偏移: {best_params[5]} × λ/2")
    print(f"适应度值: {best_score:.4f}")
    
    # 计算并显示详细结果
    tx_spacings = [best_params[0], best_params[1]]
    rx_spacings = [best_params[2]]
    horizontal_spacing = best_params[3]
    tx_start_offset = best_params[4]
    rx_start_offset = best_params[5]
    
    # 计算物理尺寸
    tx_positions, rx_positions, virtual_positions, no_overlap = antenna.calculate_array_positions(
        tx_spacings, rx_spacings, horizontal_spacing, tx_start_offset, rx_start_offset
    )
    
    tx_center_to_center = (best_params[0] + best_params[1]) * antenna.half_wavelength
    rx_center_to_center = best_params[2] * antenna.half_wavelength
    
    tx_min_y = np.min(tx_positions[:, 1])
    tx_max_y = np.max(tx_positions[:, 1])
    rx_min_y = np.min(rx_positions[:, 1])
    rx_max_y = np.max(rx_positions[:, 1])
    
    min_y = min(tx_min_y, rx_min_y)
    max_y = max(tx_max_y, rx_max_y)
    total_height = (max_y - min_y) + antenna.antenna_height
    horizontal_total = horizontal_spacing * antenna.half_wavelength
    
    print("\n物理尺寸验证:")
    print(f"发射列高度范围: {tx_min_y*1000:.1f} - {tx_max_y*1000:.1f} mm")
    print(f"接收列高度范围: {rx_min_y*1000:.1f} - {rx_max_y*1000:.1f} mm")
    print(f"阵列总高度: {total_height*1000:.1f} mm")
    print(f"水平总宽度: {horizontal_total*1000:.1f} mm")
    print(f"最大允许总高度: {antenna.max_vertical_total*1000:.1f} mm")
    print(f"最大允许水平宽度: {antenna.max_horizontal*1000:.1f} mm")
    print(f"无重叠: {no_overlap}")
    
    # 验证约束
    constraints_met = True
    if total_height > antenna.max_vertical_total:
        print("✗ 阵列总高度超过6cm约束!")
        constraints_met = False
    if horizontal_total > antenna.max_horizontal:
        print("✗ 水平总宽度超过8mm约束!")
        constraints_met = False
    if not no_overlap:
        print("✗ 天线有重叠!")
        constraints_met = False
    
    # 验证最小间距
    for i, spacing in enumerate(tx_spacings):
        if spacing < antenna.min_spacing_half_wavelengths:
            print(f"✗ 发射天线间距{i+1} ({spacing}λ/2) 小于最小要求 {antenna.min_spacing_half_wavelengths}λ/2!")
            constraints_met = False
    
    for i, spacing in enumerate(rx_spacings):
        if spacing < antenna.min_spacing_half_wavelengths:
            print(f"✗ 接收天线间距{i+1} ({spacing}λ/2) 小于最小要求 {antenna.min_spacing_half_wavelengths}λ/2!")
            constraints_met = False
    
    if constraints_met:
        print("✓ 所有约束均满足!")
    
    # 计算辐射方向图性能
    theta_range, pattern_db = antenna.calculate_radiation_pattern(virtual_positions)
    hpbw, sll, _, _ = antenna.calculate_hpbw_and_sll(pattern_db)
    
    print(f"\n辐射方向图性能:")
    print(f"半功率波束宽度(HPBW): {hpbw:.2f}°")
    print(f"目标HPBW: {antenna.target_hpbw}°")
    print(f"峰值副瓣电平(SLL, 主瓣外): {sll:.2f} dB")
    print(f"虚拟天线数量: {len(virtual_positions)} (3×2={3 * 2})")
    if len(virtual_positions) > 1:
        print(f"虚拟阵列孔径: {(virtual_positions[-1] - virtual_positions[0])*1000:.1f} mm")
    
    # 绘制结果
    antenna.plot_results(best_params)


if __name__ == "__main__":
    main()

运行结果

相关推荐
吃杠碰小鸡1 小时前
Python+Ai学习流程
人工智能·python·学习
ab1515171 小时前
3.20二刷基础121、127,完成进阶61、62
数据结构·算法·排序算法
I_LPL1 小时前
day58 代码随想录算法训练营 图论专题11
数据结构·算法·图论
m0_730115111 小时前
C++中的命令模式实战
开发语言·c++·算法
夏星印1 小时前
学习吴恩达课程机器学习笔记
人工智能·笔记·学习·机器学习·ai
小比特_蓝光1 小时前
算法篇1-----双指针
数据结构·算法
JicasdC123asd2 小时前
快速跨阶段部分网络改进YOLOv26特征提取效率与梯度流动双重优化
人工智能·yolo·目标跟踪
所 爱2 小时前
【重磅更新】Cursor Pro 会员独享功能上线!支持 Claude 4.5,智能编码再升级!
人工智能