自由空间与Okumura-Hata、COST231 Hata、SUI模型Python仿真对比
2026/9/15 6:35:53 网站建设 项目流程

简介:面向移动通信与无线信道仿真学习者,资源内容系统梳理了自由空间损耗模型、Okumura-Hata模型、COST231 Hata模型以及802.16规范中的SUI信道模型,并结合MATLAB完成了仿真分析与理论对比。资源共21个文件,其中20个MATLAB脚本涵盖各模型的路径损耗计算、功率谱密度仿真与结果可视化,另有1个Word论文对信道特性和模型差异做了详细推导,便于对照代码理解公式。压缩包大小仅323KB,轻量便携。目前已有1042人学习/下载,适合通信工程专业学生、课程设计或课题研究者参考使用。通过这套MATLAB脚本与论文,读者可快速掌握大尺度衰落模型的适用频段与场景,理解Hata系列及SUI模型的参数影响,并能直接运行脚本观察不同环境下的损耗曲线,有效节省建模时间。

1. 为什么自由空间损耗模型要和Okumura-Hata、COST231 Hata、SUI放在一起做仿真分析

在做城市宏基站覆盖仿真时,如果只用自由空间损耗模型,900MHz频段在1km处算出的路径损耗只有91.5dB,而城市密集环境下的实测值通常会比这个结果高出30dB以上。Okumura-Hata、COST231 Hata和SUI信道模型正是把这种环境差异折算成可计算修正项的工程化方案。Okumura-Hata覆盖150~1500MHz的经典移动通信频段,COST231 Hata是它在1.5~2GHz频段上的扩展,SUI信道模型则面向2~3.5GHz固定无线接入场景。把这四个模型放进同一套仿真脚本里,既可以校准频率、天线高度、距离这些参数的单位,也能直接生成论文需要的对比曲线和数据表。这套流程适合无线网络规划、链路预算分析以及毕业设计里需要给评审快速复现结果的工程师。

2. 自由空间损耗模型:链路预算公式与Python最小实现

2.1 为什么自由空间损耗是其他经验模型的基准

全向天线发射的电磁波在无遮挡空间里会均匀扩散,接收功率与距离平方成反比,再考虑天线有效面积,还会出现频率平方项。把发射功率与接收功率之比写成对数形式,就是自由空间路径损耗公式:

PL_fs = 20 log10(d) + 20 log10(f) + 32.44

这里的d单位是km,f单位是MHz。常数32.44来自球面波扩展公式里的4π/c,经过单位换算后合并成一个常量。自由空间模型里没有地面反射、山体绕射、树叶遮挡,所以它给出的是一条链路的损耗下限。工程中做链路预算时,先算出自由空间损耗,再叠加阴影衰落余量、建筑物穿透损耗和干扰余量。Okumura-Hata、COST231 Hata和SUI这三种经验模型,本质都是在自由空间基准上加入环境修正,因此在仿真分析里把自由空间曲线放在基准位置,后续模型相对它抬升了多少,一眼就能看出来。

这个模型常用在视距微波接力、卫星通信地面站和电磁兼容测试场。城市宏蜂窝覆盖如果直接用自由空间损耗去算,规划软件会把覆盖半径算得偏大,导致实际部署时出现大量弱覆盖区域,这也是后面三种模型存在的核心原因。

2.2 用Python复现自由空间损耗并用DataFrame校验数值

import numpy as np import pandas as pd def fs_loss(d_km, f_mhz): """ 自由空间路径损耗模型 :param d_km: 距离,单位km,不允许为0 :param f_mhz: 频率,单位MHz :return: 路径损耗,单位dB """ return 20 * np.log10(d_km) + 20 * np.log10(f_mhz) + 32.44 d_km = np.array([1, 2, 5, 10, 20], dtype=float) f_mhz = 900 out_df = pd.DataFrame({ '距离(km)': d_km, '损耗(dB)': fs_loss(d_km, f_mhz) }) print(out_df.round(2))

