Обнаружение коллизий огромного количества кругов

Я не уверен на 100%, что ошибка в ваших данных, но попробуйте запустить код с

data = fread(input = "../data.txt", sep = "\t", fill = TRUE)

в параметрах fread. У меня была аналогичная ошибка, и казалось, что у fread возникли проблемы с идентификацией разделения столбцов. Установка fill в true позволяет fread заполнять любые отсутствующие данные - по крайней мере, тогда вы можете проверить полученный результирующий фрейм данных и выяснить, где находится странность.

52
задан Michael Myers 30 March 2010 в 04:17
поделиться

9 ответов

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

Решение 1
Разделите ваше пространство на прямоугольные ячейки, которые не перекрываются. Таким образом, когда вы проверяете один круг на столкновение, вы ищете все круги внутри клетки, в которой находится ваш первый круг, и просматриваете X клеток в каждом направлении вокруг. Я пробовал много значений X и обнаружил, что X=1 является самым быстрым решением. Итак, вы должны разделить пространство на ячейки размером в каждом направлении, равным:

Divisor = SimulationBoxSize / MaximumCircleDiameter;
CellSize = SimulationBoxSize / Divisor;

Делитель должен быть больше 3, иначе это приведет к ошибкам (если он слишком мал, вам следует увеличить окно моделирования).
Тогда ваш алгоритм будет выглядеть так:

  1. Поместите все круги внутрь коробки
  2. Создайте структуру ячеек и храните индексы или указатели на круги внутри ячеек (в массиве или списке)
  3. Сделайте шаг во времени (переместите все) и обновите позиции кругов внутри ячеек
  4. Осмотрите каждый круг на предмет столкновения. Вы должны проверить одну клетку вокруг в каждом направлении
  5. Если есть столкновение - сделайте что-нибудь
  6. Перейдите к 3.

Если вы напишете это правильно, то у вас будет что-то около O(N) сложности, потому что максимальное количество кругов внутри 9 клеток (в 2D) или 27 клеток (в 3D) постоянно для любого общего количества кругов.

Решение 2
Обычно это делается следующим образом:

  1. Для каждой окружности создаем список окружностей, находящихся на расстоянии R < R_max, вычисляем время, через которое нужно обновить списки (что-то около T_update = R_max / V_max; где V_max - максимальная текущая скорость)
  2. Делаем шаг по времени
  3. Проверяем расстояние каждого круга с кругами в его списке
  4. Если есть столкновение - что-то делаем
  5. Если текущее время больше T_update, переходим к 1.
  6. Иначе переходим к 2.

Это решение со списками очень часто улучшается путем добавления еще одного списка с R_max_2 > R_max и с собственным T_2 временем истечения. В данном решении этот второй список используется для обновления первого списка. Конечно, после T_2 вам придется обновить все списки, что составляет O(N^2). Также будьте осторожны с временами T и T_2, потому что если столкновение может изменить скорость, то эти времена изменятся. Кроме того, если вы введете в систему некоторые предвестники, то это также приведет к изменению скорости.

Решение 1+2 Вы можете использовать списки для обнаружения столкновений и ячейки для обновления списков. В одной книге было написано, что это лучшее решение, но я думаю, что если вы создаете маленькие ячейки (как в моем примере), то решение 1 лучше. Но это мое мнение.

Другие вещи
Вы также можете сделать другие вещи для улучшения скорости моделирования:

  1. Когда вы рассчитываете расстояние r = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) + ...) вам не нужно делать операцию квадратного корня. Вы можете сравнить r^2 с некоторым значением - это нормально. Также не нужно делать все операции (x1-x2)*(x1-x2) (в смысле, для всех размерностей), потому что если x*x больше некоторого r_collision^2, то все остальные y*y и так далее, суммированные, будут больше.
  2. Метод молекулярной динамики очень легко распараллелить. Вы можете делать это с помощью потоков или даже на GPU. Вы можете вычислять каждое расстояние в разных потоках. На GPU вы можете легко создать тысячи потоков почти без затрат.

Для жестких сфер есть также эффективный алгоритм, который не делает шагов во времени, а вместо этого ищет ближайшее столкновение во времени, переходит к этому времени и обновляет все позиции. Это может быть хорошо для не плотных систем, где столкновения не очень вероятны.

15
ответ дан 7 November 2019 в 09:35
поделиться

Если Ваш код зависит от "галочки" (и тестирует, чтобы определить если перекрытие объектов в галочке), то:

  • , когда объекты перемещаются "слишком быстро", они перескакивают друг через друга без сталкивающегося

  • , когда несколько объектов сталкиваются в той же галочке, конечный результат (например, как они возвращаются, сколько ущерба они наносят...) зависит от порядка, который Вы проверяете на коллизии а не порядок, что коллизии/должны, происходят. В редких случаях это может заставить игру запираться (например, 3 объекта сталкиваются в той же галочке; object1 и object2 корректируются для их коллизии, затем object2, и object3 корректируются для их коллизии, вызывающей object2 для столкновения с object1 снова, таким образом, коллизия между object1 и object2 должна быть восстановлена, но это заставляет object2 сталкиваться с object3 снова, так...).

