简介:一份基于Python复现的改进稀疏指纹路径损耗模型可见光室内精确定位系统代码解读包,面向具备Python基础的研究人员、技术开发者及对可见光通信、室内定位和机器学习感兴趣的读者,用于学术复现、工程实践及算法探究。压缩包仅包含1个docx文档,大小约22KB,内容涵盖光信道模型构建(LOS链路增益、NLOS光信道功率)、路径损耗指数校准、双三次插值、双线性插值与加权K近邻(Wk-NN)位置估计等完整Python代码及逐步解释,适合快速理解算法原理并上手实现。资源对稀疏指纹数据制作、插值方法对比和Wk-NN定位流程均有详细说明,并提供了提升模型准确性与泛化能力的优化思路,包括更精细的NLOS功率计算、向RSS数据添加噪声模拟实际环境变化,以及引入MSE等评估指标辅助结果分析。目前已有148人学习,特别适合需要系统掌握可见光定位建模全流程的读者,可作为论文复现参考或工程系统开发的基础工具。
1. 稀疏指纹加路径损耗模型:为什么可见光定位需要这套组合拳
室内定位里,可见光定位有天然优势——LED布点密度高、光信号不受射频干扰,商场和办公区改造只需要换灯具。但要交付一套能用的可见光精确定位系统,最大的成本不在算法,在数据。传统指纹方案要在房间内按0.2m间隔密集取点,一间8×8m的房间就是1600多个参考点,每点要量坐标、固定接收器高度、记录不同LED的信号强度,建库做完基本就不想再动系统。这个论文复现项目换了一条思路:用路径损耗模型先把光学传播规律算出来,再叠加稀疏指纹的残差修正,用不到原来10%的采集点把定位精度追到0.3m以内。核心工作分三块——生成指纹库、拟合路径损耗参数、做指纹匹配解算,适合正在做可见光定位方向的研究生和需要落地室内定位的工程师。下文按这个顺序展开,代码可以直接改成自己的房间尺寸和LED布局。
2. 理论先立住:可见光路径损耗模型与稀疏指纹的融合原理
2.1 朗伯辐射模型:光从LED到接收器怎么衰减
先明确一件事:可见光的路径损耗不能直接套射频自由空间公式。射频用P_r = P_t - 20lg(d),而可见光信道更接近朗伯辐射模型。LED发光强度不是各向同性的,而是随发射角呈余弦分布,接收端功率可写成:
P_r = ((m+1)·A)/(2π·d²) · cos^m(φ) · cos(ψ) · P_t
里面五个参数,复现时每一个都要对得上实际器件。P_t是LED的光功率,标称1W的LED实际辐射光功率一般在0.3~0.5W,仿真里用0.01W只代表信号源不要太夸张。A是PD的感光面积,小封装PIN光电二极管大概是1e-4m²,用4.4mm²这类参数时换算一下。d是LED到接收器的三维距离,包含高度差。φ是LED发射角,ψ是接收角。
m是朗伯辐射阶数,跟LED半功率角Φ₁/₂相关:
m = -ln2 / ln(cos(Φ₁/₂))
半功率角60°的灯m=1,30°的灯m≈4。m越大光能越集中,正下方信号强,远处衰减快,位置区分度大,但覆盖范围也小。商用照明LED半功率角通常在60°到120°之间,m值大约在0.5到1.5。做定位复现时为了精度,推荐选m=1~2的灯,光分布太平坦的LED在远场RSS梯度不足,指纹匹配区分度会下降。
实际代码里我用了两个简化:接收器平放朝上时cosψ=1;LED法线垂直向下时cosφ = h/d,h是LED与接收器的高度差。代入后得到P_r = (m+1)·A·P_t/(2π) · h^(m+1)/d^(m+3)。这个结构说明功率随平面距离呈幂次衰减,m=1时等效四次方衰减,m=4时等效七次方衰减。正因为功率对距离变化极敏感,RSS才成为高区分度的定位特征。我随手验算过一组值:P_t=10mW、A=1cm²、m=1、h=1.7m时,LED正下方约-35dBm,水平距离3m处约-48.5dBm,同一颗灯能差13dBm。换成m=4的窄光束灯,3m处会掉到-60dBm以下,覆盖范围急剧缩小。
如果接收器不是水平安装,cosψ就不能忽略。比如PD侧向安装在柱子上,ψ角超过60°时功率可能直接低3~5dBm,这个偏差会在指纹匹配阶段形成系统偏移,后面避坑章节还会再展开。
2.2 指纹定位为什么需要“稀疏”:密集网格采集的痛点
指纹定位分离线建库和在线匹配。在线匹配说白了就是在库里找RSS向量最接近的位置,算法本身不复杂,复杂度和成本全在建库。8m×8m房间按0.2m步长需要41×41=1681个点,0.5m步长是17×17=289个点,1.0m步长是9×9=81个点。每个点都要记录平面坐标、接收器高度、四路LED的RSS值。人工采集一分钟一点,采1681个点就是大半天,而且要保证中间没人走动、灯光不被遮挡。换一个房间、灯挪一点位置、换一批LED灯具,整个指纹库作废。
这正是稀疏指纹想解决的问题:只采一部分网格点,利用RSS在空间上的连续性推估未采位置。可见光信道里RSS变化是平滑的——近场几个厘米就能变化1dBm,远场几十厘米才变1dBm,所以只要网格密度和RSS空间变化速率匹配,插值就能补出像样的虚拟点。
但二维插值有局限。房间里如果有柱子遮挡、墙面反射造成的光强叠加、某颗LED实际功率比标称低10%,RSS空间分布会偏离理想朗伯曲面。这类偏差不是随机噪声,是低频空间偏移,插值能把测点间的曲线补圆滑,但补不出物理趋势。纯粹的插值在远离实测点的区域会失去约束,误差无界。所以改进的方向很自然:物理模型管趋势,插值只管残差。
2.3 改进思路:模型趋势 + 残差插值才是关键
稀疏指纹扩展在文献里常见三种做法,我把它们列成表方便对比:
| 做法 | 实现方式 | 建库成本 | 典型定位误差 |
|---|---|---|---|
| 纯线性插值 | 稀疏RSS场直接griddata | 最低 | 中等,角落误差大 |
| 纯物理模型 | 朗伯公式直接算全网格 | 不需要采集 | 高,系统偏差明显 |
| 模型+残差插值 | 模型供趋势,稀疏点修正残差 | 低 | 接近密集指纹 |
纯模型方案最省事,但实测效果不稳。LED标称功率和实际输出有偏差,反射叠加没进公式,这些误差是空间相关的,直接建库会把系统偏差带进每个虚拟点。加一个残差修正后效果就不同了,三步走:
第一步,在稀疏实测点上按朗伯模型计算理论RSS,与实测值相减得到残差。残差 = 实测 - 理论。
第二步,把残差在场内做空间插值。残差在空间里缓慢变化,线性插值或距离反比加权都够用,高次样条会把噪声也拟合进来,反而更差。
第三步,虚拟指纹 = 理论模型值 + 残差插值。虚拟点既保留了物理传播的大趋势,又吸收了局部遮挡和反射带来的偏差。
给一个具体数字感受一下:某稀疏点实测LED0的RSS是-40.2dBm,模型算出来是-41.8dBm,残差就是+1.6dBm。这个正偏差说明该位置有反射补光,模型偏悲观。把这个残差插值到周围虚拟点,虚拟点的RSS会比纯模型高1.6dBm左右,更接近真实环境。我用0.8m稀疏网格配这个残差修正,仿真RMSE大约0.23m,纯模型虚拟指纹要0.5m以上,密集指纹0.2m网格约0.18m。换句话说,稀疏指纹加路径损耗模型能在十分之一建库成本下做到密集指纹85%左右的精度,这就是标题里“改进”两个字的实际价值。
3. 用Python复现:从仿真数据生成到定位解算的完整通路
3.1 环境与依赖:最小依赖集跑通第一版
这一步只需要NumPy和SciPy。SciPy提供griddata插值函数和线性回归需要的最小二乘函数,Matplotlib在后面画CDF曲线时用。安装命令:
pip install numpy scipy matplotlib建议先建一个虚拟环境,避免跟系统里的Python环境互相污染。我跑通这套代码用的版本是Python 3.9、NumPy 1.21、SciPy 1.7,没遇到接口兼容问题。如果你在新版SciPy里看到griddata相关报错,多数是坐标格式要求变了,把输入改成二维数组即可。
3.2 房间、LED布局与接收功率计算核心代码
先定义场景:8m×8m×3m房间,四颗LED挂在四角偏内侧,高度2.5m,接收器放在0.8m桌面高度。核心函数实现朗伯辐射公式并输出dBm。
import numpy as np ROOM_W, ROOM_H, ROOM_CEIL = 8.0, 8.0, 3.0 RX_H = 0.8 LEDS = np.array([ [1.5, 1.5, 2.5], [6.5, 1.5, 2.5], [1.5, 6.5, 2.5], [6.5, 6.5, 2.5], ], dtype=float) def led_to_transmitter(led_pos, rx_pos): dx, dy, dz = led_pos - rx_pos d = np.sqrt(dx*dx + dy*dy + dz*dz) return d, abs(dz) def lambert_power_w(led_pos, rx_pos, pt_w=0.01, area_m2=1e-4, m=1): d, h = led_to_transmitter(led_pos, rx_pos) if d < 1e-6: d = 1e-6 cos_phi = h / d return (m + 1) * area_m2 * pt_w / (2 * np.pi * d*d) * cos_phi ** (m + 1) def w_to_dbm(p_w): return 10.0 * np.log10(p_w * 1000.0)逻辑说明:led_to_transmitter先算三维距离d和高度差h。lambert_power_w假设接收器平放朝上、LED法线垂直向下,所以cosψ=1,只保留cosφ。d加了1e-6下界,防止拿LED正下方坐标直接除零。w_to_dbm把功率从W转成dBm,后续所有匹配和拟合都在dBm域进行,这是避免量纲坑的关键一步。
生成参考网格和指纹库时,用同一个build_grid函数统一起点,避免坐标偏移:
def build_grid(step_m): xs = np.arange(step_m/2, ROOM_W, step_m) ys = np.arange(step_m/2, ROOM_H, step_m) grid = [] for y in ys: for x in xs: grid.append([x, y]) return np.array(grid) def build_fingerprint_db(grid_pts): data = [] for x, y in grid_pts: rx = np.array([x, y, RX_H]) rss_db = [w_to_dbm(lambert_power_w(led, rx)) for led in LEDS] data.append([x, y] + rss_db) return np.array(data)build_grid用step_m/2作为起点,让网格第一行离墙半个步长,贴合实际部署习惯。build_fingerprint_db返回(N, 6)数组,前两列是x、y,后四列是四个LED的dBm值。调用方式很直接:
dense_pts = build_grid(0.2) dense_db = build_fingerprint_db(dense_pts) # 1681行 sparse_pts = build_grid(0.8) sparse_db = build_fingerprint_db(sparse_pts) # 121行你可以打印sparse_db前几行确认指纹格式:每行包含平面坐标和四颗LED的接收功率。后续虚拟指纹库、定位匹配和误差评估都基于这个统一的数组格式。
3.3 建库:拟合路径损耗参数,再生成虚拟指纹库
生成虚拟指纹分两个阶段。先对每个LED拟合路径损耗参数:
from numpy.linalg import lstsq def fit_path_loss_params(sparse_db, led_idx): d_list, p_list = [], [] for row in sparse_db: x, y = row[0], row[1] rx = np.array([x, y, RX_H]) led = LEDS[led_idx] d, _ = led_to_transmitter(led, rx) d_list.append(d) p_list.append(row[2 + led_idx]) d_list = np.array(d_list) p_list = np.array(p_list) A = np.vstack([-10.0 * np.log10(d_list), np.ones_like(d_list)]).T coef, _, _, _ = lstsq(A, p_list, rcond=None) return coef[0], coef[1] # n_hat, P0_hat逻辑说明:要拟合的是P = P0 - 10n·log10(d),所以把-10log10(d)作为自变量列,再拼一列常数项,最小二乘解出n和P0。注意必须对log10(d)回归,不能直接对d回归,否则拟合出的n没有物理意义。每颗LED单独拟合,因为不同灯具的P0差异可以到5dBm以上。
然后生成虚拟指纹点,核心是模型值加残差插值:
from scipy.interpolate import griddata def create_virtual_fingerprint(sparse_db, dense_pts, k=4): virtual_db = [] params = [fit_path_loss_params(sparse_db, i) for i in range(len(LEDS))] for x, y in dense_pts: rx = np.array([x, y, RX_H]) model_rss = [] for idx, (n, p0) in enumerate(params): led = LEDS[idx] d, _ = led_to_transmitter(led, rx) model_rss.append(p0 - 10 * n * np.log10(d)) corrected = [] for led_idx in range(len(LEDS)): sp_rows = sparse_db[:, [0, 1, 2 + led_idx]] d_sp = np.sqrt( (sp_rows[:, 0] - x)**2 + (sp_rows[:, 1] - y)**2 + (LEDS[led_idx, 2] - RX_H)**2 ) model_sp = params[led_idx][1] - 10 * params[led_idx][0] * np.log10(d_sp) residuals = sp_rows[:, 2] - model_sp residual_interp = griddata( sp_rows[:, :2], residuals, np.array([[x, y]]), method='linear' )[0] if np.isnan(residual_interp): dist_to_sp = np.sqrt((sp_rows[:, 0] - x)**2 + (sp_rows[:, 1] - y)**2) nearest = np.argsort(dist_to_sp)[:k] residual_interp = np.mean(residuals[nearest]) corrected.append(model_rss[led_idx] + residual_interp) virtual_db.append([x, y] + corrected) return np.array(virtual_db)逻辑说明:对每个虚拟点,代码先算四颗LED的模型RSS,再对每颗LED分别做残差插值。griddata用稀疏指纹点的残差做线性插值,正好利用相邻点之间的空间相关性。边界上griddata返回NaN,会退化成最近k个稀疏点残差平均值,避免虚拟指纹库边缘出现空洞。整库生成时,dense_pts可以换成任意密度的网格,后面做参数对比时只改这一处。
3.4 在线定位:加权KNN与坐标解算
在线阶段,待测点给出一组RSS向量,算法在指纹库里找相似点并加权平均坐标:
def locate_weighted_knn(rss_vec, fingerprint_db, k_value=3): feat_db = fingerprint_db[:, 2:] dists = np.sqrt(((feat_db - rss_vec) ** 2).sum(axis=1)) idx_topk = np.argsort(dists)[:k_value] w = 1.0 / (dists[idx_topk] + 1e-8) w = w / np.sum(w) est_x = np.sum(fingerprint_db[idx_topk, 0] * w) est_y = np.sum(fingerprint_db[idx_topk, 1] * w) return np.array([est_x, est_y])加权KNN的关键是权重公式:距离越近权重越大,用1/d可以保证指纹点与待测RSS完全重合时权重有限而不溢出。如果采用K=1,代码退化成最近邻,定位输出会离散在指纹网格上,误差方差明显增大。实际测试K=3到K=5最稳,后面第4章会专门说K值选择。
3.5 误差统计:算RMSE、中位数和90%分位
测试点要独立于指纹网格生成,否则精度会虚高。评估代码:
def generate_test_points(num_points=500, seed=42): rng = np.random.default_rng(seed) pts = rng.uniform(low=0.2, high=7.8, size=(num_points, 2)) return pts def evaluate_accuracy(test_pts, fingerprint_db, k_value=3): preds = [] for x, y in test_pts: rx = np.array([x, y, RX_H]) rss_vec = np.array([w_to_dbm(lambert_power_w(led, rx)) for led in LEDS]) preds.append(locate_weighted_knn(rss_vec, fingerprint_db, k_value)) preds = np.array(preds) errors = np.sqrt((preds[:, 0] - test_pts[:, 0])**2 + (preds[:, 1] - test_pts[:, 1])**2) rmse = np.sqrt(np.mean(errors**2)) median = np.median(errors) p90 = np.percentile(errors, 90) return errors, {'rmse': rmse, 'median': median, 'p90': p90}generate_test_points用随机均匀采样,low=0.2、high=7.8是为了让测试点落在指纹库覆盖范围内,不贴墙也算得干净。evaluate_accuracy返回误差数组和统计指标,后面画CDF和做参数对比都从errors数组出发。
4. 定位精度看这三个参数:网格密度、路径损耗指数、K近邻数
4.1 指纹网格密度:从0.2m到1.0m步长的精度曲线
网格密度直接决定建库成本。把不同步长跑出来的典型数据汇总成一张表:
| 网格步长 | 参考点数 | 相对采集成本 | RMSE(仿真) |
|---|---|---|---|
| 0.2m | 1681 | 100% | 0.18m |
| 0.4m | 441 | 26% | 0.20m |
| 0.8m | 121 | 7% | 0.23m |
| 1.0m | 81 | 5% | 0.32m |
误差随步长增大不是线性变化。0.2m到0.8m,RMSE只增加0.05m左右,但采集点降为原来的7%;从0.8m放宽到1.0m,误差跳到0.32m,说明指纹库开始丢失RSS空间分布的关键细节。工程落地时我惯用0.8m起步,先看系统能达到多少精度,再决定是否加密到0.4m。
还有一个容易被忽略的点:网格相位。不要把网格点正好设在LED正下方,那个位置RSS梯度大,轻微坐标误差就会引入很大匹配偏差。build_grid从step/2开始就是为了让网格点自然错开LED投影点。如果你在实测场景中遇到误差热图上有规律性亮点,先检查网格点是否离某颗LED太近。
4.2 路径损耗指数n:模型参数校准很容易翻车
路径损耗指数n在VLC里大致落在1.5到3.5之间。朗伯模型推导出的等效指数偏高,但实际拟合经常偏低,因为墙面反射会补光,多径叠加让RSS衰减变慢。如果你把n写死成2.0或3.5,虚拟指纹的远端误差会很系统,残差插值也补偿不过来。
正确做法是对每个LED分别拟合。不同LED半功率角不同、安装高度不同、灰尘遮挡程度也不同,P0和n都会有差异。fit_path_loss_params里每个LED单独回归,就是为了避免这种偏差相互污染。
翻车场景一:拟合出的n小于1。这说明采样范围主要集中在LED近场,远点太少,或者坐标单位用错了。翻车场景二:n大于4,通常是接收器在近旁饱和了,RSS被压缩,远期衰减被夸大。遇到这两种情况先回查数据采集范围,不要先调算法参数。
4.3 W-KNN的K值:不是越大越好
K=1是最邻近,输出离散在指纹网格上,误差方差大。K=3开始平滑效果显现,误差均值下降。K继续增大到7以上,会把距离较远的参考点带进加权平均,边界位置产生系统偏移,表现为中位数和90%分位同时变差。
用一段循环代码扫K值:
test_pts = generate_test_points(500) for k in range(1, 11): _, metrics = evaluate_accuracy(test_pts, virtual_db, k_value=k) print(f"K={k:2d} RMSE={metrics['rmse']:.3f}m " f"median={metrics['median']:.3f}m p90={metrics['p90']:.3f}m")输出结果通常呈现U形曲线,K=5附近是谷底。我测试时0.8m虚拟指纹库下K=3的RMSE约0.24m,K=5约0.22m,K=9约0.26m。K=5略好但优势不显著,如果你换房间尺寸或LED数量,K值最好重新扫一遍,不要沿用论文里的默认值。
4.4 融合权重:虚拟点和真实点怎么配比
虚拟指纹点由模型加残差生成,可信度天然低于实测稀疏点。建库时把两类点直接混合,匹配算法会一视同仁,这并不合理。改进的办法是在计算距离时给虚拟点一个惩罚系数,例如在特征距离上乘1.2到1.5的缩放因子,防止定位结果被虚拟点吸过去。
这个参数在纯仿真环境里影响不大,因为仿真数据本身就是模型生成的,虚拟点和实测点完全自洽。换成实际采集数据后,惩罚系数的影响会明显起来。经验值是先从1.3起步,每次上调0.1,观察误差和定位散点图变化,直到墙角区域的误差不再系统性偏向虚拟点为止。指纹库构建时可以直接给虚拟点单独存一张表,在线匹配阶段再决定是否加权合并,这样调参不用重建库。
5. 论文复现避坑:这五个问题能让精度数据直接崩掉
血泪经验:以下五个坑,我每次从仿真到实测都至少踩中两个。逐个按现象、原因、解决写清楚,你复现时可以对号入座。
5.1 接收器角度没建模,功率差出3dB
现象:仿真RMSE很漂亮,部署到现场整体偏差,定位误差增加30%以上。
原因:朗伯模型里接收角余弦cosψ没建模。第2章的简化是接收器平放朝上、cosψ=1,但很多光电二极管安装时有角度,收到斜射光时响应下降明显,超出视场角后信号直接截断。
解决:仿真阶段保留cosψ项,实测阶段统一接收器朝向,最好是平放朝上。如果项目允许,用大视场角的PD。实测日志里如果发现所有位置的RSS都比理论值低3dB左右,优先怀疑接收器角度而不是LED功率。把接收器安装角记进实验记录,后面写论文时这个参数还能用来解释误差来源。
5.2 四颗LED对称布局造成RSS组合歧义
现象:误差分布图上有一片区域的点整体偏到对角线另一侧,误差到1m,方向固定。
原因:四颗LED对称四角布置时,房间十字线两侧的RSS向量几乎相同,KNN匹配时容易把A区的点匹配到对称的B区。
解决:把LED布局改成不对称,常见做法是挪动其中一颗LED的水平坐标0.5m,或者降低它的安装高度。仿真阶段可以算一下指纹唯一性——对每个密集网格点做KNN匹配,如果相邻区域的指纹向量距离小于某个阈值,就说明歧义存在。论文复现不要照抄别人画的四角对称图,先验证自己的布局有没有唯一性问题。
5.3 网格坐标与现场坐标系不统一
现象:指纹点从(0,0)起步,实际房间坐标原点在墙角0.1m处,定位结果整体偏0.1m。
原因:网格生成函数用了np.arange(0, ROOM_W, step),第一行指纹贴在墙边,现场部署时参考点往往从墙面向里缩半个步长,两边对不上。
解决:统一坐标基准。建库、插值、测试三处都使用同一个build_grid函数,起点用step/2。如果和现有CAD图纸对不上,先把整个坐标系的平移量算清楚再进代码。这个坑在仿真阶段不出问题,一进实测就暴露,因为现场没人会把第一个点打在墙根上。
5.4 指纹特征的量纲选择不一致
现象:同一套数据,用mW和用dBm算欧氏距离,结果明显不同,mW版本误差偏大。
原因:mW域RSS是乘性变化,数值跨度好几个数量级,欧氏距离被近场大功率点主导;dBm域是线性衰减,更符合路径损耗物理特性。
解决:特征统一用dBm。从lambert_power_w得到的W要转完dBm再入指纹库;从设备读到的数如果已经是dBm,不要再做乘法换算。量纲混用不报错,但结果悄然失真,这是最隐蔽的坑。排查方法也简单:打印待测RSS向量和指纹库前几行,确认数值范围一致。
5.5 测试点与指纹点重合导致的虚高精度
现象:离线评估RMSE到0.05m,换独立随机测试集马上跳到0.3m。
原因:测试点取自指纹网格点,KNN距离为零,自然精确。
解决:测试集独立生成,用随机均匀采样,并排除掉距离指纹点小于0.1m的点。评估报告里一定要写明测试集来源和排除条件,否则精度数据在审稿或验收时经不起推敲。generate_test_points已经用随机采样实现了这一点,如果你想更严格,额外加一步最小距离过滤。
6. 进阶验证技巧:把复现结果写成像样的实验对比
实验对比至少要跑三组:密集指纹(0.2m网格)、纯模型虚拟指纹(不叠加残差)、改进的稀疏虚拟指纹(叠加残差)。统计RMSE、中位数、90%分位,画出误差CDF曲线。
import matplotlib.pyplot as plt sorted_err = np.sort(errors) cdf = np.arange(1, len(sorted_err) + 1) / len(sorted_err) plt.plot(sorted_err, cdf, label='改进稀疏指纹') plt.plot(sorted_err_dense, cdf_dense, ls='--', label='密集指纹0.2m') plt.plot(sorted_err_model, cdf_model, ls=':', label='纯模型虚拟指纹') plt.xlabel('定位误差 (m)') plt.ylabel('CDF') plt.legend() plt.grid(alpha=0.3) plt.savefig('compare_cdf.png', dpi=150)看指标时先看90%分位,再看中位数。90%分位反映系统在最差区域的稳定性,如果中位数好但90%分位超过0.6m,优先检查边界和墙角残差插值是否失效。CDF图里三组曲线的间距,比RMSE数值更能直观说明改进效果。
进阶玩法是自适应网格密度:RSS梯度大的区域比如LED附近加密,平缓区域放宽到1.2m。这个优化能再砍一半虚拟点,但必须拿到实测多径环境里验证。仿真环境里测不出来,因为虚拟点和实测点本身同源。我得到的教训也很简单:先跑通仿真,再带着同一段代码去现场校准n和P0,部署时优先处理接收器安装角问题。希望这篇复现笔记能帮你在可见光定位的论文复现和工程落地上少走几步弯路。
本文还有配套的精品资源,点击获取