有限制的三边形?

Ske*_*een 5 math distance triangulation approximation trilateration

我需要帮助解决一个问题,问题出现在我的小机器人实验之一,基本的想法是,每个小机器人都能够近似距离,从他们自己到物体,然而近似我得到的方式太粗糙了,我希望能够更准确地计算出更多东西.

所以:
输入:顶点列表(v_1, v_2, ... v_n),顶点v_*(机器人)
输出:未知顶点v_*(对象)的坐标

每个顶点v_1v_n的是公知的(通过调用提供的坐标getX()getY()在顶点),并且其可能获得的大致范围内,以v_*通过调用; getApproximateDistance(v_*),函数getApproximateDistance()返回两个变量变量,即; minDistancemaxDistance. - 实际距离介于两者之间.

所以我一直试图获得坐标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吗?

650*_*502 1

我会采用简单的离散方法。环面的隐式公式很简单,如果多个环面的数量很多,则可以使用基于扫描线的方法稍微有效地计算多个环面的交集。

为了通过快速计算获得高精度,可以选择使用多分辨率方法(即首先以低分辨率开始,然后仅以接近有效点的高分辨率样本重新计算。

我编写的一个小型 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。当然,即使对于仅重心的版本也可以实现双步加速。