新闻详情

新闻详情

首页 / 资讯中心 / 详情

Polar码SCL与BP译码实现:function节点抽象与工程调试

发布时间:2026/10/1 1:48:23来源:尧图网络
Polar码SCL与BP译码实现:function节点抽象与工程调试
简介面向通信与信息处理领域的极化码译码算法MATLAB实现包聚焦逐位消去SC、置信传播BP、列表消去SCL三种主流译码方案并集成循环冗余校验CRC以提升整体可靠性。压缩包共31个文件全部为MATLAB的.m脚本文件包含三种译码主程序以及路径管理、对数域计算、CRC校验等配套子函数模块划分清晰便于按函数逐段研读。目前已有773人学习下载适合信道编码方向的研究人员、学生及工程师用来进行原理验证、算法性能对比和完整仿真链路搭建。借助此包可快速运行核心译码程序直观理解SC的逐位判定、BP的消息传递以及SCL的列表搜索差异同时通过CRC辅助检查观察纠错效果进而可修改参数、替换信道模型或扩展新算法支撑课程设计、毕业设计乃至科研预研。全部源码采用MATLAB编写包体仅16KB轻量便捷可直接放入本地实验环境运行。1. function_polar 到底在解决什么问题SCL 和 BP 不是两套代码function_polar 这个命名我第一反应是“把 Polar 码译码器里的 function 节点单独抽出来”而不是一个完整库的名字。实际做下来你会发现SCL 译码和 BP 译码两套算法底层都在反复算同一个 f/g 节点SCL 靠它们做递归BP 靠它们做迭代。这个看起来不起眼的抽象能让一个工程同时产出两条性能曲线少维护一半代码。下面按我实际做过的路径把它拆成最小可运行实现、参数怎么调以及调试时真正会翻车的几个地方。适合通信仿真刚起步的人也适合想快速对比两套译码器性能的从业者。读完你能自己搭一个 N256 的 AWGN 仿真不依赖任何第三方库。2. SCL 和 BP 的原理差异为什么同一个码要两套译码器极化码构造出来之后最初的译码器是 SC串行抵消它按冻结位和信息位的顺序一位一位判决。SC 在码长较短时性能不够好后来才分化出 SCL 和 BP 两条路。SCL 保留多条候选路径用列表换取性能BP 在因子图上做迭代用并行换取时延。两者处理的是同一个极化码却对“译码器”这件事做了完全不同的取舍。2.1 SCL 的本质串行比特决策只是留了后悔药SCL 的全称是 Successive Cancellation List串行抵消列表。它在 SC 的基础上把“每次选一条路走到底”改成“每步同时保留 L 条路”。SC 的问题是决策没有后悔药当前面某个信息位判错后面再做多少次正确的抵消计算都救不回来。SCL 的做法是把 0/1 两条分支都留下来各自继续往下算最后再按路径度量选一条。路径度量PMPath Metric是 SCL 的灵魂。常见实现在 LLR 域维护一个非负增量路径与当前比特硬判一致时 PM 增加很少不一致时增加接近该比特 LLR 的绝对值。最终 PM 最小的路径就是最大似然意义上的最佳候选。注意这里有一个很容易踩的细节冻结位不产生路径分裂直接按构造时给定的值继续走所以激活路径的数量只会在信息位翻倍。复杂度上SCL 是 O(L·N·logN)L 通常取 4 到 32。L 从 1 涨到 8性能提升非常明显从 16 涨到 32 提升开始变缓。工程里如果只看 BLERL8 往往已经够用L16 适合追求极限性能的场合。但 SCL 本身是串行的列表管理还要排序所以吞吐率不高硬件上不如 BP 友好。2.2 BP 的本质因子图上的消息迭代BP 译码把极化码展开成一张因子图图上每个基本处理单元就是一个 PE。消息在节点之间来回传递左侧消息 R 从冻结位先验出发向右传右侧消息 L 从信道 LLR 出发向左传。经过若干轮迭代后每个信息位的 LLR 稳定下来再做一次硬判决。BP 的计算原语和 SCL 的递归原语高度重合。每个 PE 在做的事情本质上是两个消息的合并对数和合并对应 f 节点带已知比特的修正合并对应 g 节点。区别只在调度SCL 是深度优先的树递归BP 是逐层刷新的平面迭代。迭代次数 T 是 BP 最重要的参数。太小收敛不充分太大在 min-sum 近似下会累积误差。常见范围是 20 到 60 次。和 SCL 对比BP 的并行度高适合硬件和 GPU 加速也能天然产出软信息缺点是中短码长时同样码率下 BP 的误块率通常比 SCL 加 CRC 差 0.3 到 0.5 dB。2.3 function 节点抽象两套译码器的公因数一旦把 f 和 g 抽成独立函数SCL 和 BP 就变成了同一组节点上的两种调度方式。我一般会建一个很薄的抽象层接口如下输入左右两个 LLR外加一个可选的已知比特输出合并后的 LLR。SCL 在递归时调用它BP 在迭代循环里调用它。下表是我习惯的抽象视角抽象层SCL 里的角色BP 里的角色f 节点递归计算上一层 LLR迭代中合并同层两路消息g 节点结合已判决比特修正 LLR结合另一侧消息做方向传递调度树状深度优先按 stage 平面遍历主要参数列表大小 L、CRC 长度迭代次数 T、调度方式、早停阈值把 function 节点抽出来的直接收益是你只需要维护一份经过充分测试的 f/g 实现SCL 和 BP 各自去调它。后面做联合译码比如先用 BP 输出软信息再喂给 SCL也只是多接一条线。这正是标题里 function_polar 这个命名的工程价值。3. SCL 译码实现f/g function 节点、路径度量与三个必调参数这一章直接给可运行的最小单元。完整译码树还需要按递归顺序逐层计算 LLR但下面这几个代码块是可以直接抄进自己工程的核心也是我调试时最容易改出问题的地方。3.1 先写对 function 节点f 和 g 的符号约定决定一切我这里统一用 LLR 表示对数似然比正值偏向判 0负值偏向判 1。BPSK 映射可以约定比特 0 对应 1 电平比特 1 对应 -1 电平信道 LLR 就是 2y/σ²。符号约定一旦乱了后面所有曲线都会对不上。import math def f_node(a: float, b: float) - float: min-sum 近似的对数和节点。 LLR 0 判 0LLR 0 判 1。 sign math.copysign(1.0, a) * math.copysign(1.0, b) return sign * min(abs(a), abs(b)) def g_node(a: float, b: float, u: int) - float: 结合已判决比特 u 的 g 节点。 u0 时等效 abu1 时等效 b-a。 return a (1 - 2 * u) * bf_node 用的是 max-log 近似。严格的对数和是 log(1exp(-|ab|)) 修正实测在 SCL 列表大小中等时只差零点几个分贝但 min-sum 的数值稳定性好很多不会在极端 LLR 下冒出 inf 参与后面的加法。g_node 里 u 是当前路径对某个比特的硬判决SCL 每条路径都有自己的 u所以 g_node 实际上是路径相关的 function 节点这也是 function_polar 里 function 二字的含义。3.2 路径度量更新必须留在 LLR 域路径度量的经典公式是累积 ln(1exp(-(1-2u)·llr))如果直接在概率域算概率连乘很快下溢到 0L8 时可能还勉强能跑L16 以上几乎必出 NaN。我在工程里全部改用近似形式增量只依赖当前比特的 LLR 和判决的一致性。def update_pm(pm_old: float, llr_bit: float, bit: int) - float: SCL 路径度量增量。 bit 与 llr_bit 同号时增量接近 0反号时增量接近 |llr_bit|。 return pm_old max(0.0, (1.0 - 2.0 * bit) * -llr_bit)这段代码的意义在于把概率域的乘法变成了 LLR 域的加法并且返回的 PM 永远非负递增。你不需要在每个比特上做 exp/log 切换也就少了一个常见的数值黑匣子坑。后面要在不改变行为的前提下做优化可以提前算好每个可能 llr_bit 对应的增量查表但先保证逻辑正确再说性能。3.3 列表管理翻倍、剪枝、冻结位跳过拿到某个比特位置的 LLR 之后SCL 要做的是扩展路径并剪枝。信息位每条路径分裂成两条冻结位不分裂。这个逻辑单独抽出来非常容易测也方便后续加 CRC 辅助选择。def extend_and_prune(paths, llr_bit, is_frozen, frozen_bit, list_size): paths: list of (pm, u_list) 返回扩展并剪枝后的前 list_size 条路径。 new_paths [] for pm, u_list in paths: if is_frozen: pm2 update_pm(pm, llr_bit, frozen_bit) new_paths.append((pm2, u_list [frozen_bit])) else: pm0 update_pm(pm, llr_bit, 0) pm1 update_pm(pm, llr_bit, 1) new_paths.append((pm0, u_list [0])) new_paths.append((pm1, u_list [1])) return sorted(new_paths, keylambda x: x[0])[:list_size]这段代码看起来简单但它是 SCL 的主干因为真正的递归译码树最终就是在每个叶子节点上调用它。注意冻结位分支里 frozen_bit 也能带来 PM 增量如果冻结位的 LLR 与构造时给定值不一致这条路径会被自然惩罚。这是对的BP 里的冻结位先验也是一样的逻辑。如果 list_size 偏大全量 sorted 的开销会排在解码时间的第一位。我一般会改成heapq.nsmallest(list_size, new_paths, keylambda x: x[0])路径扩展到上万条时差别非常明显。3.4 落地到完整译码器时只调三个参数第一个是列表大小 L。N256、码率 1/2 时L8 起步L16 够用L32 收益很小。第二个是 CRC 长度普通仿真推荐 8 到 16 bit太长会吃掉码率太短则选不出正确路径。第三个是冻结位位置最稳妥的做法是用高斯近似构造或者直接用 5G 标准里给好的位置文件不要自己随机挑。我见到的常见误用是把 CRC 位插在信息位中间导致 SCL 在 CRC 位上也分裂路径。正确做法是把 CRC 位当作普通信息位的一种但路径分裂时不对它做 0/1 扩张最终终选时用 CRC 校验过滤候选路径。这一点在避坑章节还会再展开。4. BP 译码实现消息初始化、迭代模板与统一接口BP 的实现比 SCL 更依赖因子图的具体布局所以我不会硬塞一个可能和你的节点编号对不上的完整译码器。这一章给的是工程上最常用的初始化方式、迭代调度思路以及一个让 SCL/BP 共用同一入口的接口模板。4.1 消息初始化冻结位先验和信道 LLR 怎么给BP 译码需要两组消息从左侧向右传的 R 消息从右侧向左传的 L 消息。R 的初始值来自冻结位先验L 的初始值来自信道。初始化做对后面迭代才有意义。import numpy as np def bp_init(ch_llr, frozen_pos, frozen_val, stage_count, prior_strength1e6): ch_llr: 信道 LLR长度 N frozen_pos: 冻结位索引列表 frozen_val: 每个冻结位对应的固定比特值 stage_count: log2(N) n len(ch_llr) L np.zeros((stage_count, n)) R np.zeros((stage_count, n)) L[-1, :] ch_llr for pos, val in zip(frozen_pos, frozen_val): R[0, pos] prior_strength if val 0 else -prior_strength return L, RL[-1, :] ch_llr是把信道信息放到因子图最右侧R[0, :]是因子图最左侧。冻结位先验用prior_strength1e6不要用np.inf。一旦消息里混入 infmin-sum 的取最小绝对值操作会让同一个 PE 的另一路也被污染之后整个迭代矩阵都会变成 NaN。信息位位置 R 初始给 0 即可第一次迭代后消息自然填充。4.2 迭代调度flooding 和 sequential 怎么选BP 的一次迭代要遍历所有 stage。flooding 调度是所有 stage 同一轮并行更新实现简单适合 numpy 批量算或者 GPU 加速sequential 调度是从某一侧开始逐层更新收敛更快但串行依赖强。中短码我一般先用 flooding 调通逻辑再考虑换 sequential 拉低迭代次数。每次迭代结束后可以对信息位做硬判决如果连续两次迭代的硬判决结果完全一致就提前终止。这个早停条件能省掉大约三分之一无效迭代。注意 min-sum 近似的 BP 在迭代后期可能出现 LLR 幅度震荡所以早停不要只看 LLR 绝对值要比较硬判决序列是否稳定。4.3 BP 的三个必调参数迭代次数 T 是最关键的。N256 时 T40 是一个稳妥起点往上加到 100 通常没有明显收益反而可能让误块率回弹。调度方式上面说过先 flooding 后 sequential性能曲线一致后再优化。最后是冻结位先验强度prior_strength取 1e5 到 1e6 都可以太小会让冻结位约束失效太大会在迭代初期把相邻消息拉爆。这里有一个经常被忽略的点BP 的硬判决输出天然是软信息信息位的 LLR 绝对值可以作为一种置信度参考。这个特性在最后一章做 BP 辅助 SCL 时会用到。4.4 把两套译码器收进同一个接口工程里同时维护 SCL 和 BP 时我习惯用一个小门面类把两者包起来主仿真脚本只关心 mode 参数。class PolarDecoder: def __init__(self, N, info_idx, frozen_idx, frozen_val): self.N N self.info_idx info_idx self.frozen_idx frozen_idx self.frozen_val frozen_val def decode(self, ch_llr, modescl, list_size8, max_iter40): if mode scl: return self._decode_scl(ch_llr, list_size) elif mode bp: return self._decode_bp(ch_llr, max_iter) else: raise ValueError(funknown mode: {mode})这段代码本身不实现具体算法但它让回归测试、蒙特卡洛仿真脚本都在同一入口上跑。后面想加一个联合译码模式只需要在 decode 里多开一个分支不用动上层的 SNR 扫描循环。5. 用起来才遇到的坑SCL/BP 译码实现常见问题排查理论跑通之后真正耗时间的是调试。下面五条都是我自己在 N256、码率 1/2 仿真里遇到过的问题每条按现象、原因、解决三步记录。5.1 路径度量在概率域下溢L 一大就整片 NaN现象L8 一切正常L 调到 16 后译码器开始输出 NaN误块率反而比 L4 还高。原因路径度量在概率域做连乘候选路径翻倍后浮点下溢也有人把 update_pm 写成了pm np.log(...)在 LLR 极端值时对数参数趋近 0 造成 NaN。解决路径度量统一走 LLR 域近似增量也就是第 3.2 节的update_pm。不要为了追求理论上的“精确”而把公式换回概率域min-sum 近似在 SCL 列表场景下性能损失很小数值收益却非常明显。5.2 BP 迭代次数加得越大误码率反而越高现象T 从 40 提到 200BLER 从 4% 涨到 9%曲线明显变差。原因min-sum 近似在迭代中不断累积误差后期消息震荡硬判决被往错误方向推。解决固定 T 在 40 到 60或加早停条件。早停用连续两次硬判决序列一致作为判据这样既保住性能又省迭代时间。也不要为了对比把 BP 的 T 和 SCL 的 L 硬凑成同样数值二者没有一一对应关系。5.3 f/g 节点符号方向写反SCL 能跑但 BP 完全不对现象SCL 在 L1 时曲线正常BP 无论怎么调 T 都输出接近随机的结果。原因SCL 对某些符号错误有容忍因为路径度量会吸收方向偏差BP 是迭代网络方向错了会在多轮更新里被指数级放大。解决先拿 N2 的最小因子图手算一组 LLR把 f/g 节点在这个最小用例上的输入输出抄下来写成一个 pytest 用例。以后每次改 function 节点都先跑这个用例再跑整体仿真。这个习惯帮我少翻了至少三次车。5.4 冻结位列表版本不一致曲线差 0.5 dB 找不出原因现象本地复现论文N256 曲线在低信噪比段比论文差 0.5 dB 左右调列表大小、迭代次数都不见好转。原因发送端用的是 5G 标准位置表接收端用的是另一个版本的 Gaussian Approximation 构造结果两边冻结位不完全相同。Polar 码对齐的是接收端已知的冻结位而不是双方共同的计算历史。解决把构造结果落成一个文件.npy或.csv都行发射端调制脚本、SCL 译码器、BP 译码器都从同一个文件读冻结位列表。这一步看起来多余但能杜绝一个非常隐蔽的复现问题。5.5 CRC 位位置放错加了 CRC 后性能反而变差现象加了 16 bit CRC 后SCL 的 BLER 比不带 CRC 还要高而且主要崩溃在 CRC 位附近的信息位。原因CRC 作为冗余比特不应该参与路径分裂。如果把 CRC 位夹在信息位中间SCL 会在这些位置上继续做 0/1 分裂白白消耗列表容量最后 CRC 校验反而把正确路径过滤掉了。解决CRC 固定放尾部。实现上把 CRC 位当作一种“不分裂的伪冻结位”参与 PM 计算但不扩张路径只在最终选取路径时做 CRC 校验。6. 进阶让 BP 的软输出给 SCL 打分再用回归脚本守住 function 节点下限SCL 和 BP 如果能互相利用价值比单独调参更大。一个务实的做法是先跑 BP把迭代稳定后的信息位 LLR 拿出来和信道 LLR 做加权融合再用融合后的软信息喂给 SCL。这样做不能保证每一次都变好但处理突发深衰落时BP 提供的先验可以帮 SCL 把正确路径留在列表尾部而不是被一次噪声拉偏的 PM 淘汰掉。权重 α 我用信道 LLR 占比 0.6 到 0.8BP 软输出占比 0.2 到 0.4在不同 SNR 下做一次小网格搜索。注意这里的软信息不是严格意义上的译码先验所以权重设置属于工程经验不要套用教科书里的概率公式。def regression_sanity(snr_list, n_trials1000): for snr in snr_list: bler_scl run_mc(scl, snr, list_size8) bler_bp run_mc(bp, snr, max_iter40) # 中短码下 SCL 应当明显好于或至少不差于 BP assert bler_scl bler_bp 0.05, fFAIL at SNR{snr} print(PASS, snr, round(bler_scl, 4), round(bler_bp, 4))这份回归脚本固定随机种子、固定信噪比列表每天改动 function 节点后跑一遍。f/g 节点是两套译码器的公共底座只要回归断言不崩上层的大部分问题都能被快速定位。还有一个零成本 sanity check 我每换一个码长都会先做拿 N64、K32 的译码器和未编码 BPSK 对比。如果译出码字的误块率在 4 dB 时都没有明显低于未编码 BPSK 的误码率说明代码里还有逻辑 bug先别急着调列表大小或迭代次数回去检查消息方向。我现在的习惯是把冻结位位置单独存成 JSON仿真脚本固定 seed每次改动只碰 function 层。f/g 先过 N2 最小用例再跑回归最后才上蒙特卡洛。这套流程走顺之后SCL 和 BP 的踩坑时间明显下降希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Java Builder 模式实战指南:基于 java-design-patterns 仓库的 Hero 构建器深度解析 2026/10/1 2:43:17