Примечание: В теории эта вторая проблема может быть решена "рекурсивным подразделением галочки" (если больше чем 2 объекта сталкиваются, разделяют длину пополам галочки и повторной попытки, пока только 2 объекта не сталкиваются в той "подгалочке"). Это может также заставить игры запираться и/или отказывать (когда 3 или больше объекта сталкиваются в тот же самый момент, Вы заканчиваете с, "рекурсивно вызывают навсегда" сценарий).

, Кроме того; иногда, когда разработчики игр используют "галочки", они также говорят "1 галочку фиксированной длины = 1 / переменная частота кадров", которая абсурдна, потому что что-то, что, как предполагается, является фиксированной длиной, не может зависеть от чего-то переменного (например, когда GPU не удается достигнуть 60 кадров в секунду, все моделирование входит в замедленное воспроизведение); и если они не делают этого и имеют "галочки переменной длины" вместо этого затем, обе из проблем с "галочками" становятся значительно хуже (особенно в низкой частоте кадров), и моделирование становится недетерминированным (который может быть проблематичным для многопользовательского, и может привести к другому поведению, когда плеер сохраняет, загружает или приостанавливает игру).

единственный корректный путь состоит в том, чтобы добавить размер (время) и дать каждому объекту линейный сегмент, описанный как "запуск координат и окончание координат", плюс "траектория после окончания координат". Когда любой объект изменяет свою траекторию (или потому что что-то непредсказанное произошло или потому что это достигло своих "конечных координат"), Вы найдете "как можно скорее" коллизия путем выполнения "расстояния между 2 строками < (object1.radius + object2.radius)" вычисление для объекта, который изменился и любой объект; затем измените "конечные координаты" и "траекторию после окончания координат" для обоих объектов.

внешний "игровой цикл" был бы чем-то как:

    while(running) {
        frame_time = estimate_when_frame_will_be_visible(); // Note: Likely to be many milliseconds after you start drawing the frame
        while(soonest_object_end_time < frame_time) {
              update_path_of_object_with_soonest_end_time();
        }
        for each object {
            calculate_object_position_at_time(frame_time);
        }
        render();
    }

Примечание, что существует несколько способов оптимизировать это, включая:

  • разделяет мир на "зоны" - например, так, чтобы, если Вы знаете, object1 прошел бы через зоны 1, и 2 затем он не может столкнуться ни с каким другим объектом, который также не проходит через зону 1, или зона 2

  • удерживают объекты "end_time % bucket_size" блоки для уменьшения времени, потраченного, чтобы найти "затем как можно скорее, что время окончания"

  • использование несколько потоков делает "calculate_object_position_at_time (frame_time)"; для каждого объекта в параллели

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

Для производительности:

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

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

Это также делает это тривиальным, чтобы иметь произвольные отношения между "временем симуляции" и "реальное время" - вещи как ускоренная перемотка вперед и замедленное воспроизведение не заставят ничего повреждаться (даже если моделирование будет работать с такой скоростью, как аппаратные средства могут обработать или столь медленный, что его твердое, чтобы сказать, перемещается ли что-нибудь вообще); и (в отсутствие непредсказуемости) можно вычислить вперед к произвольному моменту времени в будущем, и (если Вы храните старый "объектный линейный сегмент" информация вместо того, чтобы отбросить его, когда это истекает), можно пропустить к произвольному моменту времени в прошлом и (если Вы только храните старую информацию в отдельных моментах вовремя для уменьшения затрат на хранение), можно пропустить назад ко времени, описанному хранившей информацией, и затем вычислить вперед к произвольному моменту времени. Эти вещи, объединенные также, помогают сделать вещи как "мгновенное воспроизведение замедленного воспроизведения".

Наконец; также более удобно для многопользовательских сценариев, где Вы не хотите тратить впустую огромную сумму пропускной способности, отправляющей "новое местоположение" за каждым объектом каждому клиенту в каждой галочке.

, Конечно, оборотная сторона является сложностью - как только Вы хотите иметь дело с вещами как ускорение/замедление (сила тяжести, трение, покачнувшееся перемещение), плавные кривые (эллиптические орбиты, шлицы) или объекты различной формы (например, произвольные сетки/полигоны и не сферы/круги) математика, вовлеченная в вычисление, когда как можно скорее коллизия произойдет, становится значительно более твердым и более дорогим; который является, почему разработчики игр обращаются к нижнему подходу "галочек" для моделирований, которые более сложны, чем случай сфер N или кругов с линейным движением.

0
ответ дан 7 November 2019 в 09:35
поделиться

Какой ответ будет наиболее эффективным, в некоторой степени будет зависеть от плотности кругов. Если плотность низкая, то размещение сетки с низким разрешением на карте и отметка тех элементов сетки, которые содержат круг, вероятно, будет наиболее эффективным.Это займет приблизительно O (N * m * k) на обновление, где N - общее количество кругов, m - среднее количество кругов за точка сетки, а k - среднее количество точек сетки, охватываемых одним кругом. Если один круг перемещает более одной точки сетки за оборот, вам необходимо изменить м , чтобы включить количество перемещаемых точек сетки.