这段代码的关键点是d_km使用float数组,np.log10只对正数有效,距离为0时会出现负无穷,所以函数注释里必须写明“不允许为0”。DataFrame输出便于直接复制到论文表格,也可以用round(2)保留两位小数。900MHz时,1km损耗为91.51dB,5km损耗为105.49dB,20km为117.54dB;把距离从1km翻倍到2km,损耗增加约6dB,这个增量来自20log10(2)项。

2.3 自由空间曲线怎么为后续Hata和SUI模型做基准对比

import matplotlib.pyplot as plt d_plot = np.linspace(0.5, 20, 200) plt.figure(figsize=(6, 4)) for f in [900, 1800, 3500]: plt.plot(d_plot, fs_loss(d_plot, f), lw=2, label=f'{f} MHz') plt.xlabel('距离 (km)') plt.ylabel('路径损耗 (dB)') plt.legend() plt.grid(True, linestyle='--') plt.savefig('fs_loss_baseline.png', dpi=300) plt.show()

这个图把三种频率的自由空间曲线画在一起,横轴用线性坐标,方便观察损耗随距离的单调增长趋势。如果后续要观察1km以内的近端数据,建议把横轴改成plt.semilogx(),否则近端曲线会被压在一起。仿真报告里先出一张只有自由空间的基准图,再把它放到与Hata、COST231、SUI的对比图里,整篇论文的逻辑会更清晰。

下表的数值和上面代码输出对齐,可以当作后续模型是否异常的第一道检查线。如果Okumura-Hata在城市环境900MHz的1km结果小于91.5dB,那代码里一定有单位错误。

频率 (MHz)1km (dB)5km (dB)20km (dB)
90091.51105.49117.54
180097.53111.51123.56
3500103.31117.29129.34

3. Okumura-Hata模型:城市、郊区修正项与参数边界

3.1 Hata公式如何把Okumura实测曲线变成可编程公式

Okumura在东京及周边城市做了大量传播损耗测试,总结出一套以频率、距离、基站天线高度和移动台天线高度为索引的经验曲线。Hata在Okumura曲线基础上做回归拟合,得到可以直接编程的解析式。城市标准公式为:

PL_urban = 69.55 + 26.16log10(f) - 13.82log10(hb) - a(hm) + (44.9 - 6.55log10(hb))log10(d)

其中a(hm)是移动台天线高度修正因子。中小城市场景下,a(hm) = (1.1log10(f) - 0.7)hm - (1.56log10(f) - 0.8)。大城市场景要换成a(hm) = 3.2(log10(11.75hm))^2 - 4.97。这些系数来自大量测量的统计拟合,物理含义并不强,不能随意扩展到别的频段。Okumura-Hata模型的有效边界是:频率150~1500MHz,距离1~20km,基站天线高度30~200m,移动台天线高度1~10m。在边界附近误差可能达到6~10dB,写论文时最好把适用范围和预期误差同时列出来。

3.2 用Python实现Okumura-Hata并输出不同环境损耗

def okumura_hata_loss(d_km, f_mhz, hb_m, hm_m, env='urban'): """ Okumura-Hata路径损耗 :param env: urban / suburban / open """ if f_mhz < 150 or f_mhz > 1500: print("[WARN] 频率超出Okumura-Hata适用范围") if d_km < 1 or d_km > 20: print("[WARN] 距离超出Okumura-Hata适用范围") a_hm = (1.1 * np.log10(f_mhz) - 0.7) * hm_m \ - (1.56 * np.log10(f_mhz) - 0.8) pl = (69.55 + 26.16 * np.log10(f_mhz) - 13.82 * np.log10(hb_m) - a_hm + (44.9 - 6.55 * np.log10(hb_m)) * np.log10(d_km)) if env == 'suburban': pl -= 2 * (np.log10(f_mhz / 28)) ** 2 + 5.4 elif env == 'open': pl -= 4.78 * (np.log10(f_mhz)) ** 2 \ + 18.33 * np.log10(f_mhz) - 40.94 return pl

