简介:这份文档面向5G网络优化工程师及无线通信学习者,系统梳理NR网络中上行控制信息(UCI)的承载机制与PUCCH设计要点,帮助读者理解调度请求、HARQ ACK/NACK及CSI等控制信令如何在物理上行控制信道上传输。资源为1个docx文件,压缩包约18KB,内容以表格与参数对照为主,便于快速查阅。文档重点讲解PUCCH五种格式:Format 0与1适用于短UCI并支持同一PRB内UE复用,Format 2采用频率分集且不支持复用,Format 3与4面向大UCI负载并具备不同程度的复用与跳频能力;同时依据3GPP TS 38.300-5.3.3列出各格式的UCI比特长度、符号数、起始PRB、循环移位、时域OCC、跳频及最大码率等参数差异。已有369人学习下载,适合需要掌握PUCCH格式选型、优化上行资源利用率与调度策略的读者参考。
1. 5G(NR) 里的 UCI:从一条控制信息看终端与基站的对话方式
做 5G 终端或基站侧调试的人,迟早会碰到一个绕不开的东西:UCI。你在抓 PUCCH 日志、看 PUSCH 上的 CSI 上报、排查调度异常时,背后几乎都有它在起作用。UCI 全称 Uplink Control Information,上行控制信息,是 NR 终端向 gNB 反馈“我这边情况如何”的核心载体。它承载 HARQ 反馈、CSI 报告、调度请求这几类关键内容,直接决定基站下一拍怎么给你分配资源。很多人把 5G 峰值速率计算公式背得滚瓜烂熟,却在实际链路里发现速率上不去,问题往往就出在 UCI 的配置或时序上。这篇笔记面向做 5G 协议栈、终端射频、基站测试的工程师,把 UCI 是什么、在哪些信道上发、参数怎么配、坑在哪,一层层拆开讲清楚,让你看完能自己动手复现一套 UCI 的配置与验证流程。
2. UCI 到底装了什么:三类内容与两条上行信道
2.1 HARQ-ACK、CSI、SR:UCI 的三类核心载荷
UCI 不是单一消息,而是一组上行控制信息的统称。按 3GPP 的框架,它主要装三类东西。
第一类是 HARQ-ACK,也就是混合自动重传请求的确认反馈。基站通过 PDSCH 给你发下行数据,你收到后要告诉它“收到没收到、解对没解对”。这个反馈就是 HARQ-ACK,一个比特对应一个 TB 或一个 CBG。它是闭环重传的命脉,反馈晚了或错了,基站要么盲目重传浪费资源,要么误判链路质量。
第二类是 CSI,信道状态信息。终端测量下行参考信号后,把 CQI、PMI、RI、LI 等指标上报给基站。基站靠这些信息决定给你用什么调制编码策略、几层 MIMO、预编码矩阵怎么选。CSI 报得准不准、报得及不及时,直接决定下行吞吐。很多现场“信号满格但速率低”的玄学问题,根子就在 CSI 上报周期和精度上。
第三类是 SR,调度请求。当终端有上行数据要发但还没拿到上行授权时,通过 SR 向基站“举手”要资源。SR 本身只占一个比特,但它的配置周期和时机直接影响上行时延。
这三类内容可以单独发,也可以复用在一起发。理解它们的优先级和复用规则,是读懂 UCI 的第一步。
2.2 PUCCH 与 PUSCH:UCI 的两条承载路径
UCI 不能凭空发出去,它必须寄生在上行物理信道上。NR 里承载 UCI 的信道有两个:PUCCH 和 PUSCH。
PUCCH 是专门给 UCI 用的上行控制信道,不传用户数据。它分多种格式(Format 0 到 Format 4),不同格式对应不同的载荷大小和符号长度。Format 0 和 1 适合小载荷(1 到 2 比特),Format 2、3、4 适合大载荷(CSI 报告可能几十上百比特)。选哪个格式,取决于你要发多少比特、覆盖条件如何、是否要做跳频。
PUSCH 本来是传用户数据的,但当 UCI 和上行数据在同一时隙撞车时,UCI 会被复用到 PUSCH 上一起发。这叫 UCI on PUSCH。复用有严格的规则:HARQ-ACK 会打孔或速率匹配到数据里,CSI 按 beta 偏移量插入,SR 则通常不单独在 PUSCH 上发。复用做得好,省资源;做得不好,数据解调直接翻车。
提示:判断一条 UCI 走 PUCCH 还是 PUSCH,先看同一时隙有没有上行授权。有授权且满足复用条件,就复用到 PUSCH;没有授权,才走 PUCCH。
2.3 用一张表看清 UCI 类型与信道的对应关系
| UCI 类型 | 典型载荷 | 首选信道 | 复用信道 | 关键参数 |
|---|---|---|---|---|
| HARQ-ACK | 1~2 bit(单 TB) | PUCCH Format 0/1 | PUSCH | K1、PUCCH 资源索引 |
| CSI Part 1 | 固定比特 | PUCCH Format 2 | PUSCH | 上报周期、beta 偏移 |
| CSI Part 2 | 可变比特 | PUCCH Format 2/3/4 | PUSCH | 码本配置、子带大小 |
| SR | 1 bit | PUCCH Format 0/1 | 一般不复用 | SR 周期、SR 资源 ID |
这张表是我在配 UCI 时最常翻的对照。它帮你快速定位:手上这条 UCI 该走哪条路,涉及哪些参数。实际配置里,K1(PDSCH 到 HARQ-ACK 的时隙偏移)和 PUCCH 资源索引是最容易配错的两个,后面会专门讲。
3. 动手配一套 UCI:从参数规划到日志验证
3.1 先规划 PUCCH 资源集与 SR 周期
配置 UCI 的第一步不是写代码,而是规划资源。你需要确定几件事:PUCCH 资源集里放几个资源、每个资源用什么格式、SR 周期设多少、CSI 上报周期设多少。
常见做法是:给 HARQ-ACK 配 2 到 4 个 PUCCH 资源,覆盖不同载荷大小;给 SR 单独配一个资源,周期根据业务时延要求定,比如 10ms 或 20ms;CSI 上报周期根据下行调度需求定,周期越短越准但开销越大。
下面是一段用 Python 描述 PUCCH 资源规划的示例,模拟配置生成逻辑:
# PUCCH 资源集规划示例 # 定义每个 PUCCH 资源的格式、起始符号、符号数和 PRB 偏移 pucch_resource_set = [ {"res_id": 0, "format": 0, "start_symbol": 12, "n_sym": 2, "prb_offset": 0}, {"res_id": 1, "format": 1, "start_symbol": 10, "n_sym": 4, "prb_offset": 0}, {"res_id": 2, "format": 2, "start_symbol": 4, "n_sym": 8, "prb_offset": 3}, {"res_id": 3, "format": 3, "start_symbol": 0, "n_sym": 14, "prb_offset": 5}, ] # SR 配置:周期 20ms,偏移 5 个时隙 sr_config = {"sr_period_ms": 20, "sr_offset_slot": 5, "sr_res_id": 0} # CSI 上报配置:周期 40ms,使用 PUCCH Format 2 csi_config = {"csi_period_ms": 40, "csi_format": 2, "csi_res_id": 2} def validate_resource_set(res_set): """检查资源集是否有格式覆盖小载荷和大载荷""" formats = [r["format"] for r in res_set] has_small = any(f in (0, 1) for f in formats) has_large = any(f in (2, 3, 4) for f in formats) if not (has_small and has_large): print("警告:资源集未同时覆盖小载荷和大载荷格式") else: print("资源集格式覆盖检查通过") validate_resource_set(pucch_resource_set) print("SR 配置:", sr_config) print("CSI 配置:", csi_config)这段代码的逻辑很直白:先定义资源集,再定义 SR 和 CSI 的周期参数,最后做一个覆盖性检查。参数说明上,start_symbol和n_sym决定 PUCCH 在时隙里占哪几个符号,prb_offset决定频域位置。实际配置时,这些值要和基站的调度器对齐,否则会出现终端发了但基站没在对应位置收的情况。
3.2 用命令行工具抓 PUCCH 与 PUSCH 日志
规划完参数,下一步是验证。如果你在 OAI 或类似开源 5G 协议栈上做实验,可以用命令行抓日志。常见做法是启动 gNB 和 nrUE 后,用日志级别控制输出,过滤出 UCI 相关的内容。
# 启动 gNB,开启 PHY 和 MAC 层日志 sudo ./nr-softmodem -O gnb.conf --log_config.phy_log_level debug \ --log_config.mac_log_level debug 2>&1 | tee gnb_uci.log # 另开终端启动 nrUE sudo ./nr-uesoftmodem -O ue.conf --log_config.phy_log_level debug 2>&1 | tee ue_uci.log # 过滤 UCI 相关日志行 grep -iE "UCI|PUCCH|HARQ-ACK|SR|CSI" ue_uci.log | head -50这段命令的关键在日志级别和过滤词。phy_log_level debug会把物理层的细节打出来,包括 PUCCH 的格式、资源索引、HARQ-ACK 的比特值。grep过滤时,UCI、PUCCH、HARQ-ACK、SR、CSI 这几个词能覆盖大部分相关行。如果你看到 HARQ-ACK 的比特一直是 NACK,先别急着怀疑射频,去查 K1 配置和 PDSCH 的解调结果。
参数说明:-O指定配置文件,--log_config控制日志级别。不同版本的 OAI 参数名可能略有差异,以你本地--help输出为准。
3.3 验证 HARQ-ACK 时序:K1 到底怎么算
K1 是 PDSCH 到对应 HARQ-ACK 的时隙偏移。它决定终端在收到下行数据后,隔几个时隙才反馈。K1 配小了,终端还没解调完就要发反馈,只能报 NACK;K1 配大了,重传时延增加,吞吐下降。
计算 K1 时,要考虑终端处理时延(N1)、时序提前量(TA)和调度时隙。常见做法是:先按协议最小处理时延定一个基准值,再根据实测的 PDSCH 解调时间微调。下面是一个简单的 K1 校验脚本:
def check_k1(k1_slots, n1_symbols, slot_symbols=14, ta_slots=0): """ 校验 K1 是否满足最小处理时延要求 k1_slots: 配置的 K1 值(时隙) n1_symbols: 终端最小处理时延(符号) slot_symbols: 每时隙符号数,常规 CP 为 14 ta_slots: 时序提前量折算的时隙数 """ n1_slots = n1_symbols / slot_symbols min_k1 = n1_slots + ta_slots if k1_slots < min_k1: print(f"K1={k1_slots} 小于最小要求 {min_k1:.2f},可能导致 NACK") else: print(f"K1={k1_slots} 满足要求,余量 {k1_slots - min_k1:.2f} 时隙") # 示例:N1=8 符号,TA 折算 0.5 时隙,配置 K1=2 check_k1(k1_slots=2, n1_symbols=8, ta_slots=0.5)逻辑说明:把 N1 从符号折算成时隙,加上 TA 折算值,得到最小 K1。配置值小于它就会出问题。参数上,N1 取决于终端能力(capability),不同 UE 不一样,查你设备的 capability 信令就能拿到。TA 折算要看小区半径和子载波间隔。
注意:K1 不是越大越好。K1 增大虽然给终端更多处理时间,但会拉长 HARQ 往返时延,影响上行调度效率。找到满足 N1 的最小值附近,通常是最优的。
4. UCI 配置里最容易翻车的五个地方
4.1 PUCCH 资源冲突导致 HARQ-ACK 丢失
现象:终端日志显示发了 HARQ-ACK,但基站侧收不到,重传率居高不下。
原因:多个 UE 的 PUCCH 资源在频域或码域上撞了,基站解调时互相干扰。或者同一个 UE 的 SR 资源和 HARQ-ACK 资源配到了同一个 PRB。
解决:检查 PUCCH 资源集的 PRB 偏移和循环移位配置,确保不同 UE 之间正交。同一 UE 的 SR 和 HARQ-ACK 资源要分开。用基站侧的 PUCCH 接收功率日志确认是否有干扰。
4.2 CSI 上报周期与下行调度不匹配
现象:下行速率波动大,CQI 上报值长期偏低或偏高。
原因:CSI 上报周期设得太长,基站拿到的信道信息已经过期;或者 CSI 的 beta 偏移量设得太小,CSI 在 PUSCH 上被数据挤掉。
解决:把 CSI 周期调到和下行调度周期匹配,一般 20ms 到 40ms 是常见起点。检查 PUSCH 上 CSI 的 beta 偏移,确保 CSI 有足够的编码增益。对比基站侧记录的 CQI 和终端上报的 CQI,偏差大就调周期。
4.3 SR 周期过长导致上行时延飙升
现象:上行数据要等很久才发出去,时延测试不达标。
原因:SR 周期设得太大,比如 40ms 甚至 80ms,终端有数据时要等下一个 SR 机会才能请求资源。
解决:根据业务时延要求缩短 SR 周期。eMBB 场景 10ms 到 20ms 通常够用,URLLC 场景要更短。但 SR 周期太短会增加 PUCCH 开销,要在时延和容量之间权衡。
4.4 UCI on PUSCH 复用时的打孔规则搞错
现象:PUSCH 上的数据解调错误率高,尤其是 HARQ-ACK 比特多的时隙。
原因:HARQ-ACK 复用到 PUSCH 时,打孔位置算错,把数据的关键比特打掉了。或者 CSI 的插入位置和 HARQ-ACK 重叠。
解决:严格按协议规定的复用顺序排列:先放 HARQ-ACK,再放 CSI Part 1,最后放 CSI Part 2。检查打孔后的有效编码速率,确保不超过阈值。用链路级仿真验证复用后的 BLER。
4.5 K1 配置忽略了终端能力差异
现象:同一小区里,部分 UE 的 HARQ-ACK 正常,部分 UE 一直 NACK。
原因:不同 UE 的 N1 处理能力不同,用同一个 K1 值,能力弱的 UE 来不及处理。
解决:查每个 UE 的 capability 信令,按最弱 UE 的 N1 来定 K1,或者给不同 UE 配不同的 K1 集合。在调度器里做区分,别一刀切。
5. 把 UCI 验证做成可复现的自动化流程
前面讲的都是单点配置和排查。实际项目里,UCI 的问题往往在版本迭代或参数调整后重新出现。我后来养成的习惯是:把 UCI 的关键检查做成一个自动化脚本,每次改配置后跑一遍,省得反复踩同样的坑。
具体做法是:用 Python 读取配置文件,提取 PUCCH 资源集、SR 周期、CSI 周期、K1 值,然后逐项做规则校验。校验规则包括:资源集是否覆盖小载荷和大载荷格式、SR 周期是否在合理范围、K1 是否满足最小处理时延、CSI beta 偏移是否在有效区间。任何一项不通过就报错,并打印出具体参数和建议值。
import json def load_config(path): with open(path, "r") as f: return json.load(f) def validate_uci_config(cfg): errors = [] # 检查 PUCCH 资源集格式覆盖 formats = [r["format"] for r in cfg["pucch_resource_set"]] if not any(f in (0, 1) for f in formats): errors.append("缺少小载荷 PUCCH 格式(0/1)") if not any(f in (2, 3, 4) for f in formats): errors.append("缺少大载荷 PUCCH 格式(2/3/4)") # 检查 SR 周期 if cfg["sr_period_ms"] > 40: errors.append(f"SR 周期 {cfg['sr_period_ms']}ms 偏大,建议不超过 40ms") # 检查 K1 n1_slots = cfg["n1_symbols"] / 14 if cfg["k1_slots"] < n1_slots: errors.append(f"K1={cfg['k1_slots']} 小于 N1 折算 {n1_slots:.2f} 时隙") # 检查 CSI beta 偏移 if not (1.0 <= cfg["csi_beta_offset"] <= 20.0): errors.append(f"CSI beta 偏移 {cfg['csi_beta_offset']} 超出常见范围") return errors # 示例配置 cfg = { "pucch_resource_set": [ {"res_id": 0, "format": 0}, {"res_id": 1, "format": 2}, ], "sr_period_ms": 20, "k1_slots": 2, "n1_symbols": 8, "csi_beta_offset": 5.0, } errs = validate_uci_config(cfg) if errs: for e in errs: print("配置问题:", e) else: print("UCI 配置校验通过")这个脚本的价值在于:把散落在协议文档和血泪经验里的规则,固化成可执行的检查项。每次改配置先跑它,能挡掉大部分低级错误。参数上,n1_symbols从 UE capability 取,csi_beta_offset的合理范围参考协议表格,不同 CSI 比特数对应不同 beta 值。
再进一步,可以把日志解析也加进来。抓完 PUCCH 和 PUSCH 日志后,用正则提取 HARQ-ACK 的 ACK/NACK 比例、CSI 上报的 CQI 分布、SR 的触发次数,和配置校验结果一起输出成一份报告。这样每次版本迭代,UCI 的健康状况一目了然。
我自己的习惯是:新配一套 UCI 参数,先跑配置校验,再跑一轮短时间的业务测试抓日志,最后看报告里的 ACK 比例和 CQI 分布是否正常。三步都过了,才认为这套配置可以上测试床。这个流程帮我省了很多后悔药,也让我在排查 UCI 问题时不再靠猜。希望帮到你。
本文还有配套的精品资源,点击获取