Как определить путь по шумным данным X, Y - PullRequest
9 голосов
/ 27 октября 2008

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

Это похоже на то, как вы бы использовали алгоритм подгонки линий для выбора аппроксимации линейных данных. Моя проблема только сложнее, потому что путь изгибается и извивается вокруг света. альтернативный текст http://www.praeclarum.org/so/pathfinder.png

Кто-нибудь знает какие-либо стандартные / надежные / простые для понимания алгоритмы для достижения этой цели?

Q & A

Что вы подразумеваете под шумом? Если бы у меня была идеальная реализация пути, тогда мой набор точек был бы выбран из этого идеального пути с гауссовым шумом, добавленным к элементам X и Y. Я не знаю среднего или стандартного отклонения этого шума. Я могу быть в состоянии угадать на STD Dev ...

Точки лежат рядом, но не на каком-то идеальном, но сложном пути, который вы стремитесь приблизить? Да.

Есть ли у вас априорная информация о форме пути? Есть ли другой способ получить такую ​​информацию? К сожалению, нет.

Ответы [ 10 ]

5 голосов
/ 27 октября 2008

Интерполяция Безье может соответствовать вашей проблеме.

Bezier Interpolation

Однако это не относится к упорядочению точек на пути; Есть несколько подходов для рассмотрения:

  • Любой «оптимальный» тип пути (например, наименьшее изменение направления в каждой точке пути, * Кратчайший путь через все точки), скорее всего, приведет к завершению NP. 1013 *
  • «Разумный» путь для кластеризации узлов, а затем маршрутизации между кластерами и внутри кластеров. Конечно, чем больше кластер или чем больше кластеров, тем больше эта меньшая проблема выглядит как большой n TSP.
  • Упорядочение точек по одной оси. Если имеется более двух осей, может быть полезна стратегия уменьшение размеров . например Независимый компонентный анализ.
4 голосов
/ 27 октября 2008

С несортированным списком вы не будете точно знать, какие точки включить в каждый сегмент, поэтому я думаю, вы могли бы просто пойти с ближайшей точкой.

Одним из способов может быть случайная выборка начальной точки и выбор ближайшей точки в качестве следующей точки на каждом шаге. Добавьте первые две точки к набору S.

Установите линию на точки в S, пока среднеквадратичное значение не превысит некоторое значение, затем очистите S и начните новую линию.

Пересечение последовательных линий будет конечными точками отрезков.

2 голосов
/ 28 октября 2008

Проблема с кривой Безье состоит в том, что она фактически не работает, хотя точки, которые вы выбрали, и даже если образцы точек немного искажены; кривая Безье на самом деле может быть за несколько миль.

Лучшим приближением и решением, которое, похоже, лучше напоминает исходное изображение, является Сплайн Catmull-Rom , потому что он работает через все точки кривой.

2 голосов
/ 27 октября 2008

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

  • у вас достаточно очков
  • среднее расстояние между точками мало по сравнению с наименьшим радиусом кривизны, ожидаемым для траектории
  • среднее расстояние между точками невелико по сравнению со стандартным. девиация шума
  • путь не самопересекающийся (вам может повезти, но без гарантий)

Действуйте так:

  1. Выберите (надеюсь, значимым, а не случайным образом) отправную точку, p1 .
  2. Найдите все точки, которые находятся в пределах некоторого расстояния кластеризации, r_c из p1 . Выберите r_c маленький по сравнению с ожидаемым радиусом поворота, но большой по сравнению с разбросом.
  3. Назовите этот кластер C1 .
  4. Найти точку q1 среднее значение позиций в C1 .
  5. Установите линию на точки в C1 и спроецируйте (или чуть дальше) край кластера и найдите ближайшую точку в ваших исходных данных. Обозначьте эту точку p2 .
  6. Повторяйте шаги 2-5, пока у вас не закончатся данные.

Теперь у вас есть новый список q1 .. qn , которые заказаны.

С макушки головы, очень грубо, и работает только в довольно хороших условиях ...


Поведение самопересечения, вероятно, можно улучшить, если на шаге (5) потребуется, чтобы новая спроецированная линия находилась в пределах некоторого максимального угла предыдущей.

