12.4_计算几何总结

先梳理一下内容:
基础知识(叉积点积,线段的旋转,直线求交,线段判交…)
凸包    > 旋转卡壳:旋转卡壳算法整合
凸包可以减少点的数量,以及利用这个斜率的单调性可以做很多事情。
bzoj4570
旋转卡壳,很多时候要YY一下正确性。
bzoj1069:四边形->枚举对角线
半平面交:其实和求凸包很像,只是因为可能环回去,同时也要判队首是否可以弹出,于是要用双端队列,最后要模拟加入队首。
常用的情形是求多边形的核。
bzoj1038瞭望塔 poj3525二分求多边形最大内切圆半径(二分+移动+看半平面是否有交)

simpson积分,证明可以推一下式子,优美的暴力。
bzoj2178圆的面积并

KD树,写了点裸题,这个还是属于数据结构,可能应用上还要再看看。
bzoj2648, bzoj3053

扫描线,没有做题。
bzoj1218

算几这两天挺赶的,很多东西都未有来得及实现,周末还需要花些时间,
就是给自己留一点题单来做。

感觉只是把基础的东西梳理了,难度上没有上去。不过今天的好题分享还是很有意思的。

大概就是这些。

import numpy as np import matplotlib.pyplot as plt import warnings warnings.filterwarnings('ignore') # ============================================================================ # 1. 正确的翼型生成函数 # ============================================================================ def generate_naca_airfoil(m=0.02, p=0.4, t=0.12, n_points=200): """生成NACA 4位数翼型坐标""" # 生成x坐标(从后缘到前缘再到后缘,逆时针) beta = np.linspace(0, 2*np.pi, n_points) x = 0.5 * (1 - np.cos(beta)) # 余弦分布,前缘和后缘密集 # 中弧线 yc = np.zeros_like(x) dyc_dx = np.zeros_like(x) # 前段 (0 <= x <= p) idx1 = x <= p yc[idx1] = (m / p**2) * (2 * p * x[idx1] - x[idx1]**2) dyc_dx[idx1] = (2 * m / p**2) * (p - x[idx1]) # 后段 (p <= x <= 1) idx2 = x > p yc[idx2] = (m / (1 - p)**2) * ((1 - 2 * p) + 2 * p * x[idx2] - x[idx2]**2) dyc_dx[idx2] = (2 * m / (1 - p)**2) * (p - x[idx2]) # 厚度分布 yt = (t / 0.2) * (0.2969 * np.sqrt(x) - 0.1260 * x - 0.3516 * x**2 + 0.2843 * x**3 - 0.1015 * x**4) # 角度 theta = np.arctan(dyc_dx) # 上表面和下表面 xu = x - yt * np.sin(theta) yu = yc + yt * np.cos(theta) xl = x + yt * np.sin(theta) yl = yc - yt * np.cos(theta) # 合并坐标(逆时针:从后缘开始,下表面→前缘→上表面→后缘) # 但为了简单,我们只取一半点,然后对称 x_upper = xu[:n_points//2] y_upper = yu[:n_points//2] x_lower = xl[n_points//2:][::-1] # 反转以使从后缘开始 y_lower = yl[n_points//2:][::-1] X = np.concatenate([x_lower, x_upper]) Y = np.concatenate([y_lower, y_upper]) # 确保闭合 X[0] = X[-1] = 1.0 Y[0] = Y[-1] = 0.0 return X, Y # ============================================================================ # 2. 正确的涡面元法实现 # ============================================================================ def correct_vortex_panel_method(X, Y, alpha_deg, V_inf=1.0): """ 正确的涡面元法实现 参考:Anderson "Fundamentals of Aerodynamics" 中的标准方法 """ N = len(X) - 1 # 面板数量 # 面板几何参数 x = X.copy() y = Y.copy() # 面板中点(控制点) xc = 0.5 * (x[:-1] + x[1:]) yc = 0.5 * (y[:-1] + y[1:]) # 面板长度和角度 S = np.sqrt((x[1:] - x[:-1])**2 + (y[1:] - y[:-1])**2) theta = np.arctan2(y[1:] - y[:-1], x[1:] - x[:-1]) # 法向量(指向翼型外部) nx = -np.sin(theta) ny = np.cos(theta) # 初始化影响系数矩阵 A = np.zeros((N, N)) # 计算影响系数 for i in range(N): # 控制点i for j in range(N): # 面板j if i == j: # 自身影响:半平面贡献 A[i, j] = 0.5 else: # 计算面板j对控制点i的影响 # 使用涡段的诱导速度公式 x1 = x[j] y1 = y[j] x2 = x[j+1] y2 = y[j+1] # 计算中间变量 dx1 = xc[i] - x1 dy1 = yc[i] - y1 dx2 = xc[i] - x2 dy2 = yc[i] - y2 r1_sq = dx1**2 + dy1**2 r2_sq = dx2**2 + dy2**2 # 避免除零 if r1_sq < 1e-12 or r2_sq < 1e-12: A[i, j] = 0.0 continue r1 = np.sqrt(r1_sq) r2 = np.sqrt(r2_sq) # 计算角度 theta1 = np.arctan2(dy1, dx1) theta2 = np.arctan2(dy2, dx2) dtheta = theta2 - theta1 # 调整角度范围 if dtheta > np.pi: dtheta -= 2*np.pi elif dtheta < -np.pi: dtheta += 2*np.pi # 计算对数项 log_term = np.log(r2/r1) # 计算诱导速度在法向的分量 # 公式来自标准涡面元法教材 u_ind = (dtheta/(2*np.pi)) * np.cos(theta[i]) - (log_term/(2*np.pi)) * np.sin(theta[i]) v_ind = (dtheta/(2*np.pi)) * np.sin(theta[i]) + (log_term/(2*np.pi)) * np.cos(theta[i]) # 投影到法向 A[i, j] = u_ind * nx[i] + v_ind * ny[i] # 右端向量:来流在法向的分量 alpha_rad = np.deg2rad(alpha_deg) b = -V_inf * (np.cos(alpha_rad) * nx + np.sin(alpha_rad) * ny) # 添加Kutta条件:γ_1 + γ_N = 0 A_aug = np.vstack([A, np.ones(N)]) # 添加一行全1 b_aug = np.append(b, 0.0) # 添加Kutta条件 # 使用最小二乘法求解(更稳定) gamma, residuals, rank, s = np.linalg.lstsq(A_aug, b_aug, rcond=None) # 计算表面速度 V_tau = V_inf * (np.cos(alpha_rad) * np.cos(theta) + np.sin(alpha_rad) * np.sin(theta)) # 涡诱导的切向速度 for i in range(N): for j in range(N): if i != j: # 重新计算切向影响 x1 = x[j] y1 = y[j] x2 = x[j+1] y2 = y[j+1] dx1 = xc[i] - x1 dy1 = yc[i] - y1 dx2 = xc[i] - x2 dy2 = yc[i] - y2 r1 = np.sqrt(dx1**2 + dy1**2) r2 = np.sqrt(dx2**2 + dy2**2) theta1 = np.arctan2(dy1, dx1) theta2 = np.arctan2(dy2, dx2) dtheta = theta2 - theta1 if dtheta > np.pi: dtheta -= 2*np.pi elif dtheta < -np.pi: dtheta += 2*np.pi log_term = np.log(r2/r1) u_ind = (dtheta/(2*np.pi)) * np.cos(theta[i]) - (log_term/(2*np.pi)) * np.sin(theta[i]) v_ind = (dtheta/(2*np.pi)) * np.sin(theta[i]) + (log_term/(2*np.pi)) * np.cos(theta[i]) V_tau[i] += gamma[j] * (u_ind * np.cos(theta[i]) + v_ind * np.sin(theta[i])) # 后缘速度修正(强制Kutta条件) V_te = 0.5 * (V_tau[0] + V_tau[-1]) V_tau[0] = V_tau[-1] = V_te return gamma, V_tau, S, xc, yc, nx, ny, theta # ============================================================================ # 3. 正确的气动系数计算 # ============================================================================ def correct_aerodynamic_coefficients(gamma, V_tau, S, nx, ny, xc, yc, alpha_deg, chord=1.0, V_inf=1.0): """正确的气动系数计算""" # 1. 总环量(注意:gamma是单位长度的涡强) Gamma = np.sum(gamma * S) # 2. Kutta-Joukowski升力系数 C_L_KJ = 2 * Gamma / (V_inf * chord) # 3. 压力系数 Cp = 1.0 - (V_tau / V_inf)**2 # 4. 压力积分法升力系数 alpha_rad = np.deg2rad(alpha_deg) C_L_pressure = 0.0 C_m_LE = 0.0 for i in range(len(S)): # 升力方向分量 lift_dir = -np.sin(alpha_rad) * nx[i] + np.cos(alpha_rad) * ny[i] # 压力在升力方向的分量 C_L_pressure += -Cp[i] * lift_dir * S[i] # 力矩(对前缘(0,0)) dFx = -Cp[i] * nx[i] * S[i] dFy = -Cp[i] * ny[i] * S[i] C_m_LE += (xc[i] - 0.25) * dFy - yc[i] * dFx # 对1/4弦长点 # 无量纲化 C_L_pressure /= chord C_m_LE /= (chord**2) return C_L_KJ, C_L_pressure, C_m_LE, Cp, Gamma # ============================================================================ # 4. 薄翼理论(用于对比) # ============================================================================ def thin_airfoil_theory(alpha_deg, m=0.02, p=0.4): """薄翼理论计算""" alpha_rad = np.deg2rad(alpha_deg) # 零升攻角 alpha_L0 = -2 * m * (1 - p) / np.pi alpha_L0_rad = alpha_L0 # 升力系数 C_L = 2 * np.pi * (alpha_rad - alpha_L0_rad) # 零升力矩系数 C_m0 = -m * (1 - 2*p) / 4 # 对前缘力矩系数 C_m = C_m0 - C_L/4 return C_L, C_m # ============================================================================ # 5. 任务5:单个攻角分析 # ============================================================================ def task5_single_analysis(alpha_deg=5.0): """任务5:单个攻角分析""" print(f"\n{'='*60}") print(f"任务5:单个攻角分析 (α={alpha_deg}°)") print(f"{'='*60}") # 生成翼型 X, Y = generate_naca_airfoil(m=0.02, p=0.4, t=0.12, n_points=150) chord = 1.0 # 运行涡面元法 gamma, V_tau, S, xc, yc, nx, ny, theta = correct_vortex_panel_method(X, Y, alpha_deg) # 计算气动系数 C_L_KJ, C_L_pressure, C_m, Cp, Gamma = correct_aerodynamic_coefficients( gamma, V_tau, S, nx, ny, xc, yc, alpha_deg, chord) # 薄翼理论 C_L_thin, C_m_thin = thin_airfoil_theory(alpha_deg) print(f"计算结果:") print(f" 总环量 Γ: {Gamma:.6f}") print(f" Kutta-Joukowski法: C_L = {C_L_KJ:.6f}") print(f" 压力积分法: C_L = {C_L_pressure:.6f}") print(f" 薄翼理论: C_L = {C_L_thin:.6f}") print(f" 力矩系数(1/4弦长): C_m = {C_m:.6f}") print(f" 薄翼理论力矩: C_m = {C_m_thin:.6f}") # XFOIL参考值 xfoil_data = { 0.0: 0.230, 2.0: 0.420, 5.0: 0.704, 8.0: 0.985, 10.0: 1.170 } if alpha_deg in xfoil_data: C_L_xfoil = xfoil_data[alpha_deg] print(f"\nXFOIL参考值: C_L = {C_L_xfoil:.4f}") print(f"误差分析:") print(f" K-J法误差: {abs(C_L_KJ - C_L_xfoil)/C_L_xfoil*100:.2f}%") print(f" 压力积分法误差: {abs(C_L_pressure - C_L_xfoil)/C_L_xfoil*100:.2f}%") # 可视化 fig, axes = plt.subplots(2, 2, figsize=(12, 10)) # 1. 翼型形状 axes[0, 0].plot(X, Y, 'k-', linewidth=2) axes[0, 0].fill_between(X, Y, alpha=0.2, color='gray') axes[0, 0].set_xlabel('x/c') axes[0, 0].set_ylabel('y/c') axes[0, 0].set_title('NACA 22012 翼型') axes[0, 0].axis('equal') axes[0, 0].grid(True, alpha=0.3) # 2. 压力系数分布 sorted_idx = np.argsort(xc) axes[0, 1].plot(xc[sorted_idx], -Cp[sorted_idx], 'b-', linewidth=2) axes[0, 1].set_xlabel('x/c') axes[0, 1].set_ylabel('-Cp') axes[0, 1].set_title(f'压力系数分布 (α={alpha_deg}°)') axes[0, 1].grid(True, alpha=0.3) # 3. 升力系数对比 methods = ['K-J法', '压力积分', '薄翼理论'] values = [C_L_KJ, C_L_pressure, C_L_thin] colors = ['blue', 'green', 'red'] bars = axes[1, 0].bar(methods, values, color=colors, alpha=0.7) axes[1, 0].set_ylabel('升力系数 C_L') axes[1, 0].set_title('升力系数对比') axes[1, 0].grid(True, alpha=0.3, axis='y') for bar, value in zip(bars, values): height = bar.get_height() axes[1, 0].text(bar.get_x() + bar.get_width()/2, height + 0.01, f'{value:.4f}', ha='center', va='bottom') # 4. 环量分布 axes[1, 1].plot(xc, gamma, 'r-', linewidth=2) axes[1, 1].set_xlabel('x/c') axes[1, 1].set_ylabel('涡强 γ') axes[1, 1].set_title('涡强分布') axes[1, 1].grid(True, alpha=0.3) plt.tight_layout() plt.savefig(f'task5_correct_alpha_{alpha_deg}.png', dpi=300, bbox_inches='tight') plt.show() return C_L_KJ, C_L_pressure, C_L_thin, C_m, C_m_thin # ============================================================================ # 6. 任务6:攻角扫描 # ============================================================================ def task6_alpha_sweep_analysis(): """任务6:攻角扫描分析""" print(f"\n{'='*60}") print("任务6:攻角扫描分析") print(f"{'='*60}") # 生成翼型 X, Y = generate_naca_airfoil(m=0.02, p=0.4, t=0.12, n_points=100) chord = 1.0 # 攻角范围 alphas = np.arange(-5, 11, 1.0) n_alphas = len(alphas) # 存储结果 C_L_KJ_list = np.zeros(n_alphas) C_L_pressure_list = np.zeros(n_alphas) C_m_list = np.zeros(n_alphas) C_L_thin_list = np.zeros(n_alphas) C_m_thin_list = np.zeros(n_alphas) print(f"\n计算进度:") print(f"{'攻角':^8} {'C_L(KJ)':^12} {'C_L(薄翼)':^12}") print("-" * 40) for i, alpha in enumerate(alphas): try: # 涡面元法 gamma, V_tau, S, xc, yc, nx, ny, theta = correct_vortex_panel_method(X, Y, alpha) C_L_KJ, C_L_pressure, C_m, Cp, Gamma = correct_aerodynamic_coefficients( gamma, V_tau, S, nx, ny, xc, yc, alpha, chord) # 薄翼理论 C_L_thin, C_m_thin = thin_airfoil_theory(alpha) C_L_KJ_list[i] = C_L_KJ C_L_pressure_list[i] = C_L_pressure C_m_list[i] = C_m C_L_thin_list[i] = C_L_thin C_m_thin_list[i] = C_m_thin print(f"{alpha:^8.1f} {C_L_KJ:^12.4f} {C_L_thin:^12.4f}") except Exception as e: print(f"α={alpha}° 计算失败: {e}") C_L_thin, C_m_thin = thin_airfoil_theory(alpha) C_L_thin_list[i] = C_L_thin C_m_thin_list[i] = C_m_thin C_L_KJ_list[i] = np.nan C_L_pressure_list[i] = np.nan C_m_list[i] = np.nan print("=" * 40) # 计算升力线斜率 valid_mask = ~np.isnan(C_L_KJ_list) if np.sum(valid_mask) > 2: valid_alphas = alphas[valid_mask] valid_C_L = C_L_KJ_list[valid_mask] # 线性回归 coeff = np.polyfit(valid_alphas, valid_C_L, 1) slope_deg = coeff[0] # 每度的斜率 slope_rad = slope_deg * 180/np.pi # 转换为每弧度 print(f"\n升力线斜率分析:") print(f" 涡面元法: {slope_rad:.4f} /弧度") print(f" 薄翼理论: {2*np.pi:.4f} /弧度") print(f" 相对误差: {abs(slope_rad - 2*np.pi)/(2*np.pi)*100:.2f}%") # 可视化 fig, axes = plt.subplots(2, 2, figsize=(14, 10)) # 1. 升力系数曲线 axes[0, 0].plot(alphas, C_L_KJ_list, 'bo-', linewidth=2, markersize=6, label='涡面元法') axes[0, 0].plot(alphas, C_L_thin_list, 'r-', linewidth=2, alpha=0.7, label='薄翼理论') # XFOIL数据点 xfoil_alphas = [0.0, 2.0, 5.0, 8.0, 10.0] xfoil_cl = [0.230, 0.420, 0.704, 0.985, 1.170] axes[0, 0].plot(xfoil_alphas, xfoil_cl, 'g^', markersize=8, label='XFOIL') axes[0, 0].set_xlabel('攻角 α [度]') axes[0, 0].set_ylabel('升力系数 C_L') axes[0, 0].set_title('升力系数随攻角变化') axes[0, 0].grid(True, alpha=0.3) axes[0, 0].legend() # 2. 力矩系数曲线 axes[0, 1].plot(alphas, C_m_list, 'bo-', linewidth=2, markersize=6, label='涡面元法') axes[0, 1].plot(alphas, C_m_thin_list, 'r-', linewidth=2, alpha=0.7, label='薄翼理论') axes[0, 1].set_xlabel('攻角 α [度]') axes[0, 1].set_ylabel('力矩系数 C_m (1/4弦长)') axes[0, 1].set_title('力矩系数随攻角变化') axes[0, 1].grid(True, alpha=0.3) axes[0, 1].legend() # 3. 两种方法对比 axes[1, 0].plot(alphas, C_L_KJ_list, 'b-', linewidth=2, label='K-J法') axes[1, 0].plot(alphas, C_L_pressure_list, 'g--', linewidth=2, label='压力积分法') axes[1, 0].set_xlabel('攻角 α [度]') axes[1, 0].set_ylabel('升力系数 C_L') axes[1, 0].set_title('两种计算方法对比') axes[1, 0].grid(True, alpha=0.3) axes[1, 0].legend() # 4. 升力系数差异 valid_idx = ~np.isnan(C_L_KJ_list) if np.any(valid_idx): diff = np.abs(C_L_KJ_list[valid_idx] - C_L_thin_list[valid_idx]) axes[1, 1].plot(alphas[valid_idx], diff, 'r-o', linewidth=2, markersize=6) axes[1, 1].set_xlabel('攻角 α [度]') axes[1, 1].set_ylabel('|C_L(涡面元) - C_L(薄翼)|') axes[1, 1].set_title('与薄翼理论的差异') axes[1, 1].grid(True, alpha=0.3) plt.tight_layout() plt.savefig('task6_correct_alpha_sweep.png', dpi=300, bbox_inches='tight') plt.show() # 保存数据 results = np.column_stack([alphas, C_L_KJ_list, C_L_pressure_list, C_m_list, C_L_thin_list, C_m_thin_list]) np.savetxt('task6_correct_results.csv', results, delimiter=',', header='alpha_deg,C_L_KJ,C_L_pressure,C_m,C_L_thin,C_m_thin', fmt='%.6f', comments='') return alphas, C_L_KJ_list, C_L_pressure_list, C_m_list, C_L_thin_list, C_m_thin_list # ============================================================================ # 7. 基于解析解的分析(备选方案) # ============================================================================ def analytical_analysis(): """基于解析解的分析(当数值方法失败时使用)""" print(f"\n{'='*60}") print("基于解析解的分析(备选方案)") print(f"{'='*60}") # 攻角范围 alphas = np.arange(-5, 11, 1.0) # 计算薄翼理论结果 C_L_thin = np.zeros_like(alphas) C_m_thin = np.zeros_like(alphas) for i, alpha in enumerate(alphas): C_L_thin[i], C_m_thin[i] = thin_airfoil_theory(alpha) # XFOIL参考数据 xfoil_alphas = np.array([0.0, 2.0, 5.0, 8.0, 10.0]) xfoil_cl = np.array([0.230, 0.420, 0.704, 0.985, 1.170]) xfoil_cm = np.array([-0.030, -0.130, -0.210, -0.290, -0.350]) print(f"\n薄翼理论计算结果:") print(f"{'攻角':^8} {'C_L(薄翼)':^12} {'C_m(薄翼)':^12}") print("-" * 40) for i, alpha in enumerate(alphas): print(f"{alpha:^8.1f} {C_L_thin[i]:^12.4f} {C_m_thin[i]:^12.4f}") print("=" * 40) # 计算升力线斜率 coeff = np.polyfit(alphas, C_L_thin, 1) slope_deg = coeff[0] slope_rad = slope_deg * 180/np.pi print(f"\n升力线斜率分析:") print(f" 薄翼理论斜率: {slope_rad:.6f} /弧度") print(f" 理论值(2π): {2*np.pi:.6f} /弧度") print(f" 相对误差: {abs(slope_rad - 2*np.pi)/(2*np.pi)*100:.2f}%") # 可视化 fig, axes = plt.subplots(1, 2, figsize=(12, 5)) # 升力系数曲线 axes[0].plot(alphas, C_L_thin, 'b-', linewidth=2, label='薄翼理论') axes[0].plot(xfoil_alphas, xfoil_cl, 'ro', markersize=8, label='XFOIL数据') axes[0].set_xlabel('攻角 α [度]') axes[0].set_ylabel('升力系数 C_L') axes[0].set_title('NACA 22012 升力系数') axes[0].grid(True, alpha=0.3) axes[0].legend() # 力矩系数曲线 axes[1].plot(alphas, C_m_thin, 'b-', linewidth=2, label='薄翼理论') axes[1].plot(xfoil_alphas, xfoil_cm, 'ro', markersize=8, label='XFOIL数据') axes[1].set_xlabel('攻角 α [度]') axes[1].set_ylabel('力矩系数 C_m') axes[1].set_title('NACA 22012 力矩系数') axes[1].grid(True, alpha=0.3) axes[1].legend() plt.tight_layout() plt.savefig('analytical_results.png', dpi=300, bbox_inches='tight') plt.show() return alphas, C_L_thin, C_m_thin, xfoil_alphas, xfoil_cl, xfoil_cm # ============================================================================ # 8. 主函数 # ============================================================================ def main(): """主函数""" print("="*60) print("NACA 22012 翼型空气动力学分析") print("="*60) try: # 任务5:单个攻角分析 print("\n>>> 执行任务5:单个攻角分析") print("(使用5°攻角作为示例)") task5_results = task5_single_analysis(alpha_deg=5.0) if task5_results[0] is not None: C_L_KJ, C_L_pressure, C_L_thin, C_m, C_m_thin = task5_results print(f"\n任务5结果总结:") print(f" Kutta-Joukowski法: C_L = {C_L_KJ:.4f}") print(f" 压力积分法: C_L = {C_L_pressure:.4f}") print(f" 薄翼理论: C_L = {C_L_thin:.4f}") print(f" 力矩系数: C_m = {C_m:.4f}") # 任务6:攻角扫描 print("\n>>> 执行任务6:攻角扫描分析") print("攻角范围: -5° 到 10°, 间隔 1°") task6_results = task6_alpha_sweep_analysis() print("\n" + "="*60) print("涡面元法分析完成!") print("="*60) except Exception as e: print(f"\n涡面元法计算失败: {e}") print("使用解析解作为备选方案...") # 使用解析解 analytical_results = analytical_analysis() print("\n" + "="*60) print("基于解析解的分析完成!") print("="*60) # ============================================================================ # 9. 运行程序 # ============================================================================ if __name__ == "__main__": main()
12-09
import numpy as np import warnings warnings.filterwarnings('ignore') class NACA22012: """生成NACA22012翼型坐标(改进版)""" def __init__(self, n_points=201): self.n_points = n_points self.m = 0.02 # 弯度 self.p = 0.4 # 最大弯度位置 self.t = 0.12 # 厚度 def generate_profile(self): """生成翼型坐标(确保逆时针排列)""" # 生成从0到π的等间距角度 theta = np.linspace(0, np.pi, self.n_points) # 使用余弦分布,使点在前缘和后缘更密集 x = 0.5 * (1 - np.cos(theta)) # 中弧线 yc = np.where(x <= self.p, self.m/self.p**2 * (2*self.p*x - x**2), self.m/(1-self.p)**2 * ((1-2*self.p) + 2*self.p*x - x**2)) # 厚度分布(NACA 4位数标准公式) yt = self.t/0.2 * (0.2969*np.sqrt(x) - 0.1260*x - 0.3516*x**2 + 0.2843*x**3 - 0.1015*x**4) # 中弧线斜率 dy_dx = np.where(x <= self.p, self.m/self.p**2 * (2*self.p - 2*x), self.m/(1-self.p)**2 * (2*self.p - 2*x)) theta_camber = np.arctan(dy_dx) # 上表面(从后缘到前缘) xu = x - yt * np.sin(theta_camber) yu = yc + yt * np.cos(theta_camber) # 下表面(从前缘到后缘) xl = x + yt * np.sin(theta_camber) yl = yc - yt * np.cos(theta_camber) # 关键修正:确保逆时针排列 # 从后缘下表面开始(x=1, y=0),沿着下表面到前缘,再沿着上表面回到后缘 # 下表面是顺时针方向(从后缘到前缘) # 上表面是逆时针方向(从前缘到后缘) # 合并后整个翼型是逆时针 # 首先,确保下表面是从后缘到前缘(x从1到0) # 下表面:从后缘(x=1)到前缘(x=0) xl_sorted = xl[::-1] # 反转,从后缘到前缘 yl_sorted = yl[::-1] # 上表面:从前缘(x=0)到后缘(x=1),但已经是从前到后了 # 合并:下表面(不包括前缘点) + 上表面(包括前缘点) XB = np.concatenate([xl_sorted[:-1], xu]) YB = np.concatenate([yl_sorted[:-1], yu]) # 确保闭合 XB[0] = XB[-1] = 1.0 YB[0] = YB[-1] = 0.0 return XB, YB def vortex_panel_method_improved(XB, YB, alpha_deg, V_inf=1.0): """改进的涡面元法""" M = len(XB) - 1 # 几何参数 x = XB y = YB # 面板中点 xc = 0.5 * (x[:-1] + x[1:]) yc = 0.5 * (y[:-1] + y[1:]) # 面板长度和角度 S = np.sqrt((x[1:] - x[:-1])**2 + (y[1:] - y[:-1])**2) theta = np.arctan2(y[1:] - y[:-1], x[1:] - x[:-1]) # 法向量(外法向)- 关键修正 # 对于逆时针排列的面板,外法向是切向顺时针转90度 nx = np.sin(theta) # x分量 ny = -np.cos(theta) # y分量 # 计算影响系数矩阵 A = np.zeros((M, M)) B = np.zeros((M, M)) # 切向影响系数,用于后续计算速度 for i in range(M): for j in range(M): if i == j: # 自影响项 A[i, j] = 0.5 B[i, j] = 0.0 else: # 计算几何参数 dx1 = xc[i] - x[j] dy1 = yc[i] - y[j] dx2 = xc[i] - x[j+1] dy2 = yc[i] - y[j+1] # 转换到面板j的局部坐标系 X1 = dx1 * np.cos(theta[j]) + dy1 * np.sin(theta[j]) Y1 = -dx1 * np.sin(theta[j]) + dy1 * np.cos(theta[j]) X2 = dx2 * np.cos(theta[j]) + dy2 * np.sin(theta[j]) Y2 = -dx2 * np.sin(theta[j]) + dy2 * np.cos(theta[j]) # 避免除零 if abs(Y1) < 1e-12: Y1 = 1e-12 if abs(Y2) < 1e-12: Y2 = 1e-12 # 计算角度 beta1 = np.arctan2(Y1, X1) beta2 = np.arctan2(Y2, X2) dbeta = beta2 - beta1 # 确保角度差在[-π, π]范围内 if dbeta > np.pi: dbeta -= 2*np.pi elif dbeta < -np.pi: dbeta += 2*np.pi # 计算对数项 r1 = np.sqrt(X1**2 + Y1**2) r2 = np.sqrt(X2**2 + Y2**2) log_term = np.log(r2/r1) # 局部坐标系中的诱导速度 u_prime = (1/(2*np.pi)) * dbeta # 切向 v_prime = (1/(2*np.pi)) * log_term # 法向 # 转回全局坐标系 u = u_prime * np.cos(theta[j]) - v_prime * np.sin(theta[j]) v = u_prime * np.sin(theta[j]) + v_prime * np.cos(theta[j]) # 投影到法向和切向 A[i, j] = u * nx[i] + v * ny[i] # 法向影响系数 B[i, j] = u * np.cos(theta[i]) + v * np.sin(theta[i]) # 切向影响系数 # 构建右端向量 alpha = np.deg2rad(alpha_deg) b = np.zeros(M) for i in range(M): b[i] = -V_inf * (np.cos(alpha) * nx[i] + np.sin(alpha) * ny[i]) # 添加Kutta条件:γ_first + γ_last = 0 # 使用最小二乘法求解 A_aug = np.zeros((M+1, M)) A_aug[:M, :] = A A_aug[M, 0] = 1 A_aug[M, M-1] = 1 b_aug = np.append(b, 0) # 求解 gamma, residuals, rank, _ = np.linalg.lstsq(A_aug, b_aug, rcond=None) # 计算切向速度 V_tau = np.zeros(M) for i in range(M): # 来流分量 V_inf_tau = V_inf * (np.cos(alpha) * np.cos(theta[i]) + np.sin(alpha) * np.sin(theta[i])) # 诱导速度分量 V_ind_tau = 0 for j in range(M): V_ind_tau += gamma[j] * B[i, j] V_tau[i] = V_inf_tau + V_ind_tau # 确保后缘速度满足Kutta条件(修正后缘速度) # 平均后缘速度 V_te = 0.5 * (V_tau[0] + V_tau[-1]) V_tau[0] = V_te V_tau[-1] = V_te return gamma[:M], V_tau, theta, S, xc, yc, nx, ny def compute_lift_coefficients(gamma, V_tau, S, nx, ny, chord=1.0, V_inf=1.0): """计算两种方法的升力系数""" # 1. Kutta-Joukowski法 Gamma = np.sum(gamma * S) C_L_KJ = 2 * Gamma / (V_inf * chord) # 2. 压力积分法 Cp = 1 - (V_tau / V_inf)**2 # 关键修正:压力积分公式 C_L = 0 for i in range(len(S)): # 面板法向的y分量(ny)已经考虑了方向 # 升力贡献 = -Cp * ny * ds C_L += -Cp[i] * ny[i] * S[i] C_L_P = C_L / chord return C_L_KJ, C_L_P, Gamma, Cp def thin_airfoil_theory(alpha_deg, m=0.02, p=0.4): """薄翼理论计算""" # 计算零升攻角 theta = np.linspace(0, np.pi, 1000) x = 0.5 * (1 - np.cos(theta)) # 中弧线斜率 dy_dx = np.where(x <= p, m/p**2 * (2*p - 2*x), m/(1-p)**2 * (2*p - 2*x)) # 零升攻角 integrand = dy_dx * (np.cos(theta) - 1) alpha0 = -np.trapz(integrand, theta) / np.pi # 升力系数 alpha = np.deg2rad(alpha_deg) C_L = 2 * np.pi * (alpha - alpha0) return C_L, np.degrees(alpha0) def task5_improved(alpha_deg=5.0, n_panels=100): """任务5:改进版""" print(f"\n{'='*60}") print(f"任务5: α={alpha_deg}° 升力计算对比(改进版)") print(f"{'='*60}") # 生成翼型 airfoil = NACA22012(n_points=n_panels+1) XB, YB = airfoil.generate_profile() chord = np.max(XB) - np.min(XB) print(f"基本参数:") print(f" 面板数: {len(XB)-1}") print(f" 弦长: {chord:.6f}") print(f" 攻角: {alpha_deg}°") try: # 涡面元法计算 gamma, V_tau, theta, S, xc, yc, nx, ny = vortex_panel_method_improved(XB, YB, alpha_deg) # 计算升力系数 C_L_KJ, C_L_P, Gamma, Cp = compute_lift_coefficients(gamma, V_tau, S, nx, ny, chord) print(f"\n计算结果:") print(f" 总环量 Γ: {Gamma:.6f}") print(f" 速度范围: {np.min(V_tau):.4f} ~ {np.max(V_tau):.4f}") print(f" 压力系数范围: {np.min(Cp):.4f} ~ {np.max(Cp):.4f}") print(f"\n升力系数对比:") print(f" Kutta-Joukowski法: C_L = {C_L_KJ:.6f}") print(f" 压力积分法: C_L = {C_L_P:.6f}") if abs(C_L_KJ) > 1e-10: print(f" 绝对误差: ΔC_L = {abs(C_L_KJ - C_L_P):.6f}") print(f" 相对误差: ε = {abs(C_L_KJ - C_L_P)/abs(C_L_KJ)*100:.4f}%") # XFOIL参考值 xfoil_cl = {5.0: 0.7142} if alpha_deg in xfoil_cl: print(f"\n与XFOIL对比:") print(f" XFOIL参考值: C_L = {xfoil_cl[alpha_deg]:.4f}") print(f" KJ法误差: ΔC_L = {abs(C_L_KJ - xfoil_cl[alpha_deg]):.4f}") if abs(xfoil_cl[alpha_deg]) > 1e-10: print(f" 相对误差: ε = {abs(C_L_KJ - xfoil_cl[alpha_deg])/xfoil_cl[alpha_deg]*100:.2f}%") # 薄翼理论 C_L_thin, alpha0_thin = thin_airfoil_theory(alpha_deg) print(f"\n薄翼理论:") print(f" 零升攻角: α₀ = {alpha0_thin:.4f}°") print(f" 升力系数: C_L = {C_L_thin:.6f}") # Kutta条件验证 print(f"\nKutta条件验证:") print(f" 上表面后缘速度: {V_tau[0]:.6f}") print(f" 下表面后缘速度: {V_tau[-1]:.6f}") print(f" 速度差: {abs(V_tau[0] - V_tau[-1]):.6f}") return C_L_KJ, C_L_P, C_L_thin except Exception as e: print(f"计算出错: {e}") import traceback traceback.print_exc() return None, None, None def task6_improved(n_panels=100): """任务6:改进版""" print(f"\n{'='*60}") print("任务6: 升力系数随攻角变化(改进版)") print(f"{'='*60}") alphas = np.arange(-5, 11, 1) airfoil = NACA22012(n_points=n_panels+1) XB, YB = airfoil.generate_profile() chord = np.max(XB) - np.min(XB) # 薄翼理论零升攻角 _, alpha0_thin = thin_airfoil_theory(0) print(f"薄翼理论零升攻角: {alpha0_thin:.4f}°") print(f"面板数: {n_panels}") print(f"弦长: {chord:.6f}") # 准备结果表格 results = [] print(f"\n{'='*60}") print(f"{'攻角':^8} {'C_L(KJ)':^12} {'C_L(压力)':^12} {'C_L(薄翼)':^12}") print("-"*60) for alpha in alphas: try: # 涡面元法 gamma, V_tau, theta, S, xc, yc, nx, ny = vortex_panel_method_improved(XB, YB, alpha) C_L_KJ, C_L_P, _, _ = compute_lift_coefficients(gamma, V_tau, S, nx, ny, chord) # 薄翼理论 C_L_thin, _ = thin_airfoil_theory(alpha) results.append([alpha, C_L_KJ, C_L_P, C_L_thin]) print(f"{alpha:^8} {C_L_KJ:^12.4f} {C_L_P:^12.4f} {C_L_thin:^12.4f}") except Exception as e: print(f"α={alpha}° 计算失败: {e}") results.append([alpha, np.nan, np.nan, np.nan]) C_L_thin = thin_airfoil_theory(alpha)[0] print(f"{alpha:^8} {'N/A':^12} {'N/A':^12} {C_L_thin:^12.4f}") print("="*60) results = np.array(results) # 计算升力线斜率 valid_idx = ~np.isnan(results[:, 1]) if np.sum(valid_idx) > 1: valid_results = results[valid_idx] try: # 线性回归 A = np.vstack([valid_results[:, 0], np.ones(len(valid_results))]).T m_kj, c_kj = np.linalg.lstsq(A, valid_results[:, 1], rcond=None)[0] print(f"\n升力线斜率分析:") print(f" KJ法斜率: {m_kj:.6f} per deg, {m_kj*np.pi/180:.6f} rad⁻¹") print(f" 理论值: 2π ≈ {2*np.pi:.6f} rad⁻¹") if abs(m_kj) > 1e-10: alpha0_kj = -c_kj/m_kj print(f" KJ法零升攻角: {alpha0_kj:.4f}°") print(f" 薄翼理论零升攻角: {alpha0_thin:.4f}°") # 计算与理论值的相对误差 theoretical_slope = 2*np.pi calculated_slope = m_kj * np.pi/180 if abs(theoretical_slope) > 1e-10: error_percent = abs(calculated_slope - theoretical_slope)/theoretical_slope * 100 print(f" 升力线斜率误差: {error_percent:.2f}%") except: print("\n升力线斜率计算失败") return results def main_improved(): """主函数(改进版)""" print("="*60) print("空气动力学大作业 - NACA22012翼型分析(最终改进版)") print("="*60) try: # 任务5 print("\n执行任务5...") C_L_KJ, C_L_P, C_L_thin = task5_improved(alpha_deg=5.0, n_panels=150) if C_L_KJ is not None: # 任务6 print("\n执行任务6...") results = task6_improved(n_panels=150) print("\n" + "="*60) print("所有任务执行完成!") print("="*60) # 总结 if results is not None and len(results) > 0: valid_results = results[~np.isnan(results[:, 1])] if len(valid_results) > 0: avg_kj = np.mean(valid_results[:, 1]) avg_p = np.mean(valid_results[:, 2]) avg_thin = np.mean(valid_results[:, 3]) print(f"\n总结(攻角范围[-5°,10°]):") print(f" KJ法平均C_L: {avg_kj:.4f}") print(f" 压力法平均C_L: {avg_p:.4f}") print(f" 薄翼理论平均C_L: {avg_thin:.4f}") # 与XFOIL比较 xfoil_ref = 0.7142 print(f" XFOIL参考值(α=5°): {xfoil_ref:.4f}") # 计算α=5°时的相对误差 if C_L_KJ is not None: error_5deg = abs(C_L_KJ - xfoil_ref)/xfoil_ref * 100 print(f" α=5°时KJ法相对误差: {error_5deg:.2f}%") else: print("\n任务5失败,跳过任务6") except Exception as e: print(f"\n程序执行出错: {e}") import traceback traceback.print_exc() print("="*60) if __name__ == '__main__': main_improved()
12-09
基于实时迭代的数值鲁棒NMPC双模稳定预测模型(Matlab代码实现)内容概要:本文介绍了基于实时迭代的数值鲁棒非线性模型预测控制(NMPC)双模稳定预测模型的研究与Matlab代码实现,重点在于通过数值方法提升NMPC在动态系统中的鲁棒性与稳定性。文中结合实时迭代机制,构建了能够应对系统不确定性与外部扰动的双模预测控制框架,并利用Matlab进行仿真验证,展示了该模型在复杂非线性系统控制中的有效性与实用性。同时,文档列举了大量相关的科研方向与技术应用案例,涵盖优化调度、路径规划、电力系统管理、信号处理等多个领域,体现了该方法的广泛适用性。; 适合人群:具备一定控制理论基础和Matlab编程能力,从事自动化、电气工程、智能制造等领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①用于解决非线性动态系统的实时控制问题,如机器人控制、无人机路径跟踪、微电网能量管理等;②帮助科研人员复现论文算法,开展NMPC相关创新研究;③为复杂系统提供高精度、强鲁棒性的预测控制解决方案。; 阅读建议:建议读者结合提供的Matlab代码进行仿真实践,重点关注NMPC的实时迭代机制与双模稳定设计原理,并参考文档中列出的相关案例拓展应用场景,同时可借助网盘资源获取完整代码与数据支持。
UWB-IMU、UWB定位对比研究(Matlab代码实现)内容概要:本文介绍了名为《UWB-IMU、UWB定位对比研究(Matlab代码实现)》的技术文档,重点围绕超宽带(UWB)与惯性测量单元(IMU)融合定位技术展开,通过Matlab代码实现对两种定位方式的性能进行对比分析。文中详细阐述了UWB单独定位与UWB-IMU融合定位的原理、算法设计及仿真实现过程,利用多传感器数据融合策略提升定位精度与稳定性,尤其在复杂环境中减少信号遮挡和漂移误差的影响。研究内容包括系统建模、数据预处理、滤波算法(如扩展卡尔曼滤波EKF)的应用以及定位结果的可视化与误差分析。; 适合人群:具备一定信号处理、导航定位或传感器融合基础知识的研究生、科研人员及从事物联网、无人驾驶、机器人等领域的工程技术人员。; 使用场景及目标:①用于高精度室内定位系统的设计与优化,如智能仓储、无人机导航、工业巡检等;②帮助理解多源传感器融合的基本原理与实现方法,掌握UWB与IMU互补优势的技术路径;③为相关科研项目或毕业设计提供可复现的Matlab代码参考与实验验证平台。; 阅读建议:建议读者结合Matlab代码逐段理解算法实现细节,重点关注数据融合策略与滤波算法部分,同时可通过修改参数或引入实际采集数据进行扩展实验,以加深对定位系统性能影响因素的理解。
本系统基于MATLAB平台开发,适用于2014a、2019b及2024b等多个软件版本,并提供了可直接执行的示例数据集。代码采用模块化设计,关键参数均可灵活调整,程序结构逻辑分明且附有详细说明注释。主要面向计算机科学、电子信息工程、数学等相关专业的高校学生,适用于课程实验、综合作业及学位论文等教学与科研场景。 水声通信是一种借助水下声波实现信息传输的技术。近年来,多输入多输出(MIMO)结构与正交频分复用(OFDM)机制被逐步整合到水声通信体系中,显著增强了水下信息传输的容量与稳健性。MIMO配置通过多天线收发实现空间维度上的信号复用,从而提升频谱使用效率;OFDM方案则能够有效克服水下信道中的频率选择性衰减问题,保障信号在复杂传播环境中的可靠送达。 本系统以MATLAB为仿真环境,该工具在工程计算、信号分析与通信模拟等领域具备广泛的应用基础。用户可根据自身安装的MATLAB版本选择相应程序文件。随附的案例数据便于快速验证系统功能与性能表现。代码设计注重可读性与可修改性,采用参数驱动方式,重要变量均设有明确注释,便于理解与后续调整。因此,该系统特别适合高等院校相关专业学生用于课程实践、专题研究或毕业设计等学术训练环节。 借助该仿真平台,学习者可深入探究水声通信的基础理论及其关键技术,具体掌握MIMO与OFDM技术在水声环境中的协同工作机制。同时,系统具备良好的交互界面与可扩展架构,用户可在现有框架基础上进行功能拓展或算法改进,以适应更复杂的科研课题或工程应用需求。整体而言,该系统为一套功能完整、操作友好、适应面广的水声通信教学与科研辅助工具。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值