函数中的a_hm使用中小城市修正,因为城市宏蜂窝仿真里移动台通常位于街道上,中小城市公式更贴近实测。郊区修正和开阔地修正都是负数,表示同距离下环境遮挡更少,损耗低于城市值。代码用print输出警告但不中断,便于在一次运行里同时排查多个越界条件。参数上hb_m应取天线中心相对地面的高度,而不是铁塔总高;移动台高度hm_m一般取1.5m或2m,对应手持终端高度。

3.3 900MHz下城市、郊区和开阔地仿真对比

d = np.linspace(1, 20, 5) df_hata = pd.DataFrame({'距离(km)': d}) for env in ['urban', 'suburban', 'open']: df_hata[env] = okumura_hata_loss(d, 900, 50, 2, env) print(df_hata.round(2))

输出结果中,urban的斜率明显高于郊区,开阔地曲线则整体低约28dB。这说明同样一个基站,覆盖开阔地时站间距可以明显拉大,而城市高楼区需要加密站点。仿真报告里不能只放曲线,还需要把这张数值表放进去,方便评审回溯数据来源。固定hb=50m、hm=2m是常见的默认选择,距离取1~20km已经覆盖一个宏蜂窝的典型小区半径范围。

4. COST231 Hata与SUI信道模型:高频段仿真实现与结果对比

4.1 COST231 Hata在1800MHz比Okumura-Hata更合适

Okumura-Hata模型频率上限到1500MHz,1.8GHz的GSM/LTE网络需要新的参数。COST231委员会在Hata基础上修改了常数项和频率系数,得到:

PL = 46.3 + 33.9log10(f) - 13.82log10(hb) - a(hm) + (44.9 - 6.55log10(hb))log10(d) + C

其中C=0dB对应中等城市或郊区,C=3dB对应大城市核心区。与Okumura-Hata相比,常数项从69.55降到46.3,频率系数从26.16升到33.9。原因是1800MHz频段波长更短,环境反射和绕射行为与900MHz差别明显。COST231 Hata的适用范围是频率1500~2000MHz,距离1~20km,基站天线高度30~200m,移动台天线高度1~10m。在LTE的1800MHz频段仿真里,我一般优先用COST231 Hata,而不是把Okumura-Hata强行外推。

def cost231_hata_loss(d_km, f_mhz, hb_m, hm_m, cm=0): a_hm = (1.1 * np.log10(f_mhz) - 0.7) * hm_m \ - (1.56 * np.log10(f_mhz) - 0.8) return (46.3 + 33.9 * np.log10(f_mhz) - 13.82 * np.log10(hb_m) - a_hm + (44.9 - 6.55 * np.log10(hb_m)) * np.log10(d_km) + cm)

cm参数是城市核心区惩罚项,仿真中先把cm=0跑一遍,再对密集写字楼区域单独用cm=3做热点覆盖评估。如果整个城市一张网都用cm=3,过高的损耗会高估弱覆盖区域,推荐的站间距也会偏小。

4.2 SUI信道模型的公式和地形系数

SUI模型来自IEEE 802.16固定无线接入工作组,目标是2~3.5GHz频段的固定无线终端和点对多点宽带接入。它的公式结构比Hata系列更灵活:

PL_SUI = A + 10nlog10(d/d0) + Xf + Xh + s

其中A是距离d0=100m处的参考损耗,Xf = 6log10(f/2000)是频率修正,Xh是移动台高度修正,s是阴影衰落随机项。地形类别决定路径损耗指数n:A类对应山地/高植被,n=4.6,传播损耗随距离增长最快;C类对应平坦/少植被,n=2.6;B类居中,n=3.5。这个设计与Hata有很大差别,Hata把环境差异放在常数修正项里,SUI则把环境差异放在距离斜率里。

地形类别典型环境路径损耗指数nXh修正
A山地、高树4.6-10.8log10(hm/2)
B丘陵、中等树3.5-10.8log10(hm/2)
C平坦、少树2.6-20log10(hm/2)

应用时要注意SUI模型的d0是100m,不是1km。如果一直沿用自由空间模型里20log10(d)的习惯,很容易在d0换算上出错。