С другой стороны, если плотность чрезвычайно высока, вам лучше попробовать метод обхода графика. Пусть каждый круг содержит всех соседей на расстоянии R ( R > r_i для каждого радиуса круга r_i ). Затем, если вы двигаетесь, вы запрашиваете все круги в «прямом» направлении о соседях, которые у них есть, и берете все, что будет в пределах D; тогда вы забываете все те, которые в обратном направлении находятся дальше, чем D. Теперь полное обновление займет O (N * n ^ 2) , где n - среднее количество круги в радиусе R . Для чего-то вроде близкорасположенной гексагональной решетки это даст вам гораздо лучшие результаты, чем метод сетки, описанный выше.

2
ответ дан 7 November 2019 в 09:35
поделиться

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

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

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

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

4
ответ дан 7 November 2019 в 09:35
поделиться

Существуют структуры данных « пространственный индекс » для хранения ваших кругов для последующего быстрого сравнения; Quadtree, r-tree и kd-tree являются примерами.

Решение 1 похоже на пространственный индекс, а решение 2 выиграет от пространственного индекса каждый раз, когда вы пересчитываете свои пары.

Чтобы усложнить ситуацию, ваши объекты движутся - у них есть скорость.

Использование пространственных индексов для объектов в играх и симуляторах является нормальным, но в основном это касается стационарных объектов и, как правило, объектов, которые не реагируют на столкновение движением.

Это нормальное явление в играх и такое, что вы вычисляете все через заданные временные интервалы (дискретные), поэтому может случиться так, что два объекта проходят через друг друга, но вы не замечаете, потому что они двигались очень быстро. Многие игры даже не оценивают столкновения в строгом хронологическом порядке. У них есть пространственный индекс для стационарных объектов, например. стены и списки всех движущихся объектов, которые они тщательно проверяют (хотя и с расслабленными дискретными проверками, как я обрисовал).

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

Обрисованный вами парный подход звучит многообещающе. Вы можете сохранить пары отсортированными по следующему столкновению и повторно вставить их, когда они столкнулись в соответствующих новых положениях. Вам нужно только отсортировать новый сгенерированный список столкновений (O (n lg n)) для двух объектов, а затем объединить два списка (новые коллизии для каждого объекта и существующий список коллизий; вставка новых коллизий, удаление тех устаревшие коллизии, в которых перечислены два объекта, которые столкнулись), что составляет O (n).

Еще одно решение этой проблемы - адаптировать пространственный индекс для хранения объектов не строго в одном секторе, а в каждом, через который он прошел с момента последнего вычисления, и действовать дискретно. Это означает хранение быстро движущихся объектов в вашей пространственной структуре, и вам нужно будет оптимизировать ее для этого случая.

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

Как говорит Марк в комментариях, было бы довольно просто распараллелить вычисления.

17
ответ дан 7 November 2019 в 09:35
поделиться

Вы можете создать 2D-версию «сферического дерева», которое представляет собой особый (и очень простой в реализации) случай «пространственного индекса», который будет предложенный. Идея состоит в том, чтобы «объединить» круги в «содержащий» круг, пока вы не получите единственный круг, который «содержит» «огромное количество кругов».

Просто чтобы показать простоту вычисления «содержащего круга» (верхняя часть моей головы): 1) Добавьте центры двух кругов (как векторы) и масштабируйте на 1 / 2, это центр окружности 2) Вычтите положения центров двух окружностей (как векторы), добавьте радиусы и масштаб на 1/2, то есть радиус окружности

{ {1}}
2
ответ дан 7 November 2019 в 09:35
поделиться

Разделите свое пространство на регионы и ведите список кругов с центром в каждом регионе.

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

3
ответ дан 7 November 2019 в 09:35
поделиться

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

Я видел, как ваше "решение 1" использовалось для этой проблемы раньше и называлось "хэш столкновений". Оно может хорошо работать, если пространство, с которым вы имеете дело, достаточно мало, чтобы быть управляемым, и вы ожидаете, что ваши объекты будут хотя бы смутно близки к равномерному распределению. Если ваши объекты могут быть сгруппированы, то очевидно, что это создает проблему. Использование гибридного подхода с некоторым типом дерева разделов внутри каждого хэш-бокса может помочь с этим и может преобразовать чисто древовидный подход в нечто, что легче масштабировать параллельно.

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

1
ответ дан 7 November 2019 в 09:35
поделиться

Предложение - я не разработчик игр

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

, как вы указываете

Мы можем предположить, что объект круга имеет следующие свойства:

-Координаты

] -Радиус

-Скорость

-Направление

Скорость постоянна, но направление может меняться.

Затем, когда направление одного объекта изменится, пересчитайте те пары, которые затронуты. Этот метод эффективен, если направления не меняются слишком часто.

1
ответ дан 7 November 2019 в 09:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: