iBeacons的三angular形例子

我正在研究使用多个iBeacons做一个“粗糙的”室内位置的可能性。 应用程序是一种“博物馆”的设置,能够形成一个网格与不同的对象,然后单独的信标(尽pipe这可能不是不可能的)将更容易。

有没有例子,经验,使用多个信标三angular形到某种位置,或一些逻辑来帮助我自己写的方式?

我一直在做一些实验,使用三个信标来获得一个精确的位置。

三边测量的结果

不幸的是,结果在质量上令人失望。 主要有两个问题:

  1. 在非受控的环境中,您可以find金属和其他影响信号的物体,信标的接收信号强度变化频繁,以至于看起来不可能得到低于5米的误差范围。
  2. 根据用户处理接收器设备的方式,读数也可以改变很多。 如果用户把他/她的手放在蓝牙天线上,则该algorithm将具有低信号作为input,并且因此信标将假定离设备非常远。 看到这个图像看蓝牙天线的准确位置。

可能的解决scheme

在和一位积极阻止我这样下去的苹果工程师交谈之后,我觉得现在更倾向于使用的选项是蛮横的。 尝试每隔X米设置一个信标(X是系统中可容忍的最大误差),因此我们可以通过计算网格上哪个信标最接近设备,并假设给定设备的位置来跟踪这个信标网格该设备处于相同的位置。

三angular测量algorithm

但是,为了完整起见,下面我分享三边测量algorithm的核心function。 它基于本文的第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 - ax*ax - ay*ay + bx*bx + by*by; Z = dB*dB - dC*dC - bx*bx - by*by + cx*cx + cy*cy; x = (W*(cy-by) - Z*(by-ay)) / (2 * ((bx-ax)*(cy-by) - (cx-bx)*(by-ay))); y = (W - 2*x*(bx-ax)) / (2*(by-ay)); //y2 is a second measure of y to mitigate errors y2 = (Z - 2*x*(cx-bx)) / (2*(cy-by)); y = (y + y2) / 2; return CGPointMake(x, y); } 

我看着这个。 这个术语你想要三边测量。 (在三angular测量中,你有3个已知点的angular度,在三边测量中你有3个已知点的距离)如果你使用Google,你应该可以find几篇文章,其中包括一个Wiki。 它涉及求解一组3个联立方程。 我看到的文件是三维三边测量 – 二维是更容易,因为你可以放下Z字。

我发现的是抽象的math。 我还没有花时间把通用algorithm映射成特定的代码,但是我打算在某个时候处理它。

请注意,你得到的结果将是非常粗糙的,特别是除了空房间之外的任何事情。 信号很弱,以至于一个人,一个雕像或者任何阻挡视线的东西都会显着增加你的距离读数。 你甚至可能会在build筑物中发生build设性干扰(主要来自墙壁)的地方,使得一些地方的阅读比实际更接近。

这里是一个开源的java库,将执行三边测量/多边测量: https : //github.com/lemmingapex/Trilateration

例

它使用来自Apache Commons Math的stream行非线性最小二乘优化器Levenberg-Marquardtalgorithm。

 double[][] 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); 

大多math术性的例子,就像维基百科上的例子一样,恰好处理三个圆圈,并假设完全准确的信息。 这些情况允许有更确切答案的更简单的问题expression式,并且在实际情况下通常不令人满意。

在R 2或R 3欧氏空间中,包含测量误差,感兴趣的区域(椭圆)或体积(椭球体)的距离中的问题通常被获得而不是一个点。 如果需要点估计而不是区域,则应使用面积质心或体心。 R 2空间需要至less3个非退化点和距离才能获得唯一的区域; 同样的R 3空间需要至less4个非简并点和距离来获得唯一的区域。

使用iBeacon进行准确的室内定位将是一个挑战,原因如下:

  1. 正如前面的评论所指出的,iBeacon信号往往波动很大。 其原因包括多path效应,移动时手机与iBeacon之间的dynamic物体障碍,其他2.4GHz干扰等等。 因此理想情况下,您不希望信任单个数据包的数据,而是对来自同一信标的多个数据包进行平均。 这将要求在这几个数据包之间电话/信标距离不会变化太多。 对于一般的BLE数据包(如StickNFind的信标)可以很容易地设置为10Hz的信标速率。 但是对于iBeacon来说,这将是很难的,因为
  2. iBeacon的信标频率可能不会高于1Hz。 如果有人能够指出另有说法,我会很高兴,但是迄今为止所见到的所有信息都证实了这一说法。 这实际上是有道理的,因为大多数iBeacons将电池供电,高频显着影响电池寿命。 考虑到人们的平均步行速度是5.3km (〜1.5m / s),所以即使你只用一个适度的3个信标包进行平均,你也很难获得〜5m的准确度。

另一方面,如果您可以将iBeacon频率增加到大于10Hz(我怀疑是可能的),那么使用适当的处理方法可能具有5米或更高的精度。 首先,基于逆平方法的平凡解决scheme(如三边测量法 )常常performance不佳,因为实际上,不同的信标的距离/ RSSI关系通常与反向平方法不同,因为上述原因。 但是,只要RSSI在任何特定位置(通常情况下)都是相对稳定的,则可以使用称为指纹的方法来实现更高的准确度。 用于指纹识别的常用方法是kNN( k-最近邻 )。

2014-04-24更新

一些iBeacons 可以播放超过1Hz,比如Estimote默认使用5Hz。 然而,根据这个链接 :“ 这是苹果的限制,无论设备多么广告,IOS每秒钟都会更新一次信号灯。 ” 那里有另一个评论(可能来自Estimote供应商)说:“ 我们的信标可以更快地播放,它可以改善结果和测量 ”。 那么iBeacon频率是否有利还不清楚。