Java Builder 模式实战指南:基于 java-design-patterns 仓库的 Hero 构建器深度解析

示例工程教程 【免费下载链接】java-design-patterns Design patterns implemented in Java 项目地址: https://gitcode.com/GitHub_Trending/ja/java-design-patterns 点击查看 免费下载 Builder(构建器)模式是 Gang of Four 提出的经典创建…

阅读更多 →
嵌入式Bootloader与OTA面试核心考点:从原理到代码实现 2026/10/1 2:43:17

嵌入式Bootloader与OTA面试核心考点:从原理到代码实现

二、A/B分区设计:解决固件大小不匹配问题 A/B分区是OTA升级的基础架构,核心是设计固定大小的双分区,避免固件大小差异导致的启动失败。 1. 分区设计原理 固定分区:设计时确定A、B分区的起始地址和大小,编译脚本将新固件…

阅读更多 →
AI出题工具Quizgecko:英语老师备课提效的全流程实战指南 2026/10/1 2:43:16

AI出题工具Quizgecko:英语老师备课提效的全流程实战指南

1. 从备课到出题,Quizgecko到底解决了英语老师的什么痛点带过几年英语课的老师都懂,日常工作中最耗心力的不是站在讲台上那四十五分钟,而是讲台下的备课。尤其是出题这件事——单元测试要出,周测要出,期中期末更要出&a…

