Поиск неизвестной точки с помощью взвешенной мультилатерации

У меня есть ряд точек (координаты широты/долготы) на земле и ряд оценок расстояния от каждой точки до неизвестного места. Я хотел бы использовать множество, чтобы оценить местоположение этого неизвестного места. Начиная с простого примера, представьте 4 точки и связанные с ними оценки расстояния до неизвестной точки с неизвестным местоположением:

широта, долгота, оценка расстояния 3-х кортежей ниже:

p1 = (31.2297, 121.4734, 3335.65)                           
p2 = (34.539, 69.171, 2477.17)                          
p3 = (47.907, 106.91, 1719.65)                      
p4 = (50.43, 80.25, 1242.27) 

Поиск неизвестной точки уже объяснялся здесь и пример трилатерации здесь. Используя приведенный выше пример, неизвестное находится в координатах широта/долгота: 36,989, 91,464.

Мой вопрос уникален, потому что я ищу способ выполнить мультилатерацию с весами. Каждая оценка расстояния является только оценкой; измерения неточны, но чем меньше расстояние, тем точнее измерение. Я хотел бы использовать мультилатерацию, но я хотел бы придать точкам, связанным с меньшими оценками расстояния, больший «вес» при определении окончательного ответа, поскольку эти более короткие оценки более точны. Как я могу это сделать? Я ищу решение в Python.

Возвращаясь к предыдущему примеру, но внося ошибку, я хочу снова найти неизвестное местоположение точки:

p1 = (31.2297, 121.4734, 4699.15)           
p2 = (34.539, 69.171, 2211.97)        
p3 = (47.907, 106.91, 1439.75)                              
p4 = (50.43, 80.25, 1222.07)    

person turtle    schedule 19.07.2013    source источник
comment
Можете ли вы описать, как ваша оценка ошибки, связанной с каждым измерением, изменяется в зависимости от расстояния — увеличивается ли она линейно? квадратично?   -  person ali_m    schedule 20.07.2013
comment
Это хороший вопрос. Для простоты предположим, что он линейный.   -  person turtle    schedule 20.07.2013


Ответы (1)


Хотя это, вероятно, не совсем то, что вы ищете, вы можете использовать это в качестве отправной точки:

import numpy as np
import scipy.optimize as opt

#Returns the distance from a point to the list of spheres
def calc_distance(point):
    return np.power(np.sum(np.power(centers-point,2),axis=1),.5)-rad

#Latitude/longitude to carteisan
def geo2cart(lat,lon):
    lat=np.deg2rad(lat)
    lon=np.deg2rad(lon)
    points=np.vstack((earth_radius*np.cos(lat)*np.cos(lon),
           earth_radius*np.cos(lat)*np.sin(lon),
           earth_radius*np.sin(lat))).T
    return points

#Cartesian to lat/lon
def cart2geo(xyz):
    if xyz.ndim==1: xyz=xyz[None,:]
    lat=np.arcsin(xyz[:,2]/earth_radius)
    lon=np.arctan2(xyz[:,1],xyz[:,0])
    return np.rad2deg(lat),np.rad2deg(lon)

#Minimization function. 
def minimize(point):
    dist= calc_distance(point)
    #Here you can change the minimization parameter, here the distances
    #from a sphere to a point is divided by its radius for linear weighting.
    err=np.linalg.norm(dist/rad)
    return err

earth_radius = 6378
p1 = (31.2297, 121.4734, 3335.65)
p2 = (34.539, 69.171, 2477.17)
p3 = (47.907, 106.91, 1719.65)
p4 = (50.43, 80.25, 1242.27)

points = np.vstack((p1,p2,p3,p4))
lat    = points[:,0]
lon    = points[:,1]
rad    = points[:,2]

centers = geo2cart(lat,lon)

out=[]
for x in range(30):
    latrand=np.average(lat/rad)*np.random.rand(1)*np.sum(rad)
    lonrand=np.average(lon/rad)*np.random.rand(1)*np.sum(rad)
    start=geo2cart(latrand,lonrand)
    end_pos=opt.fmin_powell(minimize,start)
    out.append([cart2geo(end_pos),np.linalg.norm(end_pos-geo2cart(36.989,91464))])


out = sorted(out, key=lambda x: x[1])
print 'Latitude:',out[0][0][0],'Longitude:',out[0][0][1],'Distance:',out[0][1]

Мы получаем:

First set of points:  lat 40.1105092 lon 88.07068701
Second set of points: lat 40.36636421 lon 88.84527729

Я уверен, что есть лучший способ, но, по крайней мере, вы можете поиграть с весами и функциями ошибок, чтобы увидеть, что произойдет. Конечно, есть несколько серьезных проблем, одна из которых заключается в том, что вы можете застрять в локальных минимумах. Вероятно, есть способ наименьших квадратов сделать это - я просто не вижу его в данный момент.

Просто чтобы дважды проверить, как это работает:

p0=np.random.rand(2)*90+20
p1=np.random.rand(2)*-10+20+p0
p2=np.random.rand(2)*-10+20+p0
p3=np.random.rand(2)*-10+20+p0
p4=np.random.rand(2)*-10+20+p0

target=geo2cart(p0[0],p0[1])
points=np.vstack((p1,p2,p3,p4))
lat    = points[:,0]
lon    = points[:,1]

centers=geo2cart(lat,lon)
#You can change the random at the end to tune the amount of noise
rad =  np.power(np.sum(np.power(centers-target,2),axis=1),.5)#+np.random.rand(4)*10    

print '------------'
start=geo2cart(np.average(lat),np.average(lon))
end_pos=opt.fmin_powell(minimize,start)
print 'Exact',p0
print 'Start guess',cart2geo(start)
print 'Found',cart2geo(end_pos)
print 'Distance',np.linalg.norm(end_pos-target)

Exact [  45.21292244  101.85151772]
Start guess (array([ 60.63554123]), array([ 115.08426225]))
Found (array([ 45.21292244]), array([ 101.85151772]))
Distance 5.30420680512e-11
person Daniel    schedule 20.07.2013
comment
Спасибо за эту большую работу. Одна вещь, которую я не понимаю, это то, где вы получаете ответы на первый и второй наборы пунктов. Например, для первого набора точек вы получаете решение 40.1105092, 88.07068701, которое далеко (~ 500 км) от фактического местоположения. Кроме того, когда я запускаю ваш скрипт, cart2geo() дает широту и долготу прогноза в 54.85960821, 89.49203514, а не 40.1105092, 88.07068701. Я правильно это понимаю? Можете ли вы уточнить свое объяснение? - person turtle; 20.07.2013
comment
Я играл с начальными точками — как уже упоминалось, вы можете легко застрять в локальных минимумах. Какой шум содержится в исходных данных? Небольшие изменения в шуме могут немного изменить ответ. Также ваш второй набор очков под СВД дает кардинально другой результат. - person Daniel; 20.07.2013