如果你像我一样,不喜欢math,你可能想快速search“室内定位sdk”。 有很多公司提供室内定位服务。

无耻的插件:我为indoo.rs工作,并可以推荐这项服务。 它还包括路由等“正义”室内定位之上。

对于那些需要@Javier Chávarrifunction的Android设备(节省一些时间):

 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; } 

我的架构师/经理写了下面的algorithm,

 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; //http://stackoverflow.com/a/524770/663941 //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; } 
  1. 计算三angular形的重心(3个信标)
  2. 计算最短距离/最近的信标
  3. 计算信标和重心之间的距离
  4. 将最短距离转换为坐标单位,这是一个常数,他用来预测准确性。 你可以testing变化的常数
  5. 计算距离增量
  6. 添加与最近的信标x,y的三angular洲。

经过testing,我发现它精确到5米。

请评论我的testing,如果我们可以改进它。

我为Android 4.4实现了一个非常简单的指纹algorithm,在相对“不好”的环境下进行了testing:

  • 附近有近10个wifi AP。
  • 其他几个蓝牙信号附近。

准确度似乎在5-8米,取决于我如何放置3 Ibeacon播放器。 algorithm很简单,我想你可以自己实现一个,步骤是:

  1. 加载室内地图。
  2. 用所有待定位点的地图进行采样。
  3. logging所有采样数据,数据应包括:地图坐标,位置信号及其RSSI。

所以当你开始定位的时候,这只是与步骤相反的一步。

我们还试图find使用iBeacons将某个人精确定位到一个房间的最佳方法。 问题在于信标信号功率不是恒定的,并且受到其他2.4Ghz信号,金属物体等的影响,为了达到最高的精度,需要单独地校准每个信标,并且一旦它被设置在期望的位置。 (并且进行一些现场testing以查看其他蓝牙设备出现时的信号波动)。 我们也有一些来自Estimote的iBeacons(与Konrad Dzwinel的video相同),他们已经开发了一些关于iBeacons可以做什么的技术演示。 在他们的应用程序中可以看到显示iBeacons的雷达。 有时候是相当准确的,但有时却不是,(似乎并没有考虑手机移动来计算头寸)。 查看我们在这里制作的video中的演示: http : //goo.gl/98hiza

虽然理论上3个iBeacons应该足以达到一个很好的精确度,但是在现实世界的情况下,需要更多的beacons来确保你正在寻找的精度。

真正帮助我的是Code.Google.com上的这个项目: https : //code.google.com/p/wsnlocalizationscala/它包含大量的代码和几种三边测量algorithm,全部用C#编写。 这是一个很大的图书馆,但并不是真正用于“开箱即用”的。

我发现Vishnu Prahbu的解决scheme非常有用。 我把它移植到C#,如果有人需要它。

 public static PointF GetLocationWithCenterOfGravity(PointF a, PointF b, PointF c, float dA, float dB, float dC) { //http://stackoverflow.com/questions/20332856/triangulate-example-for-ibeacons var METERS_IN_COORDINATE_UNITS_RATIO = 1.0f; //http://stackoverflow.com/a/524770/663941 //Find Center of Gravity var cogX = (aX + bX + cX) / 3; var cogY = (aY + bY + cY) / 3; var cog = new PointF(cogX,cogY); //Nearest Beacon PointF nearestBeacon; float shortestDistanceInMeters; if (dA < dB && dA < dC) { nearestBeacon = a; shortestDistanceInMeters = dA; } else if (dB < dC) { nearestBeacon = b; shortestDistanceInMeters = dB; } else { nearestBeacon = c; shortestDistanceInMeters = dC; } //http://www.mathplanet.com/education/algebra-2/conic-sections/distance-between-two-points-and-the-midpoint //Distance between nearest beacon and COG var distanceToCog = (float)(Math.Sqrt(Math.Pow(cog.X - nearestBeacon.X, 2) + Math.Pow(cog.Y - nearestBeacon.Y, 2))); //Convert shortest distance in meters into coordinates units. var 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 var t = shortestDistanceInCoordinationUnits / distanceToCog; var pointsDiff = new PointF(cog.X - nearestBeacon.X, cog.Y - nearestBeacon.Y); var tTimesDiff = new PointF(pointsDiff.X * t, pointsDiff.Y * t); //Add t times diff with nearestBeacon to find coordinates at a distance from nearest beacon in line to COG. var userLocation = new PointF(nearestBeacon.X + tTimesDiff.X, nearestBeacon.Y + tTimesDiff.Y); return userLocation; } 

替代方程

 - (CGPoint)getCoordinateWithBeaconA:(CGPoint)a beaconB:(CGPoint)b beaconC:(CGPoint)c distanceA:(CGFloat)dA distanceB:(CGFloat)dB distanceC:(CGFloat)dC { CGFloat x, y; x = ( ( (pow(dA,2)-pow(dB,2)) + (pow(cx,2)-pow(ax,2)) + (pow(by,2)-pow(ay,2)) ) * (2*cy-2*by) - ( (pow(dB,2)-pow(dC,2)) + (pow(cx,2)-pow(cx,2)) + (pow(cy,2)-pow(by,2)) ) *(2*by-2*ay) ) / ( (2*bx-2*cx)*(2*by-2*ay)-(2*ax-2*bx)*(2*cy-2*by) ); y = ( (pow(dA,2)-pow(dB,2)) + (pow(cx,2)-pow(ax,2)) + (pow(by,2)-pow(ay,2)) + x*(2*ax-2*bx)) / (2*by-2*ay); return CGPointMake(x, y); }