Ske*_*een 5 math distance triangulation approximation trilateration
我需要帮助解决一个问题,问题出现在我的小机器人实验之一,基本的想法是,每个小机器人都能够近似距离,从他们自己到物体,然而近似我得到的方式太粗糙了,我希望能够更准确地计算出更多东西.
所以:
输入:顶点列表(v_1, v_2, ... v_n),顶点v_*(机器人)
输出:未知顶点v_*(对象)的坐标
每个顶点v_1到v_n的是公知的(通过调用提供的坐标getX()和getY()在顶点),并且其可能获得的大致范围内,以v_*通过调用; getApproximateDistance(v_*),函数getApproximateDistance()返回两个变量变量,即; minDistance和maxDistance. - 实际距离介于两者之间.
所以我一直试图获得坐标v_*,是使用trilateration,但我似乎无法找到一个用限制(下限和上限)进行三边测量的公式,所以这就是我真正想要的(在数学方面不够好,自己搞清楚).
注意:是三角测量的方式而不是?
注意:我可能想知道一种方法,性能/准确性权衡.
数据示例:
[Vertex . `getX()` . `getY()` . `minDistance` . `maxDistance`]
[`v_1` . 2 . 2 . 0.5 . 1 ]
[`v_2` . 1 . 2 . 0.3 . 1 ]
[`v_3` . 1.5 . 1 . 0.3 . 0.5]
Run Code Online (Sandbox Code Playgroud)
图片显示数据:http://img52.imageshack.us/img52/6414/unavngivetcb.png
很明显,近似值v_1可以更好,[0.5; 1]因为上面数据创建的数字是环的小切口(受限v_3),但是我如何计算,并且可能在该图中找到近似值(这个数字是可能是凹的)?
这会更适合MathOverflow吗?
我会采用简单的离散方法。环面的隐式公式很简单,如果多个环面的数量很多,则可以使用基于扫描线的方法稍微有效地计算多个环面的交集。
为了通过快速计算获得高精度,可以选择使用多分辨率方法(即首先以低分辨率开始,然后仅以接近有效点的高分辨率样本重新计算。
我编写的一个小型 Python 玩具可以在大约 0.5 秒内生成交叉区域的 400x400 像素图像(这种计算如果使用 C 语言完成,将获得 100 倍的加速)。

# x, y, r0, r1
data = [(2.0, 2.0, 0.5, 1.0),
(1.0, 2.0, 0.3, 1.0),
(1.5, 1.0, 0.3, 0.5)]
x0 = max(x - r1 for x, y, r0, r1 in data)
y0 = max(y - r1 for x, y, r0, r1 in data)
x1 = min(x + r1 for x, y, r0, r1 in data)
y1 = min(y + r1 for x, y, r0, r1 in data)
def hit(x, y):
for cx, cy, r0, r1 in data:
if not (r0**2 <= ((x - cx)**2 + (y - cy)**2) <= r1**2):
return False
return True
res = 400
step = 16
white = chr(255)
grey = chr(192)
black = chr(0)
img = [black] * (res * res)
# Low-res pass
cells = {}
for i in xrange(0, res, step):
y = y0 + i * (y1 - y0) / res
for j in xrange(0, res, step):
x = x0 + j * (x1 - x0) / res
if hit(x, y):
for h in xrange(-step*2, step*3, step):
for v in xrange(-step*2, step*3, step):
cells[(i+v, j+h)] = True
# High-res pass
for i in xrange(0, res, step):
for j in xrange(0, res, step):
if cells.get((i, j), False):
img[i * res + j] = grey
img[(i + step - 1) * res + j] = grey
img[(i + step - 1) * res + (j + step - 1)] = grey
img[i * res + (j + step - 1)] = grey
for v in xrange(step):
y = y0 + (i + v) * (y1 - y0) / res
for h in xrange(step):
x = x0 + (j + h) * (x1 - x0) / res
if hit(x, y):
img[(i + v)*res + (j + h)] = white
open("result.pgm", "wb").write(("P5\n%i %i 255\n" % (res, res)) +
"".join(img))
Run Code Online (Sandbox Code Playgroud)
另一个有趣的选择是使用 GPU(如果可用)。从白色图片开始并用黑色绘制,每个环的外部将在末端留下白色的交叉区域。
例如,使用 Python/Qt 进行此计算的代码很简单:
img = QImage(res, res, QImage.Format_RGB32)
dc = QPainter(img)
dc.fillRect(0, 0, res, res, QBrush(QColor(255, 255, 255)))
dc.setPen(Qt.NoPen)
dc.setBrush(QBrush(QColor(0, 0, 0)))
for x, y, r0, r1 in data:
xa1 = (x - r1 - x0) * res / (x1 - x0)
xb1 = (x + r1 - x0) * res / (x1 - x0)
ya1 = (y - r1 - y0) * res / (y1 - y0)
yb1 = (y + r1 - y0) * res / (y1 - y0)
xa0 = (x - r0 - x0) * res / (x1 - x0)
xb0 = (x + r0 - x0) * res / (x1 - x0)
ya0 = (y - r0 - y0) * res / (y1 - y0)
yb0 = (y + r0 - y0) * res / (y1 - y0)
p = QPainterPath()
p.addEllipse(QRectF(xa0, ya0, xb0-xa0, yb0-ya0))
p.addEllipse(QRectF(xa1, ya1, xb1-xa1, yb1-ya1))
p.addRect(QRectF(0, 0, res, res))
dc.drawPath(p)
Run Code Online (Sandbox Code Playgroud)
800x800 分辨率图像的计算部分大约需要 8 毫秒(我不确定它是否有硬件加速)。
如果只计算交点的重心,则根本不需要内存分配。例如,“暴力”方法只需几行 C
typedef struct TReading {
double x, y, r0, r1;
} Reading;
int hit(double xx, double yy,
Reading *readings, int num_readings)
{
while (num_readings--)
{
double dx = xx - readings->x;
double dy = yy - readings->y;
double d2 = dx*dx + dy*dy;
if (d2 < readings->r0 * readings->r0) return 0;
if (d2 > readings->r1 * readings->r1) return 0;
readings++;
}
return 1;
}
int computeLocation(Reading *readings, int num_readings,
int resolution,
double *result_x, double *result_y)
{
// Compute bounding box of interesting zone
double x0 = -1E20, y0 = -1E20, x1 = 1E20, y1 = 1E20;
for (int i=0; i<num_readings; i++)
{
if (readings[i].x - readings[i].r1 > x0)
x0 = readings[i].x - readings[i].r1;
if (readings[i].y - readings[i].r1 > y0)
y0 = readings[i].y - readings[i].r1;
if (readings[i].x + readings[i].r1 < x1)
x1 = readings[i].x + readings[i].r1;
if (readings[i].y + readings[i].r1 < y1)
y1 = readings[i].y + readings[i].r1;
}
// Scan processing
double ax = 0, ay = 0;
int total = 0;
for (int i=0; i<=resolution; i++)
{
double yy = y0 + i * (y1 - y0) / resolution;
for (int j=0; j<=resolution; j++)
{
double xx = x0 + j * (x1 - x0) / resolution;
if (hit(xx, yy, readings, num_readings))
{
ax += xx; ay += yy; total += 1;
}
}
}
if (total)
{
*result_x = ax / total;
*result_y = ay / total;
}
return total;
}
Run Code Online (Sandbox Code Playgroud)
resolution = 100在我的电脑上可以在 0.08 毫秒(x=1.50000,y=1.383250)或1.3 毫秒(x=1.500000,y=1.383308)内计算重心resolution = 400。当然,即使对于仅重心的版本也可以实现双步加速。