4.3 Python实现SUI并和三种模型画在同一张log距离图上

def sui_loss(d_km, f_mhz, terrain='C', hm_m=2.0): d0 = 0.1 # 参考距离100m,换算成km A = 20 * np.log10(4 * np.pi * f_mhz / 3) # d0=100m,f单位MHz n_dict = {'A': 4.6, 'B': 3.5, 'C': 2.6} n = n_dict[terrain] xf = 6.0 * np.log10(f_mhz / 2000.0) if terrain == 'C': xh = -20.0 * np.log10(hm_m / 2.0) else: xh = -10.8 * np.log10(hm_m / 2.0) return A + 10 * n * np.log10(d_km / d0) + xf + xh

A的表达式里4πf/3来自d0=100m和波长λ=300/fMHz的换算:4πd0/λ = 4π100fMHz/300 = 4π*fMHz/3。这个推导在论文附录里写一行,评审就不会认为模型是黑盒。s阴影衰落故意不放进去,因为随机项会影响多次对比的稳定性。建议在仿真循环里固定随机种子,例如np.random.RandomState(42),再生成阴影衰落值。

绘图时如果想直观对比模型差异,可以这样写:

d_plot = np.linspace(0.1, 10, 100) plt.semilogx(d_plot, fs_loss(d_plot, 1800), label='Free Space 1800MHz') plt.semilogx(d_plot, cost231_hata_loss(d_plot, 1800, 50, 2, cm=3), label='COST231 1800MHz') plt.semilogx(d_plot, okumura_hata_loss(d_plot, 900, 50, 2, 'urban'), label='Okumura-Hata 900MHz') plt.semilogx(d_plot, sui_loss(d_plot, 3500, 'C', 2), label='SUI 3500MHz C类') plt.show()

这里故意混用频段,主要是看曲线形状:SUI在log距离坐标下的斜率取决于地形n,Okumura-Hata和COST231 Hata的距离斜率都来自同一项(44.9 - 6.55log10(hb)),所以两曲线近似平行。严谨的论文对比应该让四个模型使用同一频段,但Okumura-Hata在1800MHz已经超界,所以常见做法是分两组:900MHz组和1800/3500MHz组,每组一张图。

5. 仿真分析与Word论文自动化:用python-docx批量生成图表和数据表

5.1 仿真数据统一结构

写论文最怕图里是一个数值,表格里又是另一个数值。常见做法是先建一个config字典,把频率、天线高度、距离数组统一放进去,然后生成一个总DataFrame。后续无论画图还是写Word,都从同一个DataFrame取值。

config = { 'f_mhz': 1800, 'hb_m': 50, 'hm_m': 2, 'd_km': np.linspace(1, 20, 8), } result = pd.DataFrame({'distance_km': config['d_km']}) result['free_space'] = fs_loss(result['distance_km'], config['f_mhz']) result['cost231_hata'] = cost231_hata_loss( result['distance_km'], config['f_mhz'], config['hb_m'], config['hm_m'], cm=3 ) print(result.round(2))

这种结构最大的好处是每次调整频率或天线高度,只改config里的值,图表和Word表格同步刷新,不会产生“图是1800MHz、表是900MHz”的数据错位。DataFrame的列也正好对应Word表格的表头,后续循环写入非常方便。

5.2 生成论文级曲线图

matplotlib默认样式直接放进Word会显得很单薄。论文图至少要做四件事:坐标轴字体不小于8pt、线条宽度2、网格使用虚线、保存dpi为300。

import matplotlib matplotlib.rcParams.update({ 'font.size': 9, 'lines.linewidth': 2, 'grid.linestyle': '--', 'axes.grid': True, }) fig, ax = plt.subplots(figsize=(6, 4)) ax.semilogx(result['distance_km'], result['free_space'], label='Free Space') ax.semilogx(result['distance_km'], result['cost231_hata'], label='COST231 Hata') ax.set_xlabel('Distance (km)') ax.set_ylabel('Path Loss (dB)') ax.legend() fig.tight_layout() fig.savefig('paper_cost231.png', dpi=300)