阅读更多 →
深度学习必备网址汇总:从入门到工业落地的资源导航 2026/10/1 2:43:16

深度学习必备网址汇总:从入门到工业落地的资源导航

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
2026年AI工业控制系统搭建指南:三层架构、OPC UA与边缘计算实战 2026/10/1 2:43:16

2026年AI工业控制系统搭建指南:三层架构、OPC UA与边缘计算实战

1. 2026年AI工业控制系统的真实面貌1.1 先搞清楚“AI工业控制系统”到底指什么“2026 AI工业控制系统”这个说法听起来很唬人,但拆开来看,它并不是一个全新的、要取代PLC和DCS的怪物。我在多个非标自动化项目里摸爬滚打这些年,最深的体会是&a…

阅读更多 →
具身智能商业化逻辑(2):各系统之间的接口协议研究 2026/10/1 2:43:10

具身智能商业化逻辑(2):各系统之间的接口协议研究

前沿技术探索:TVA智能体(简称TVA) TVA智能体(亦称“AI智能体视觉”或“TVA视觉智能体”)是依托Transformer架构与“因式智能体”理论构建的通用视觉技术体系。它有机融合深度强化学习(DRL)、卷积神经网络(CNN)与因式分解算法(FRA),构成了具身智能的核心视觉中枢(…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