2 голосов
/ 27 октября 2008

Если ваши точки расположены близко друг к другу, вы можете использовать обычные «прямые» линии (ортогональные линии). Используя обычные алгоритмы сглаживания . Вы можете видеть мир как плоский.

Если они находятся далеко друг от друга, вам необходимо компенсировать округление земли, используя большие круги для навигации от точки к точке. В противном случае ваши прямые линии пройдут более длинный путь.

Это ваш выбор, если точка находится слишком далеко, чтобы создать прямые линии.

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

Если вам нужно отправить курс (ы) на самолет, корабль или другого путешественника, вам, вероятно, нужно посетить каждую точку. Если вы получаете данные GPS от объекта, вы, вероятно, просто хотите нанести курс на экран и удалить шум.


После просмотра ваших правок: Если это объект, движущийся по некоторой траектории, которую вы хотите построить, вы можете сгладить направление и скорость вместо значений x / y. (Придание вашим измеренным значениям (x) фиксированного и возрастающего интервала Y значительно облегчает сглаживание.)

1 голос
/ 08 ноября 2008

Я так понимаю, что "несортированный список" означает, что, хотя ваш набор точек завершен, вы не знаете, в каком порядке они прошли?

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

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

Итак, вот код схемы PLT, который делает это.

#lang scheme

(require (only-in srfi/1 iota))

; a bunch of trig
(define (deg->rad d)
  (* pi (/ d 180)))

(define (rad->deg r)
  (* 180 (/ r pi)))

(define (euclidean-length v)
  (sqrt (apply + (map (lambda (x) (expt x 2)) v))))

(define (dot a b)
  (apply + (map * a b)))

(define (angle-ratio a b)
  (/ (dot a b)
     (* (euclidean-length a) (euclidean-length b))))

; given a list of 3 points, calculate the likelihood of the
; angle they represent. straight is better.
(define (probability-triple a b c)
  (let ([av (map - a b)]
        [bv (map - c b)])
    (cos (/ (- pi (abs (acos (angle-ratio av bv)))) 2))))