savefig时可以加bbox_inches='tight'自动裁掉四周空白。Word中图片宽度按版心设置:单栏论文设14cm左右,双栏设7.5cm左右。如果图片字号太大,缩小后会出现文字重叠,所以字体宁可设小一点,也不要依赖Word缩放。

5.3 python-docx自动写入Word论文

from docx import Document from docx.shared import Pt, Cm from docx.oxml.ns import qn from docx.enum.text import WD_ALIGN_PARAGRAPH doc = Document() doc.add_heading('仿真结果', level=1) doc.add_paragraph('这里给出1800MHz频段下自由空间与COST231 Hata模型的对比结果。') doc.add_picture('paper_cost231.png', width=Cm(14)) doc.paragraphs[-1].alignment = WD_ALIGN_PARAGRAPH.CENTER table = doc.add_table(rows=result.shape[0] + 1, cols=result.shape[1]) table.style = 'Table Grid' for j, col_name in enumerate(result.columns): table.cell(0, j).text = col_name for i, row in result.iterrows(): for j, val in enumerate(row): table.cell(i + 1, j).text = f'{val:.2f}' style = doc.styles['Normal'] style.font.name = 'Times New Roman' style.font.size = Pt(10.5) style.element.rPr.rFonts.set(qn('w:eastAsia'), '宋体') doc.save('path_loss_report.docx')

doc.add_picture插入图片后,新的图片会被放在最后一个段落,doc.paragraphs[-1]就是这张图片的段落对象,可以对它设置居中对齐。表格行数等于DataFrame行数加一个表头,所以数据循环从第1行开始写入。中文字体设置必须同时改eastAsia,否则Word里中文仍会显示成默认英文字体。需要不同小数位时,可以按列单独格式化,避免表格里小数位参差不齐。

5.4 Word里的三线表实现

论文常用三线表,而python-docx的Table Grid是带竖线的网格。三线表的做法是先清空所有边框,再单独设置顶线、表头下线和底线,需要操作w:tblBorders和w:tcBorders。如果不想写太多边框代码,一个更省事的选择是在Word里把Table Grid表格全文选中,然后套用自带的“三线表”样式。Alt text: 在自动化报告里,我一般保留Table Grid,只把数值列右对齐,这样既满足快速生成,也不会被编辑部退稿。

6. 基于SUI与Okumura-Hata边界的仿真验证技巧

6.1 用统一范围检查函数拦截越界参数

def check_range(model, f_mhz, d_km): range_map = { 'fs': (0, 100000, 0, 10000), 'okumura_hata': (150, 1500, 1, 20), 'cost231_hata': (1500, 2000, 1, 20), 'sui': (2000, 3500, 0.1, 10), } f_lo, f_hi, d_lo, d_hi = range_map[model] return f_lo <= f_mhz <= f_hi and d_lo <= d_km <= d_hi

仿真前对每个取样点调用check_range,越界点记录到warn列表,而不是中断程序。这样可以在论文附录里写清楚哪些点属于外推,评审看到后不会把外推结果当成模型本身的验证结论。

6.2 SUI模型单位一致性自检

SUI模型里最容易错的单位是d0。自检方法是令d_km=0.1、hm=2m,此时10nlog10(d/d0)=0,Xh=0,PL应等于A+Xf。以3500MHz、C类为例,A大约等于83.33dB,Xf=6log10(3500/2000)=4.6dB,所以100m处损耗应该在87.9dB左右。如果函数输出的结果是100dB以上,说明d0可能被写成了1km,或者A表达式里的波长单位出了问题。

频率 (MHz)SUI A值 (dB)C类n1km处损耗 (dB)
200078.462.6104.46
250080.392.6106.39
350083.332.6109.33

1km处的损耗是按A+102.6log10(1/0.1)计算的,即A+26dB。仿真时如果发现1km结果小于A值,说明log10(d/d0)写成了log10(d*10),距离单位混乱。对照上表逐项核对自己的SUI输出,能定位大部分单位问题。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询