这里用了元宝的编程功能,我整理了需求,代码由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()
运行结果
