Flutter中如何利用三边测量法实现iBeacon室内定位?
室内定位应用:修复iBeacon三边测量(Trilateration)坐标计算问题
我正在开发一款房间内的室内定位应用,使用3个iBeacon设备。已获取到这些Beacon的RSSI值,尝试通过公式计算x、y坐标,但结果不准确,数值几乎无变化。请问如何在我的Flutter代码中正确使用Trilateration(三边测量法)?
我的代码:
class TabScanning extends StatefulWidget { TabScanning({super.key}); double x = 0, y = 0; @override _TabScanningState createState() => _TabScanningState(); } class _TabScanningState extends State<TabScanning> { StreamSubscription<RangingResult>? _streamRanging; final _regionBeacons = <Region, List<Beacon>>{}; final _beacons = <Beacon>[]; final controller = Get.find<RequirementStateController>(); double x1 = 0, y1 = 150; double x2 = -150, y2 = -150; double x3 = 150, y3 = -150; double d1 = 0, d2 = 0, d3 = 0; int rssi = 0; int txPower = -67; int N = 2; List<double> distance = [0.0, 0.0, 0.0]; List<Beacon> defaultBeacons = [ const Beacon( proximityUUID: '1AA10000-0A46-215F-E97E-5A966A7DEDC3', major: 1, minor: 3, accuracy: 0.0, macAddress: "", txPower: 0, ), const Beacon( proximityUUID: '1AA10000-0A46-215F-E97E-5A966A7DEDC3', major: 1, minor: 4, accuracy: 0.0, macAddress: "ss", txPower: 0, ), const Beacon( proximityUUID: '1AA10000-0A46-215F-E97E-5A966A7DEDC3', major: 1, minor: 8, accuracy: 0.0, macAddress: "", txPower: 0, ), ]; @override void initState() { super.initState(); _beacons.addAll(defaultBeacons); controller.startStream.listen((flag) { if (flag == true) { initScanBeacon(); } }); controller.pauseStream.listen((flag) { if (flag == true) { //pauseScanBeacon(); } }); } initScanBeacon() async { await flutterBeacon.initializeScanning; if (!controller.authorizationStatusOk || !controller.locationServiceEnabled || !controller.bluetoothEnabled) { print( 'RETURNED, authorizationStatusOk=${controller.authorizationStatusOk}, ' 'locationServiceEnabled=${controller.locationServiceEnabled}, ' 'bluetoothEnabled=${controller.bluetoothEnabled}'); return; } final regions = <Region>[ Region( identifier: 'ibeacon', proximityUUID: '1AA10000-0A46-215F-E97E-5A966A7DEDC3', ), ]; if (_streamRanging != null) { if (_streamRanging!.isPaused) { _streamRanging?.resume(); return; } } _streamRanging = flutterBeacon.ranging(regions).listen((RangingResult result) { print(result); if (mounted) { setState(() { _regionBeacons[result.region] = result.beacons; _beacons.forEach((existingBeacon) { result.beacons.forEach((newBeacon) { if (existingBeacon.major == newBeacon.major && existingBeacon.minor == newBeacon.minor) { _beacons[_beacons.indexOf(existingBeacon)] = Beacon( proximityUUID: existingBeacon.proximityUUID, major: existingBeacon.major, minor: existingBeacon.minor, accuracy: newBeacon.accuracy, rssi: newBeacon.rssi, macAddress: newBeacon.macAddress, txPower: newBeacon.txPower, ); } }); }); _beacons.sort(_compareParameters); for (int i = 0; i < _beacons.length; i++) { rssi =_beacons[i].rssi; if (_beacons[i].minor == 3) { dist[0] = pow(10, ((txPower - rssi) / (10 * 2))) as double; } else if (_beacons[i].minor == 4) { dist[1] = pow(10, ((txPower - rssi) / (10 * 2))) as double; } else if (_beacons[i].minor == 8) { dist[2] = pow(10, ((txPower - rssi) / (10 * 2))) as double; } } d1 = dist[0]; d2 = dist[1]; d3 = dist[2]; double a = (-2 * x1) + (2 * x2); double b = (-2 * y1) + (2 * y2); num c = pow(d1, 2) - pow(d2, 2) - pow(x1, 2) + pow(x2, 2) - pow(y1, 2) + pow(y2, 2); double d = (-2 * x2) + (2 * x3); double e = (-2 * y2) + (2 * y3); num f = pow(d2, 2) - pow(d3, 2) - pow(x2, 2) + pow(x3, 2) - pow(y2, 2) + pow(y3, 2); widget.x = (c * e - f * b); widget.x = widget.x / (e * a - b * d); widget.y = (c * d - a * f); widget.y = widget.y / (b * d - a * e); }); } }); } pauseScanBeacon() async { //_streamRanging?.pause(); if (_beacons.isNotEmpty) { setState(() { //_beacons.clear(); }); } } int _compareParameters(Beacon a, Beacon b) { int compare = a.proximityUUID.compareTo(b.proximityUUID); if (compare == 0) {compare = a.major.compareTo(b.major);} if (compare == 0) {compare = a.minor.compareTo(b.minor);} return compare; } }
我在GitHub上找到上述计算方式,但计算出的x、y数值几乎无变化,比如x=0.012……、y=-38.15……。
问题修复方案
一、修正距离计算逻辑
你的代码存在变量名错误(定义distance数组却用dist)、传播损耗系数固定为2(室内环境建议3~4)、硬编码txPower(应该用每个Beacon实际返回的txPower)三个核心问题,修改后的距离计算代码:
// 替换原有的距离计算循环 for (int i = 0; i < _beacons.length; i++) { final beacon = _beacons[i]; // 优先使用Beacon自身的txPower,无效时用默认值 final currentTxPower = beacon.txPower != 0 ? beacon.txPower : txPower; final currentRssi = beacon.rssi; // 过滤极端无效RSSI值 if (currentRssi < -100 || currentRssi > -30) { continue; } // 正确的RSSI转距离公式:d = 10^((TxPower - RSSI)/(10*N)) // N为环境损耗系数,室内取3.5左右,可根据实际场景调整 final calculatedDistance = pow(10, ((currentTxPower - currentRssi) / (10 * 3.5))) as double; if (beacon.minor == 3) { distance[0] = calculatedDistance; } else if (beacon.minor == 4) { distance[1] = calculatedDistance; } else if (beacon.minor == 8) { distance[2] = calculatedDistance; } }
二、修复三边测量数学公式
原代码的线性方程组推导错误,正确的三边测量公式基于两点间距离公式展开,修改后的坐标计算代码:
d1 = distance[0]; d2 = distance[1]; d3 = distance[2]; // 跳过无效距离值(初始0或异常值) if (d1 <= 0 || d2 <= 0 || d3 <= 0) { return; } // 正确的系数计算 double a = 2 * (x2 - x1); double b = 2 * (y2 - y1); double c = pow(d1, 2) - pow(d2, 2) - pow(x1, 2) + pow(x2, 2) - pow(y1, 2) + pow(y2, 2); double dCoeff = 2 * (x3 - x2); double e = 2 * (y3 - y2); double f = pow(d2, 2) - pow(d3, 2) - pow(x2, 2) + pow(x3, 2) - pow(y2, 2) + pow(y3, 2); // 避免除以0 double denominator = a * e - b * dCoeff; if (denominator.abs() < 0.0001) { return; } // 求解坐标 widget.x = (c * e - f * b) / denominator; widget.y = (a * f - c * dCoeff) / denominator;
三、提升数据稳定性
- 滑动平均平滑距离:减少RSSI波动带来的距离跳变,添加全局变量存储历史距离:
// 类内定义 Map<int, List<double>> distanceHistory = {3: [], 4: [], 8: []}; final maxHistoryCount = 5; // 在计算出calculatedDistance后替换赋值逻辑 distanceHistory[beacon.minor]!.add(calculatedDistance); if (distanceHistory[beacon.minor]!.length > maxHistoryCount) { distanceHistory[beacon.minor]!.removeAt(0); } final avgDistance = distanceHistory[beacon.minor]!.reduce((a, b) => a + b) / distanceHistory[beacon.minor]!.length; if (beacon.minor == 3) distance[0] = avgDistance;
- 优化Beacon数据更新:替换原有的双重循环,用Map提升匹配效率:
// 替换原有的_beacons更新逻辑 final newBeaconMap = {for (var b in result.beacons) '${b.major}-${b.minor}': b}; for (int i = 0; i < _beacons.length; i++) { final key = '${_beacons[i].major}-${_beacons[i].minor}'; if (newBeaconMap.containsKey(key)) { _beacons[i] = newBeaconMap[key]!; } }
四、验证物理坐标
确保代码中x1/y1、x2/y2、x3/y3与Beacon实际部署的物理坐标一致(单位统一,比如厘米或米),坐标错误会直接导致计算结果偏离。
内容的提问来源于stack exchange,提问作者Neiss
相关产品推荐
相关产品推荐

