ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

多址接入协议Matlab仿真:从ALOHA到CSMA/TDMA的建模与实现

2026/9/16 1:04:24 拓冰建站 浏览量
多址接入协议Matlab仿真:从ALOHA到CSMA/TDMA的建模与实现 简介基于Matlab实现的多址接入协议仿真源码面向通信、电子信息及计算机等相关专业学生可供课程设计、项目实践或毕业设计阶段对照学习。压缩包内共6个.m脚本文件包体仅约7KB主要涵盖纯ALOHA、时隙ALOHA、非持续CSMA等经典随机多址接入机制并针对不同协议变体或负载条件提供了多个可运行版本读者可直接运行脚本查看吞吐量表现、冲突处理逻辑与参数变化对性能的影响从而理解随机多址接入协议的核心思路。这份源码适合已有一定Matlab基础、希望深入掌握协议建模与仿真流程的读者但代码更偏参考资料不能直接照搬需要结合教材自行调试、修改并厘清各函数与循环结构才能迁移到自己的仿真场景中若配合课程实验或教材配套例题使用可更直观地对比各协议适用场景与性能差异。当前已有141人下载学习可用作协议仿真入门、课程报告或项目起步的参考。1. 多址接入协议仿真要解的真实问题一个办公室的无线接入点如果同一时刻只有一台设备在说话根本不需要接入协议但实际场景是几十台终端共享一个信道。多址接入协议要解决的就是共享信道时谁在什么时刻有权发送的调度问题。开工通信系统项目之前网络性能能不能达标、负载涨到什么程度会进入拥塞这类问题不能靠搭真机来回答成本太高所以要先做协议仿真。Matlab在这个领域被用得最多矩阵运算适合批量产生随机到达可视化函数库让仿真结果可以直接和理论曲线对齐。下面的实现从ALOHA、时隙ALOHA讲到CSMA和TDMA提供一套能自己跑、能改参数的仿真源码并说明每处关键参数的含义和常见坑。2. 多址接入协议分类与Matlab仿真建模方案2.1 三条路线固定分配、随机接入与轮询多址接入协议从资源分配方式上可以分成三类。第一类是固定分配包括以频率切分的FDMA、以时间切分的TDMA、以正交码区分的CDMA。这类协议把信道划分成互不重叠的资源块每个节点占用固定份额理论上不存在碰撞缺点是信道利用率静态某个节点没数据时它的资源被浪费只能靠组网设计去回填。第二类是随机接入节点想发就发碰撞后再找机会重发。ALOHA是这一支的原形后来演化出时隙ALOHA、CSMA/CD、CSMA/CA。随机接入在低负载时延迟小、实现简单但负载超过临界点后吞吐量不升反降。第三类是轮询与预约中心节点逐个询问或者节点先预约再传输控制开销占了信道一定比例适合对确定性要求高的工业现场总线。2.2 时间驱动与事件驱动两种仿真模型的选择Matlab仿真协议之前先要决定时间推进方式。时间驱动把仿真时间切成固定时间片在每个时间片内推进所有节点状态代码直观、调试容易缺点是事件密度高的时候需要更小步长运行时间成倍拉长。时隙ALOHA天然按时间片工作我一般直接用时间驱动每时隙一次泊松抽样就够了。事件驱动则只在状态变化的时刻处理逻辑比如包到达、传输结束中间空闲时间直接跳过运行效率高但实现复杂度上升。纯ALOHA没有时隙边界包在任何时刻可以到达我采用事件驱动泊松到达的包间隔服从指数分布用 exprnd 生成间隔序列再检查包之间是否落在脆弱期内。这里的选择原则是协议带时隙同步或离散退避单位就用时间驱动协议是连续时间模型且事件密度不高就用事件驱动。2.3 用参数表集中管理仿真变量协议仿真代码最容易失控的地方在于参数散落。节点数、时隙数、包长、负载范围各写在各处改一个变量就要全局搜索且容易漏改。我习惯把所有可调参数集中进一个配置结构体主脚本和所有函数都只读这个结构体。% params_config.m % 多址接入协议仿真集中参数配置 p.nNodes 50; % 节点总数 p.nSlots 50000; % 仿真总时隙数 p.pktLen 10; % 包长单位时隙 p.GRange 0.05:0.05:3.0; % 归一化负载G扫描范围 p.backoffMax 8; % CSMA最大退避时隙数 p.repeat 20; % 蒙特卡洛重复次数 p.randomSeed 42; % 随机数种子保证可复现参数说明GRange 里的归一化负载G统一表示每个包传输时间内平均到达的包数三种协议都按这个口径换算。pktLen 只影响CSMA和TDMAALOHA默认包时长为1个时隙不需要修改。每次实验跑完把 p 一起存盘后续复盘时能够精确还原数据。这个参数结构体要在主控脚本的开头读取避免函数内部重新定义同名变量导致隐性覆盖。3. ALOHA与时隙ALOHA的Matlab仿真实现3.1 吞吐量公式为什么是 S G·e^(-G)时隙ALOHA让所有节点在时隙边界开始发送一帧占一个完整时隙。如果同一时隙内到达多帧则全部撞毁。在节点数足够大、每个节点的产生率都较小的假设下时隙内的到达帧数可以近似为泊松分布。设平均每时隙到达G帧包含新产生帧和冲突后的重传尝试则恰好到达1帧的概率为 G·e^(-G)这个概率就是单位时隙成功传输帧的期望也就是吞吐量 S。函数在 G1 时取最大值 1/e ≈ 0.368这是时隙ALOHA的容量天花板。同样的推导放在纯ALOHA里由于包在任意时刻发送脆弱期从时隙边界扩展到2个包长的时间范围SG·e^(-2G)峰值只有 1/(2e) ≈ 0.184。仿真首要目的就是复现这条曲线并验证仿真模型与理论模型的一致性。3.2 时隙ALOHA仿真源码与逐行说明% slotted_aloha.m function S slotted_aloha(G, nSlots) % 时隙ALOHA仿真 % G: 平均每时隙到达帧数含新包与重传尝试 % nSlots: 仿真时隙总数 % S: 实测吞吐量单位成功帧数/时隙 success 0; for s 1:nSlots k poissrnd(G); % 该时隙到达帧数按泊松分布抽样 if k 1 % 恰好一帧传输成功 success success 1; end % k0表示空闲k2表示冲突均不计成功 end S success / nSlots; end代码逻辑说明每时隙用 poissrnd 从泊松分布抽取到达帧数k0空闲k1成功k2碰撞失败。这种写法不显式维护每个节点的重传状态而是把重传尝试折算进负载G是文献里典型的大群体模型。优点是单时隙计算量恒定适合批量扫描缺点是只能得到吞吐量拿不到时延分布需要时再扩展记录逻辑。调用它可以这样扫描一条曲线G 0.05:0.05:3.0; S arrayfun((g) slotted_aloha(g, 50000), G);arrayfun 逐个 g 调用仿真函数返回的 S 是与 G 等长的向量。50000时隙是让每点吞吐量波动降到约1%的经验值G偏小时可以适当缩减。3.3 纯ALOHA的事件驱动仿真纯ALOHA不需要时隙同步。若一帧从 t0 开始发送、持续 T 长任何落在区间 (t0-T, t0T) 内的其它帧起点都会与它产生重叠这个 2T 的窗口就是脆弱期。事件驱动的做法是先按指数分布生成所有帧的到达时刻再检查任意两帧是否落入彼此的脆弱期。% pure_aloha_event.m function S pure_aloha_event(G, simTime) % 事件驱动纯ALOHA仿真 % G: 归一化负载到达率×包长包长归一化为1 % simTime: 仿真总时间 % S: 实测吞吐量单位成功帧数/时间 T 1; % 包长归一化时间为1 arrivals []; t 0; while t simTime t t exprnd(1 / G); % 泊松到达间隔服从指数分布 if t simTime arrivals(end1) t; end end conflict false(1, numel(arrivals)); for i 1:numel(arrivals) for j i1:numel(arrivals) if arrivals(j) - arrivals(i) 2 * T conflict(i) true; % 两帧都判定冲突 conflict(j) true; end end end S sum(~conflict) / simTime; end参数说明exprnd 的输入是期望间隔 1/GG 越大平均间隔越短同样仿真时间内帧数越多。如果两帧起点相差恰好 2T则第一帧在第 T 时刻结束第二帧从 2T 时刻开始没有重叠因此判断条件用的是小于号。两层循环检测冲突的时间复杂度是 O(n^2)当 simTime 达到数十万时明显变慢可以把到达时刻先排序再使用滑动窗口只检查与当前帧间隔小于 2T 的后继帧。3.4 仿真值和理论值为什么对不上跑出来的 S-G 曲线如果整体高于理论值或低出 5% 以上先从三个方向排查。第一仿真时间不够长。泊松过程在短时间内的抽样方差大simTime 至少设到 20000且固定随机数种子才能稳定复现。第二边界效应。仿真开始与结束附近的帧其脆弱期一部分落在观测区间之外冲突机会被少算造成吞吐量略偏大。处理办法是去掉统计区间前 5% 和后 5% 的数据再计算 S。第三理论公式对到达过程的假设是平稳泊松流如果底层模型改成有限节点数且每个节点有独立的、非均匀的产生率实测结果必然偏离理论曲线这时应直接以仿真曲线为准并把输入条件写入报告。提示固定随机数种子用 rng(42) 即可Matlab 中同一版本下相同种子产生的随机序列完全一致这是复现所有实验的第一步。4. CSMA与TDMA协议仿真实现及核心参数对比4.1 CSMA持续发送模型的Matlab实现随机接入协议从ALOHA进化到CSMA核心变化是增加信道监听。1-持续CSMA的逻辑是节点持续监听信道空闲立即发送忙则继续监听直到空闲。这个持续等待实际上会造成多名等待者在同一空闲时刻同时发送退避机制不到位时冲突率不降反升。我在仿真里直接建一个等待队列表现信道忙时段积累的帧信道空闲瞬间把这批帧全部投入发送并用 total1 判断是否成功。% csma_persistent_sim.m function [S] csma_persistent_sim(G, pktLen, nSlots) % 1-持续CSMA简化仿真 % G: 归一化负载每个包长时间内平均到达帧数 % pktLen: 包长单位时隙 % nSlots: 仿真总时隙数 arrivePerSlot G / pktLen; % 每个时隙平均到达帧数 waiting 0; % 等待队列中的帧数 busyLeft 0; % 信道剩余忙时隙数 success 0; for s 1:nSlots newArrivals poissrnd(arrivePerSlot); if busyLeft 0 busyLeft busyLeft - 1; waiting waiting newArrivals; % 新包进入等待 else total waiting newArrivals; % 信道空闲竞争发送 if total 1 success success 1; busyLeft pktLen - 1; % 占用信道 elseif total 1 % 多包同时发送冲突且全部丢弃waiting 清空 end waiting 0; end end S success / nSlots; end代码逻辑说明单位时隙内到达率是 G 除以包长所以一个时隙平均到达 G/pktLen 帧。信道忙时新包全部排队信道空闲的那一拍等待队列里的帧和新到达帧同时尝试发送。total1 意味着只有一发话节点成功进入传输total1 则全部冲突丢弃。这个模型保留了 1-持续CSMA 最重要的行为特征节点不采样随机退避容易在空闲时刻集中发送。参数怎么调pktLen 增大会让信道被占用的周期变长单位时隙到达率变小吞吐量峰值位置会略微左移。把 pktLen 分别设成 5、10、20 对比可以看到低负载段差异不大高负载段冲突频次明显不同。仿真时 pktLen 保持正整数避免 busyLeft 计数出现小数位错位。4.2 TDMA仿真固定时隙分配下的饱和吞吐量TDMA 的建模核心是每个节点只在轮到自己的时隙发送。假设 N 个节点组成一帧每帧 N 个时隙每个节点在有包的概率 q 下占用自己的时隙发送。因为时隙专有只要包存在就一定能发送成功没有碰撞概率。仿真里用双重循环按帧和节点推进rand 与 q 比较来决定该节点本帧是否有包。% tdma_sim.m function S tdma_sim(N, q, nFrames) % TDMA固定时隙分配仿真 % N: 节点数 % q: 每个节点每帧拥有数据的概率 % nFrames:仿真帧数 % S: 每时隙成功传输帧数 success 0; for f 1:nFrames for node 1:N if rand q success success 1; end end end S success / (N * nFrames); end参数说明rand 产生 [0,1) 均匀随机数与 q 比较等价于一次伯努利抽样。由于每个时隙的唯一属主以概率 q 有包成功帧数期望就是 N·nFrames·q所以 S 直接等于 q。这个模型没有排队现象节点上一帧产生的包如果错过了自己的时隙要等下一帧但在吞吐量统计里不影响最终值。要做时延分析的话需要额外记录包的产生时刻和发送时刻的差值。4.3 各协议在相同负载下的能力对比下表是在相同归一化负载 G 下四种协议实测吞吐量的数量级对照。时隙ALOHA和CSMA在 G1 附近达到峰值TDMA按 qG 线性上升。数值来自单次仿真随机种子改变后会有波动但峰值位置和变化趋势是稳定的。协议峰值吞吐量归一化达到峰值的负载G冲突发生条件纯ALOHA≈0.184G0.5两帧脆弱期重叠时隙ALOHA≈0.368G1.0同时隙多帧1-持续CSMA一般0.5~0.60.8~1.2等待队列并发TDMA0~1线性上升任意无冲突从表里能看到一个反直觉现象时隙ALOHA在最优工作点 G1 时信道只有约37%的时隙在传有效数据剩余63%要么空转要么冲突。这类瓶颈不是代码实现造成而是协议本身的数学极限。CSMA加入监听之后可以把峰值拉到一半以上TDMA则从原理上完全消除碰撞但代价是空闲时隙无法被其他节点使用。4.4 归一化负载G的设置口径做多址接入对比时最容易踩的坑是 G 的定义不统一。有的资料把 G 定义为单位时间内的平均包数有的定义为单位包长时间内平均包数还有的定义为总到达率除以信道服务率。统一口径的方法是每个包长定为1个参考单位G 到达率 × 包长。在时隙ALOHA里时隙长度直接取包长在CSMA仿真代码里包长包含多个时隙需要先把到达率折算成每时隙到达数在TDMA里G 对应 q×节点占用因子。函数之间传参时我会在注释里写清输入量纲避免拿不同口径的 G 互相对比得到错误结论。5. 多址接入协议仿真结果可视化与性能评估方法5.1 一图对比四类协议的吞吐量-负载曲线跑完数据之后的第一件事永远是画图。把四种协议放进同一个坐标系横轴统一为 G、纵轴统一为 S才能在相同工作条件下做横向对比。Matlab 的 plot 叠加使用 hold on曲线符号区分协议类型最后一并设置图例和坐标标签。% Plot_throughput_curves.m G 0.05:0.05:3.0; S_sa arrayfun((g) slotted_aloha(g, 50000), G); S_pa arrayfun((g) pure_aloha_event(g, 50000), G); S_csma arrayfun((g) csma_persistent_sim(g, 10, 50000), G); figure(Color,w, Position,[100 100 600 450]); plot(G, S_sa, -o, LineWidth, 1.6); hold on; plot(G, S_pa, -^, LineWidth, 1.6); plot(G, S_csma, -s, LineWidth, 1.6); grid on; box on; xlabel(归一化负载 G); ylabel(归一化吞吐量 S); legend(时隙ALOHA,纯ALOHA,CSMA(包长10时隙), ... Location,southeast); title(多址接入协议仿真吞吐量对比);hold on 是保留当前坐标系继续叠加曲线的关键命令漏掉它时后画的曲线会覆盖前一条。legend 的 Location 参数选 southeast避免图例盖住曲线峰值区域。写技术报告时我还会用 exportgraphics 导出矢量版本exportgraphics(gcf, throughput_compare.pdf, ContentType,vector)这样插入文档后放大不会发虚。5.2 时延分布用ECDF画平均值会漏掉大尾巴吞吐量是协议性能的一半时延是另一半。ALOHA这类冲突重传模型的时延分布有明显重尾少数包可能等待极长时间平均时延容易被长尾样本拉高直接看均值会低估问题的严重性。对比多个协议时我一般绘制经验累积分布函数 ECDF。要获取时延数据需要在仿真中记录每个包的产生时隙和首次成功时隙。以时隙ALOHA为例可以在原函数基础上增加一段记录逻辑把每个成功包的目标等待时间写成样本数据然后用 ecdf 函数计算分布并绘图。% delay_ecdf_plot.m % 假设 delayData 是列向量单位时隙 [f, x] ecdf(delayData); figure(Color,w); stairs(x, f, LineWidth, 1.5); grid on; xlabel(时延时隙); ylabel(经验CDF); title(时隙ALOHA时延分布);ecdf 直接对样本估算累积分布不需要手工分桶或平滑处理。x 轴上每个阶梯对应一个样本的排序位置80% 分位点能直观看出长尾拖到多远。如果发现某个协议的80%分位时延是均值的3倍以上说明该负载点接近拥塞应当降低负载或者换用带监听的协议。5.3 多次运行取均值和95%置信区间单次仿真只是一次随机试验同样的参数在另一组随机数下结果会不同。为了把仿真结论写进文档至少跑20到30次然后计算均值、标准差和95%置信区间。置信区间半宽等于1.96倍标准误差标准误差是样本标准差除以运行次数的平方根。% confidence_interval.m runs 30; S_i zeros(runs, 1); for r 1:runs S_i(r) slotted_aloha(1.0, 20000); % 固定 G1 重复运行 end meanS mean(S_i); stdS std(S_i); ci95 [meanS - 1.96*stdS/sqrt(runs), ... meanS 1.96*stdS/sqrt(runs)]; fprintf(吞吐量均值 %.4f, 95%%置信区间 [%.4f, %.4f]\n, ... meanS, ci95(1), ci95(2));运行次数 runs 的选择看置信区间半宽。如果半宽超过0.01说明随机波动太大要么增加 runs要么加大每次仿真的时隙数。这两种做法都在减小方差的来源区别是前者控制运行间差异后者控制单次抽样精度。理论偏差仍然来自模型简化比如未建模功率捕获和误码率这个不是加大次数能消除的。提示输出文本里的 %% 是 fprintf 中输出 % 符号的转义写法许多人在第一次写这条语句时容易漏掉导致格式说明符报错。6. 蒙特卡洛并行加速与仿真源码组织6.1 参数扫描用parfor并行参数扫描的每个 G 值对应一次独立仿真天然适合并行。Matlab 的 parfor 只需要把 for 关键字替换掉前提是循环体内部不依赖迭代顺序且每个迭代的随机数流独立。启动并行池后多核加速效果在参数点较多时非常明显。% parallel_sweep.m if isempty(gcp(nocreate)) parpool(local, 4); end S_scan zeros(length(G), 1); parfor i 1:length(G) S_scan(i) slotted_aloha(G(i), 50000); end参数说明gcp(nocreate) 在并行池未创建时返回空避免每次运行都重新开池。parfor 循环体里不能用依赖循环顺序的变量也不建议在函数内部手动调用 rng 重置随机种子Matlab 会自动给每个 worker 分配独立随机流。4 核并行大约能把 60 个 G 点的扫描时间缩短到原来的 1/3 到 1/2瓶颈通常变成内存带宽而非计算本身。6.2 用标准误差判断收敛标准误差 SE σ/√n 是估计精度的核心指标其中 σ 是样本标准差n 是运行次数。SE 相对均值越小估计越可靠。工程上可以在每次运行后计算一次相对标准误差当它低于1%时停止追加运行次数避免无条件多跑浪费时间。% adaptive_stop.m S zeros(100, 1); n 10; SE_ratio 1; while SE_ratio 0.01 n 100 S(n) slotted_aloha(0.8, 50000); meanS mean(S(1:n)); SE_ratio std(S(1:n)) / sqrt(n) / max(meanS, eps); n n 1; end提示这个循环每多运行一次就额外做一次完整 50000 时隙仿真总体时间仍然线性增长。想快一点可以把单次仿真改为长仿真再分段切片例如跑 200000 时隙按每 50000 时隙切出一个独立统计值等价于一次获得 4 个样本切出来的段之间只有微弱相关性方差评估近似有效。6.3 把仿真源码组织成可交换的工程目录仿真代码如果全部堆在一个主脚本里参数、函数、绘图混在一起一旦协议数量超过三个就难以维护。我通常按功能拆成三块目录函数目录只放纯算法脚本目录负责调用、批处理与画图根目录保留参数配置和结果存档。multi_access_sim/ ├── functions/ % 协议仿真核心函数无绘图、无文件IO │ ├── slotted_aloha.m │ ├── pure_aloha_event.m │ ├── csma_persistent_sim.m │ └── tdma_sim.m ├── scripts/ % 主控与绘图脚本 │ ├── run_all_protocols.m │ ├── Plot_throughput_curves.m │ └── confidence_interval.m ├── params_config.m % 集中参数配置 ├── results.mat % 所有实验结果与参数快照 └── README.md % 说明每个函数输入量纲函数目录里的文件不画图、不写盘保证同一套协议代码可以在不同主脚本之间复用。scripts 目录放批实验脚本只在脚本里出现 figure、save、fprintf 这类副作用操作。results.mat 保存时连同 p 结构一起存避免几个月后回看数据却不知道用的是哪组参数。给同事复现时只需要把整个目录压缩发过去解压后运行 run_all_protocols.m 即可。6.4 文件命名和版本标记迭代调参过程中我遇到过两次更新了函数逻辑却忘记同步改文件名最后报告里的数据用了旧版本跑的问题。现在所有协议函数都在注释块第一行写版本变更记录文件名在重要逻辑变化时带上 v2、v3 后缀。例如 slotted_aloha_v3.m 的注释写v3 修改了边界效应处理丢弃前5%仿真区段。运行脚本的同时把当前所有函数的版本号打印到命令窗口与 results.mat 一起留档才不会出现数据和代码对不上的情况。本文还有配套的精品资源点击获取