我正在研究使用多个iBeacon进行“粗略”的室内位置定位的可能性。该应用程序是一种“博物馆”设置,如果能够形成一个带有不同对象位置的网格,则会更容易,而不是单个信标(尽管这也可能不可能)。
是否有使用多个信标三角定位到某种位置或某种逻辑的实例和经验,可以帮助我自己编写代码?
我正在研究使用多个iBeacon进行“粗略”的室内位置定位的可能性。该应用程序是一种“博物馆”设置,如果能够形成一个带有不同对象位置的网格,则会更容易,而不是单个信标(尽管这也可能不可能)。
是否有使用多个信标三角定位到某种位置或某种逻辑的实例和经验,可以帮助我自己编写代码?
我一直在做一些实验,试图使用三个信标获得精确的位置。
三边测量结果
不幸的是,在质量方面,结果非常令人失望。主要存在两个问题:
可能的解决方案
在与一位苹果工程师交谈后,他极力反对我走这条路,我现在更倾向于使用蛮力方法。尝试每隔X米(X是系统中容忍的最大误差)设置一个信标,以便我们可以在这些信标网格上跟踪给定设备的位置,通过计算网格上最接近设备的信标并假设设备在同一位置来确定设备的位置。
三边测量算法
但是,为了完整起见,下面分享三边测量算法的核心函数。它基于这篇文章中第3段(“已知三个距离”)。
- (CGPoint)getCoordinateWithBeaconA:(CGPoint)a beaconB:(CGPoint)b beaconC:(CGPoint)c distanceA:(CGFloat)dA distanceB:(CGFloat)dB distanceC:(CGFloat)dC {
CGFloat W, Z, x, y, y2;
W = dA*dA - dB*dB - a.x*a.x - a.y*a.y + b.x*b.x + b.y*b.y;
Z = dB*dB - dC*dC - b.x*b.x - b.y*b.y + c.x*c.x + c.y*c.y;
x = (W*(c.y-b.y) - Z*(b.y-a.y)) / (2 * ((b.x-a.x)*(c.y-b.y) - (c.x-b.x)*(b.y-a.y)));
y = (W - 2*x*(b.x-a.x)) / (2*(b.y-a.y));
//y2 is a second measure of y to mitigate errors
y2 = (Z - 2*x*(c.x-b.x)) / (2*(c.y-b.y));
y = (y + y2) / 2;
return CGPointMake(x, y);
}
/(2*(b.y-a.y))
这一部分会产生0,然后导致除以零错误。 - Yaziddouble[][] positions = new double[][] { { 5.0, -6.0 }, { 13.0, -15.0 }, { 21.0, -3.0 }, { 12.42, -21.2 } };
double[] distances = new double[] { 8.06, 13.97, 23.32, 15.31 };
NonLinearLeastSquaresSolver solver = new NonLinearLeastSquaresSolver(new TrilaterationFunction(positions, distances), new LevenbergMarquardtOptimizer());
Optimum optimum = solver.solve();
// the answer
double[] calculatedPosition = optimum.getPoint().toArray();
// error and geometry information
RealVector standardDeviation = optimum.getSigma(0);
RealMatrix covarianceMatrix = optimum.getCovariances(0);
大多数学术示例(例如维基百科上的一个)涉及恰好三个圆,并假设信息完全准确。这种情况下,问题的形式更简单,有精确答案,但通常不适用于实际情况。
在包含测量误差的R2或R3欧几里得空间中,通常会获得感兴趣的区域(椭圆)或体积(椭球),而不是一个点。如果需要点估计而不是区域,则应使用区域质心或体积质心。 R2空间需要至少3个非退化点和距离才能获得唯一的区域;同样,R3空间需要至少4个非退化点和距离才能获得唯一的区域。
我进行了调查。你需要的术语是“三边测量”。(在三角测量中,你有3个已知点的角度,而在三边测量中,你有3个已知点的距离)。如果你在Google上搜索,你应该能找到几篇文章,其中一篇是Wiki上的。它涉及解决一组三元一次方程。我看到的文件都是针对3D三边测量的 - 2D更容易,因为你可以忽略Z项。
我发现的是抽象的数学知识。我还没有花时间将一般算法映射到具体代码,但我计划在某个时候着手解决它。
请注意,你得到的结果会非常粗略,特别是在除空房间以外的任何地方。信号足够弱,以至于一个人、雕像或任何挡住视线的物体都会显著增加你的距离读数。你甚至可能会在建筑物内的某些地方出现构造干涉(主要来自墙壁),使一些地方读数比实际距离更接近。
另一方面,如果您可以将iBeacon频率提高到大于10Hz(我怀疑是否可能),则使用适当的处理方法可能可以实现5m或更高的精度。首先基于反比例定律的琐碎解决方案,如三边测量,通常表现不佳,因为在实践中,不同信标的距离/RSSI关系经常与反比例定律有很大偏差,原因是上述第1个原因。但只要RSSI在任何特定位置对于某个信标相对稳定(通常情况下是这样),您就可以使用称为指纹识别的方法来实现更高的精度。用于指纹识别的常用方法是kNN(k-最近邻)。
一些iBeacons可以广播超过1Hz,例如Estimote默认使用5Hz。然而,根据link:“这是苹果的限制。iOS每秒返回信标更新,无论设备广告频率如何。”那里还有另一个评论(可能来自Estimote供应商)说:“我们的信标可以广播得更快,这可能会改善结果和测量”。因此,更高的iBeacon频率是否有益尚不清楚。
对于那些需要在Android设备上使用@Javier Chávarri
的三边测量函数(为了节省时间)的人:
public static Location getLocationWithTrilateration(Location beaconA, Location beaconB, Location beaconC, double distanceA, double distanceB, double distanceC){
double bAlat = beaconA.getLatitude();
double bAlong = beaconA.getLongitude();
double bBlat = beaconB.getLatitude();
double bBlong = beaconB.getLongitude();
double bClat = beaconC.getLatitude();
double bClong = beaconC.getLongitude();
double W, Z, foundBeaconLat, foundBeaconLong, foundBeaconLongFilter;
W = distanceA * distanceA - distanceB * distanceB - bAlat * bAlat - bAlong * bAlong + bBlat * bBlat + bBlong * bBlong;
Z = distanceB * distanceB - distanceC * distanceC - bBlat * bBlat - bBlong * bBlong + bClat * bClat + bClong * bClong;
foundBeaconLat = (W * (bClong - bBlong) - Z * (bBlong - bAlong)) / (2 * ((bBlat - bAlat) * (bClong - bBlong) - (bClat - bBlat) * (bBlong - bAlong)));
foundBeaconLong = (W - 2 * foundBeaconLat * (bBlat - bAlat)) / (2 * (bBlong - bAlong));
//`foundBeaconLongFilter` is a second measure of `foundBeaconLong` to mitigate errors
foundBeaconLongFilter = (Z - 2 * foundBeaconLat * (bClat - bBlat)) / (2 * (bClong - bBlong));
foundBeaconLong = (foundBeaconLong + foundBeaconLongFilter) / 2;
Location foundLocation = new Location("Location");
foundLocation.setLatitude(foundBeaconLat);
foundLocation.setLongitude(foundBeaconLong);
return foundLocation;
}
public static Location getLocationWithCenterOfGravity(Location beaconA, Location beaconB, Location beaconC, double distanceA, double distanceB, double distanceC) {
//Every meter there are approx 4.5 points
double METERS_IN_COORDINATE_UNITS_RATIO = 4.5;
//https://dev59.com/jXRB5IYBdhLWcg3wv5tA#524770
//Find Center of Gravity
double cogX = (beaconA.getLatitude() + beaconB.getLatitude() + beaconC.getLatitude()) / 3;
double cogY = (beaconA.getLongitude() + beaconB.getLongitude() + beaconC.getLongitude()) / 3;
Location cog = new Location("Cog");
cog.setLatitude(cogX);
cog.setLongitude(cogY);
//Nearest Beacon
Location nearestBeacon;
double shortestDistanceInMeters;
if (distanceA < distanceB && distanceA < distanceC) {
nearestBeacon = beaconA;
shortestDistanceInMeters = distanceA;
} else if (distanceB < distanceC) {
nearestBeacon = beaconB;
shortestDistanceInMeters = distanceB;
} else {
nearestBeacon = beaconC;
shortestDistanceInMeters = distanceC;
}
//http://www.mathplanet.com/education/algebra-2/conic-sections/distance-between-two-points-and-the-midpoint
//Distance between nearest beacon and COG
double distanceToCog = Math.sqrt(Math.pow(cog.getLatitude() - nearestBeacon.getLatitude(),2)
+ Math.pow(cog.getLongitude() - nearestBeacon.getLongitude(),2));
//Convert shortest distance in meters into coordinates units.
double shortestDistanceInCoordinationUnits = shortestDistanceInMeters * METERS_IN_COORDINATE_UNITS_RATIO;
//http://math.stackexchange.com/questions/46527/coordinates-of-point-on-a-line-defined-by-two-other-points-with-a-known-distance?rq=1
//On the line between Nearest Beacon and COG find shortestDistance point apart from Nearest Beacon
double t = shortestDistanceInCoordinationUnits/distanceToCog;
Location pointsDiff = new Location("PointsDiff");
pointsDiff.setLatitude(cog.getLatitude() - nearestBeacon.getLatitude());
pointsDiff.setLongitude(cog.getLongitude() - nearestBeacon.getLongitude());
Location tTimesDiff = new Location("tTimesDiff");
tTimesDiff.setLatitude( pointsDiff.getLatitude() * t );
tTimesDiff.setLongitude(pointsDiff.getLongitude() * t);
//Add t times diff with nearestBeacon to find coordinates at a distance from nearest beacon in line to COG.
Location userLocation = new Location("UserLocation");
userLocation.setLatitude(nearestBeacon.getLatitude() + tTimesDiff.getLatitude());
userLocation.setLongitude(nearestBeacon.getLongitude() + tTimesDiff.getLongitude());
return userLocation;
}
经过测试,我发现其准确度达到5米。
如果我们能够进一步完善它,请评论给我您的测试结果。
我已经为Android 4.4实现了一个非常简单的指纹算法,在相对“恶劣”的环境下进行了测试:
精度在5-8米之间,具体取决于我放置3个Ibeacon广播器的方式。该算法非常简单,我认为您也可以自己实现,步骤如下:
因此,当您开始定位时,只需要按照上述步骤反向进行即可。
对我最有帮助的是Code.Google.com上的这个项目:https://code.google.com/p/wsnlocalizationscala/,其中包含大量代码、多种三边测量算法,均使用C#编写。这是一个大型库,但并不真正意味着可以“开箱即用”。