简介该仿真程序基于三边定位算法面向可见光通信与数字信号处理方向的研究者和开发者用于验证室内定位方案。压缩包共2个文件均为MATLAB脚本体积仅2KB包含定位主程序和配套执行文件结构精简便于直接运行或按需修改。目前已有852人学习下载。程序覆盖发射端LED光信号建模、接收端解调与噪声抑制、以及三边定位核心模块完整实现距离估计、坐标计算和误差校正流程。距离估计可选时间差或强度衰减方式坐标计算利用三角几何并配以最小二乘或非线性优化误差校正则可集成卡尔曼滤波或粒子滤波有助于深入理解调制解调、信号恢复等数字信号处理环节。该实现还可作为智能建筑、物联网设备及导航系统的定位算法参考适合光通信定位、算法仿真等各类研究与教学场景。1. 可见光通信室内定位仿真三边定位算法能落到什么精度做室内定位的人多半有过这种体验蓝牙、WiFi、UWB 都试了一圈不是指纹库维护成本高就是硬件价格劝退。可见光通信VLC室内定位的切入点很直接——把天花板上本来就要装的 LED 当成基站用光电二极管接收光信号既完成照明又顺带把位置算出来。这套思路在仿真里跑通并不难难的是把“测距—解算—误差校正”整条链路在 MATLAB 里串起来并且知道哪一步在拖精度后腿。这篇笔记要拆的资源是一套基于三边定位算法的可见光通信室内定位仿真程序压缩包里是 positioning.m 与 Positioning_main.m 两个脚本适合正在做课程设计、毕设预研或者想快速验证 VLC 定位方案的研究者。它给你的不是现成产品而是一套可以改参数、换噪声模型、看误差分布的参考实现。2. 定位链路与距离估计RSS 和 TOA 怎么选出三个距离值2.1 VLC 定位链路的发射端与接收端构成先把整条链路看明白后面调参才有方向。VLC 室内定位系统分成四段信源编码与调制、LED 驱动与光发射、自由空间光信道、光电接收与数字信号处理。在发射端LED 并不只是发光它还要把信息调制到光强度上。常见的做法是强度调制直接检测IM/DD也就是用电流驱动 LED让光功率跟随信号变化。为了让照明不闪烁调制频率要高于人眼可感知的范围一般至少几百 kHz实际系统里常做到 MHz 级别。仿真程序里通常不会把 PWM 波形画出来而是直接用光功率、调制指数、LED 数量这些参数去等效。接收端则是光电二极管PD加跨阻放大器把光信号变成电压信号再经过 ADC 采样送入数字信号处理模块。数字信号处理在这里干的活包括解调、去噪声、从接收信号里估计距离。很多人拿到仿真程序先去看定位解算代码这是不对的——定位误差的根子往往在前面距离估得不准后面三边定位算得再精细也是白搭。2.2 RSS 测距的传播模型与噪声敏感性RSS接收信号强度测距是 VLC 定位里最省事的方案不需要时间同步只要把接收光功率和距离的关系标定好就行。VLC 信道常用朗伯辐射模型描述LED 通常近似为一阶或二阶朗伯辐射体。接收功率大致可以写成接收功率 发射功率 × (朗伯阶数 1) / (2π × 距离平方) × 发射角方向系数 × 接收角方向系数 × 接收面积室内定位默认 LED 和 PD 都朝下安装发射角和入射角可以近似看成同一个角度这个角度的余弦恰好等于垂直高度除以真实距离。把几何关系代入后会发现接收功率与距离的高次方成反比这意味着距离越大光功率衰减越剧烈。RSS 测距的优点是实现简单缺点是模型误差大。仿真中如果只用自由空间路径损耗不考虑墙面反射和遮挡算出来的距离会偏乐观而实际环境中桌面反光、灯具老化、PD 朝向偏移都会改变接收功率。所以跑仿真时不要盯着理想曲线看要主动往接收功率里加百分比噪声比如 5% 到 10% 的随机扰动观察定位误差如何放大。2.3 TOA/TDOA 测距的同步条件TOA到达时间测距通过测量光信号从 LED 到 PD 的飞行时间来算距离。光速是 3×10^8 米每秒室内距离只有几米飞行时间只有十几纳秒。要在仿真里体现 TOA 测距采样率至少要 GHz 级别这对 ADC 是非常大的压力实际系统很少在消费级硬件上做纯 TOA。更现实的是 TDOA到达时间差它不要求 LED 和 PD 之间绝对同步只要多个 LED 之间同步即可。但 LED 之间的时钟同步同样需要专门机制去保证。在 MATLAB 仿真程序里TOA 测距通常会简化成理想定时加上一个高斯时间误差再乘光速转成距离误差。要留意的是如果你看到仿真的定位精度特别高先看它的时间误差设了多少——ns 级别的误差在室内定位里几乎不可实现仿真参数要往实际硬件水平靠。测距方式是否需硬件同步误差主来源仿真实现复杂度精度潜力RSS不需要反射、遮挡、器件非线性低中TOA收发端严格同步时钟偏差、多径中高高TDOA发射端同步即可基站间同步精度中较高2.4 三边定位的几何求交与最小二乘求解拿到三个 LED 到接收机的距离后定位问题就变成一个几何问题已知三个圆心LED 坐标和三个半径测量距离求交点。理想情况下三个圆交于一点但仿真和实测里几乎没有这种好事——测距误差、坐标误差、模型误差都会让三个圆交成一个区域甚至根本不交。教科书里常用解析几何求解做法是把其中一个圆作为参考圆用另外两个圆与参考圆的差值消去二次项变成线性方程组。设 LED 坐标为 (x_i, y_i)测量距离为 d_i以第一个 LED 为参考可以得到2(x_i - x_1)Δx 2(y_i - y_1)Δy d_1² - d_i² x_i² y_i² - x_1² - y_1²这里有一个工程上的关键选择不要用两个差值方程直接解二元一次方程组而应该把三个以上的 LED 数据全部放进最小二乘框架里。原因很简单两个方程刚好求出唯一解但任何测距误差都会完整保留在结果里而三个以上方程时最小二乘会把误差按平方和最小的原则分配到多个方向上定位结果稳定性明显好。用 MATLAB 实现时核心就是 mldivide 的左除操作不需要手写矩阵求逆。3. 仿真程序的主流程与函数划分positioning.m 怎么调用3.1 主脚本与核心功能函数的职责划分拿到压缩包后先别急着整体运行把两个文件的职责先分开。最合理的结构通常是这样位置函数是核心算法函数负责“输入 LED 坐标和距离输出估计坐标”主脚本负责“配置参数、生成测试点、叠加噪声、循环调用位置函数、统计误差”。我在给参考程序做复刻时最常用的做法是让位置函数保持纯计算属性不掺任何随机因素。这样便于单元测试也方便对比不同测距噪声下的表现。举个例子位置函数我一般写成这样function [pos] positioning(led_xyz, dist_meas) % led_xyz: 3x3 矩阵每行是一个 LED 的三维坐标 % dist_meas: 3x1 向量三个 LED 到接收机的测量距离 % pos: 1x2 向量估算出的接收机二维坐标 A 2 * (led_xyz(2:end, 1:2) - led_xyz(1, 1:2)); b dist_meas(1)^2 - dist_meas(2:end).^2 ... sum(led_xyz(2:end, 1:2).^2, 2) ... - sum(led_xyz(1:2, 1:2).^2); pos (A * A) \ (A * b); pos pos; end这段代码的逻辑是先把第二、第三个 LED 与第一个 LED 的坐标差构造矩阵再算右侧常数项。最后一个左除操作完成最小二乘求解。需要留意如果只需要二维定位LED 的 z 坐标不参与求解矩阵只用 x、y如果做三维定位LED 数量至少得四个且 A 矩阵的构造要延伸到三维。主脚本里最要紧的是随机数种子和噪声模型。很多翻车现场都源于没设随机种子每次运行结果都不同调参数时根本看不出是参数变化带来的影响还是随机波动。3.2 房间、LED 布局与接收机网格的构建室内定位仿真第一步要布设房间。常见做法是把房间定义成一个 4m × 4m × 3m 的空间LED 安装在天花板接收机放在离地面 0.85m 左右的高度这个高度参考了桌面设备或者手持终端的典型位置。LED 的数量和位置直接影响定位精度覆盖范围。布局方面三颗 LED 是最低配置排成三角形覆盖房间中心区域。但要注意三角形布局在房间边缘误差会迅速变大原因后面避坑章会展开。如果希望整个房间的误差更均匀四颗 LED 按矩形或菱形布局是更稳妥的方案。主脚本里一般会有类似这样的参数块% 房间尺寸与 LED 位置 room_size [4, 4, 3]; % 长宽高 led_pos [0, 0, 3; ... % LED1 坐标 4, 0, 3; ... % LED2 坐标 2, 3.5, 3]; % LED3 坐标 z_pd 0.85; % 接收机高度接收机的测试方式有两种一是固定几个特殊位置点比如房间中心、角落、LED 正下方看单个点的定位误差二是把房间地面按 0.1m 间隔划分成网格每个网格点都做一次定位最后画出误差热力图。第二种方式更能暴露布局问题但计算量也更大建议先跑粗网格比如 0.5m 间隔确认参数合理后再加密。3.3 跑通一次定位的 MATLAB 代码骨架有了位置函数和参数主脚本就可以按蒙特卡洛方式统计定位误差了。下面这套骨架我在看参考程序时非常推荐的逻辑它把测距噪声彻底暴露出来clear; clc; rng(2025); % 固定随机种子保证结果可复现 % 三颗 LED 的天花板坐标 led [0 0 3; 4 0 3; 2 3.5 3]; target [2, 1.2, 0.85]; % 接收机真实位置 % 真实距离 d_true sqrt(sum((led - target).^2, 2)); d_meas d_true .* (1 0.05 * randn(3, 1)); % 距离加入 5% 高斯噪声 pos_estimated positioning(led, d_meas); err norm(pos_estimated - target(1:2)); fprintf(定位误差%.3f m\n, err);这段代码先用真实坐标算出三个 LED 到目标的距离然后给距离乘上一个加噪因子模拟 RSS 测距里接收功率波动带来的距离估计偏差。5% 的比例噪声在这个场景里算是比较温和的实际环境中由于反光和阴影距离误差比例可能会到 10% 以上。跑通一次只是第一步。单次结果没有统计意义必须做蒙特卡洛循环跑几百次把误差累积下来再统计 RMSE。这也是为什么主脚本里通常会带一个 for 循环的原因。要判断一个定位方案好坏至少得看三个指标平均误差、均方根误差RMSE、P95 误差。4. 参数调优与误差评估接口参数和 RMSE 这样盯4.1 几个影响定位精度的关键输入参数仿真程序不是拿来直接出结果的它是拿来回答“如果参数变成这样精度会怎样”这个问题。下面这几个参数是优先要动的。LED 发射功率直接决定了接收端的信噪比底子。功率提高 3dB接收信号幅度增加约 41%在噪声不变的情况下距离测量误差会明显下降。但仿真里有个陷阱发射功率提上去了接收机的前端饱和效应却没建模导致仿真结果偏乐观实际硬件里 PD 早就饱和了。LED 个数和布局是最值得调的。三颗 LED 能定位但精度分布完全由位置决定四颗 LED 才能给最小二乘提供冗余角落区域的误差能压下来不少。朗伯阶数也很关键这个参数描述 LED 发光的集中程度阶数越高光束越窄正下方信号强、周边信号弱距离估计对位置更敏感。还有一个常被忽略的参数是接收机高度。很多人默认固定 z 坐标但如果模拟的是手持设备高度变化会直接改变 LED 到 PD 的垂直距离进而改变接收功率。仿真时最好把接收机高度也当作变量扫一遍。4.2 给仿真叠加噪声信噪比与距离误差模型的取舍噪声模型选得对不对直接决定仿真结论有没有参考价值。最常见的做法是加高斯白噪声这个没有错但要分清楚是在哪个环节加。在信号域加噪声也就是在接收功率上叠加噪声是最接近物理实际的做法但如果你的仿真程序只提供距离域接口那就在距离上加比例噪声。我常用的信号域噪声加法是这样的思路先算理想接收功率加上热噪声和散粒噪声后换算回距离。热噪声的功率和带宽、跨阻增益有关散粒噪声和光电流有关。在简化仿真里可以用一个等效 SNR 来涵盖所有噪声源snr_db 30; % 等效信噪比 noise_power p_rx / (10^(snr_db / 10)); p_rx_noisy p_rx sqrt(noise_power) * randn(size(p_rx));用 SNR 控制噪声的好处是直观30dB 对应比较好但现实的链路20dB 已经开始反映比较恶劣的情况。坏处是它掩盖了噪声的频谱特性实空间里噪声往往不是平坦白噪声主要是 100Hz 的市电工频干扰。要模拟得更细可以在接收功率序列里加入 100Hz 正弦分量这比单纯加大噪声功率更能体现真实场景。4.3 定位结果的评估指标RMSE、P95 与误差分布评估指标上报告里只写一个平均误差是不严谨的。平均误差会被中间区域的高精度稀释角落的差表现完全看不出来。业界更认 RMSE 和 P95。RMSE 对大的离群误差敏感能够反映最坏情况的影响P95 则告诉你 95% 的定位点误差在多少米以内这个指标对导航类应用更实际。计算方式不复杂把所有蒙特卡洛误差排序取 95% 分位。如果 RMSE 和平均误差差得太大说明误差分布有肥尾也就是某些位置的定位严重失效。误差热力图是定位仿真里最直观的调试工具。用 surf 或 pcolor 把每个网格点的误差画出来马上就能看出盲区在哪里。正常情况是房间中心误差小、边缘误差大LED 正下方误差最小。如果热力图出现奇怪的规律比如某个 LED 正下方反而误差大优先怀疑距离模型的方向角计算错了。提示跑误差热力图前先单独检查三个 LED 的距离误差均值。如果某个 LED 的距离误差显著大于其他两个定位结果会被这个“差基站”拖垮此时调定位算法不如调这个 LED 的功率模型。5. 仿真常见问题与避坑四个典型翻车现场5.1 三个圆交不到一点最小二乘与测距失配现象跑定位时某些测试点算出的位置完全偏离真实坐标甚至跑到房间外面去。原因三个距离圆在几何上根本不相交或者交出的区域极大。RSS 测距误差大的时候测量距离和实际位置之间根本不满足三角不等式强行解析求解就会产生离谱结果。解决不要用手写的二元方程精确求解改用最小二乘。同时看看测距噪声是不是加得太大——有时候为了展示算法鲁棒性仿真里噪声加得过于夸张导致距离已经有了 30% 以上的误差这种条件下三边定位从数学上看本来就无能为力。我在这种情况下的习惯是把噪声比例降到 5% 左右先把主流程跑通再逐步加大噪声找极限。5.2 LED 正下方坐标抖动入射角模型病态现象把接收机放在某颗 LED 正下方理论上应该定位最准但蒙特卡洛统计出的误差反而不稳定有时比其他位置还要大。原因正下方时发射角和入射角都接近零度余弦值接近 1角度变化对接收功率的影响趋近于零。也就是说接收机在 LED 正下方附近移动时接收功率基本不变从功率反推距离几乎失效。这个区域在数学上叫病态区。解决别把单颗 LED 正下方当作精度保障点。实际部署时接收机出现在 LED 正下方的概率不高但如果你的应用场景是固定工位上方的灯具那就应该主动增加 LED 数量或者引入 PD 倾斜角测量。仿真里要验证这一点可以在 LED 正下方旁边密集撒点看误差分布是否出现原点附近的跳跃。5.3 数据量大但误差没变小网格划分与墙角盲区现象把接收机网格间隔从 0.5m 缩小到 0.1m计算结果更精确了但 RMSE 没什么实质改善。原因网格加密只增加了采样密度没有改变定位算法的客观误差来源。如果在墙角区域接收功率本身就弱、信噪比低网格再密墙角那个点的距离估计误差还是大。这是系统性问题不是统计量够不够的问题。解决先按区域分别统计误差。把房间分成中心区和边缘区分别看 RMSE如果边缘区误差三倍于中心区就要考虑改 LED 布局把靠墙的 LED 往中间挪一点或者增加一个墙角处的辅助 LED。这个结论不是玄学数学上就是病态几何加上低信噪比的双重效应。5.4 精度高得离谱坐标系与角度单位埋雷现象仿真结果 RMSE 低于 1 毫米比真实硬件高两个数量级一看就不对劲。原因最常见的是角度单位错误。MATLAB 的 cos、sin 默认用弧度如果手动输入的角度是度数没转弧度方向余弦就完全错了。另一种是坐标单位没统一LED 坐标用米接收机坐标却填了毫米代入公式后距离量级混乱。解决在参数区统一加注释强制所有长度用米、所有角度用弧度。顺手做一个合理性检查跑一个理想无噪声的定位估计输出应当和真实坐标完全相同偏差应该小于 1e-10。如果这个检查都过不了任何统计指标都没有意义。注意无噪声自检必须通过才能开始研究噪声下的误差统计。这是仿真程序调试的第一优先级。6. 更进一步加权最小二乘与连续轨迹定位拿到基础版本的三边定位参考程序后有两个方向能带来实质精度提升值得自己动手改一改。第一个方向是加权最小二乘。基础版本里所有 LED 的距离误差被一视同仁但实际上离接收机越远的 LEDRSS 测距的相对误差越大。给每个 LED 的距离方程加一个与距离成反比的权重让远处 LED 对定位结果的影响减小角落区域的误差会明显收敛。MATLAB 实现上不需要改多少左除前面乘上一个权重矩阵就行W diag(1 ./ ((d_meas 0.1) .^ 2)); % 距离大的权重小 pos_w (A * W * A) \ (A * W * b);第二个方向是对连续运动轨迹做滤波。单次定位结果是独立的上一帧到下一帧的跳动没有约束而室内定位的接收机通常是移动的比如手持终端或者自动导引车。用卡尔曼滤波把位置的历史信息结合进来让轨迹平滑化比单纯提高单点定位精度更有效果。仿真程序里先按时间序列生成一条移动轨迹每帧都做一次三边定位再把定位结果输入卡尔曼滤波器输出的轨迹会比原始定位平滑得多。如果想把参考程序改得更贴近实际部署还可以做一件事把光信道模型从自由空间路径损耗换成包含一次反射的模型。墙面反射会对接收功率产生一个偏置分量让 RSS 测距的系统性偏差更明显。仿真里只要在理想接收功率上叠加一个占直射功率 5% 到 15% 的反射分量就能复现出真实环境里定位漂移的现象。做好这一步仿真结论的说服力会明显上一个台阶也会对实际系统的误差预算有更准确的预期。从我开始用这类程序做定位预研以后每次拿到新的仿真代码都会先做三个动作固定随机种子、跑无噪声自检、再扫一遍关键参数。这套流程看起来很笨但几乎能过滤掉所有低级错误。希望帮到你。本文还有配套的精品资源点击获取