; makes a random 2d point. uncomment the bit for a 3d point
(define (random-point . x)
  (list (/ (random 1000) 100)
        (/ (random 1000) 100)
        #;(/ (random 1000) 100)))

; calculate the likelihood of an entire list of points
(define (point-order-likelihood lst)
  (if (null? (cdddr lst))
      1
      (* (probability-triple (car lst)
                             (cadr lst)
                             (caddr lst))
         (point-order-likelihood (cdr lst)))))

; just print a list of points
(define (print-points lst)
  (for ([p (in-list lst)])
    (printf "~a~n"
            (string-join (map number->string
                              (map exact->inexact p))
                         " "))))

; attempts to improve upon a list
(define (find-better-arrangement start
                                 ; by default, try only 10 times to find something better
                                 [tries 10]
                                 ; if we find an arrangement that is as good as one where
                                 ; every segment bends by 22.5 degrees (which would be
                                 ; reasonably gentle) then call it good enough. higher
                                 ; cut offs are more demanding.
                                 [cut-off (expt (cos (/ pi 8))
                                                (- (length start) 2))])
  (let ([vec (list->vector start)]
        ; evaluate what we've started with
        [eval (point-order-likelihood start)])
    (let/ec done
      ; if the current list exceeds the cut off, we're done
      (when (> eval cut-off)
        (done start))
      ; otherwise, try no more than 'tries' times...
      (for ([x (in-range tries)])
        ; pick two random points in the list
        (let ([ai (random (vector-length vec))]
              [bi (random (vector-length vec))])
          ; if they're the same...
          (when (= ai bi)
            ; increment the second by 1, wrapping around the list if necessary
            (set! bi (modulo (add1 bi) (vector-length vec))))
          ; take the values from the two positions...
          (let ([a  (vector-ref vec ai)]
                [b  (vector-ref vec bi)])
            ; swap them
            (vector-set! vec bi a)
            (vector-set! vec ai b)
            ; make a list out of the vector
            (let ([new (vector->list vec)])
              ; if it evaluates to better
              (when (> (point-order-likelihood new) eval)
                ; start over with it
                (done (find-better-arrangement new tries cut-off)))))))
      ; we fell out the bottom of the search. just give back what we started with
      start)))

; evaluate, display, and improve a list of points, five times
(define points (map random-point (iota 10)))
(define tmp points)
(printf "~a~n" (point-order-likelihood tmp))
(print-points tmp)
(set! tmp (find-better-arrangement tmp 10))
(printf "~a~n" (point-order-likelihood tmp))
(print-points tmp)
(set! tmp (find-better-arrangement tmp 100))
(printf "~a~n" (point-order-likelihood tmp))
(print-points tmp)
(set! tmp (find-better-arrangement tmp 1000))
(printf "~a~n" (point-order-likelihood tmp))
(print-points tmp)
(set! tmp (find-better-arrangement tmp 10000))
(printf "~a~n" (point-order-likelihood tmp))
(print-points tmp)
1 голос
/ 27 октября 2008

Совершенно другой подход, который не требует другого ограничения, но детали могут зависеть от вашего приложения. Лучше всего сработает, если вокруг пути будет «плотное облако точек».

Используйте функцию «стоимость», которая определяет разницу между кривой и облаком точек. Используйте параметризованную кривую и стандартный алгоритм оптимизации. - ИЛИ ЖЕ - Начните с прямой кривой от начала до конца, затем используйте генетический алгоритм для ее изменения.

Типичная функция стоимости - взять наименьшее расстояние между каждой точкой и кривой и сложить квадраты.

У меня недостаточно опыта, чтобы предложить оптимизацию или генетический алгоритм, но я уверен, что это можно сделать:)

Я мог бы представить генетический алгоритм следующим образом: Путь будет построен из путевых точек. Начните с размещения N путевых точек в прямой линии от начала до конца. (N можно выбрать в зависимости от проблемы). Мутации могут быть:

  1. Для каждого сегмента, если rnd ()
  2. Для каждой путевой точки координаты X и Y немного различаются.

Вам нужно будет включить общую длину в функцию стоимости. Разделение может не потребоваться, или, возможно, x («шанс разделения») может потребоваться уменьшить, поскольку вводится больше путевых точек. Вы можете или не можете применить (2) к начальной и конечной точке.

Было бы интересно попробовать это ...

1 голос
/ 27 октября 2008

Мой подход заключается в том, чтобы сначала отсортировать список точек, а затем использовать кривую Безье.

Хитрость, конечно, в сортировке. Начните с одной случайной точки и найдите ближайшую точку. Предположим, что эти два связаны. С этими двумя конечными точками найдите ближайшие точки к ним. Предположим, что тот с меньшим расстоянием до его конечной точки связан с этой точкой. Повторяйте, пока все точки не соединятся.

Я предполагаю, что с этим подходом все еще есть некоторые проблемы, но, возможно, вы можете использовать его как отправную точку (каламбур).

Изменить: Вы можете сделать это несколько раз с разными начальными точками, а затем посмотреть, где результаты отличаются. Это, по крайней мере, дает вам некоторую уверенность, какие точки связаны друг с другом.

0 голосов
/ 27 октября 2008

Сколько у вас очков?
Кривая Безье, как уже упоминалось, является хорошей идеей, если у вас сравнительно мало точек. Если у вас много точек, создайте кластеры в соответствии с рекомендациями dmckee.

Однако вам также нужно другое ограничение для определения порядка точек. Было много хороших предложений о том, как выбирать точки, но если вы не введете другое ограничение, любое из них даст возможное решение.

Возможные ограничения, которые я могу придумать:

  • кратчайший путь
  • большинство прямых сегментов
  • Наименьшее полное абсолютное вращение
  • Направленное предпочтение (то есть горизонтальное / вертикальное более вероятно, чем перекрещивание)

Во всех случаях, чтобы соответствовать ограничению, вам, вероятно, необходимо проверить все перестановки последовательности. Если вы начнете с «правильной догадки», вы быстро прекратите работу других.

0 голосов
/ 27 октября 2008

Похоже, что вы знаете «золотую кривую» по вашим ответам на вопросы, я бы предложил найти кривую Безье «золотой кривой», как предложено @jamesh, и нарисовать ее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...