Як зробити стежки природного вигляду за допомогою A * на сітці?


13

Я читав це: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html

Але є деякі речі, які я не розумію, наприклад, у статті йдеться про те, щоб використовувати щось подібне для наведення маршруту з діагональним рухом:

function heuristic(node) =
    dx = abs(node.x - goal.x)
    dy = abs(node.y - goal.y)
    return D * max(dx, dy)

Я не знаю, як встановити D для отримання природного вигляду, як у статті, я встановив D найнижчу вартість між сусідніми квадратами, як це було сказано, і я не знаю, що вони мали на увазі під матеріалами про евристику. бути 4 * D, це, здається, нічого не змінить.

Це моя евристична функція та функція переміщення:

def heuristic(self, node, goal):
    D = 5
    dx = abs(node.x - goal.x)
    dy = abs(node.y - goal.y)
    return D * max(dx, dy)

def move_cost(self, current, node):
   cross = abs(current.x - node.x) == 1 and abs(current.y - node.y) == 1
   return 7 if cross else 5

Результат:

введіть тут опис зображення

Плавний шлях плавання, який ми хочемо пройти:

введіть тут опис зображення

Решта мого коду: http://pastebin.com/TL2cEkeX


Оновлення

Це найкраще рішення, яке я знайшов поки що:

def heuristic(node, start, goal):
    dx1 = node.x - goal.x
    dy1 = node.y - goal.y
    dx2 = start.x - goal.x
    dy2 = start.y - goal.y
    cross = abs(dx1*dy2 - dx2*dy1)

    dx3 = abs(dx1)
    dy3 = abs(dy1)

    return 5 + (cross*0.01) * (dx3+dy3) + (sqrt(2)-2) * min(dx3, dy3)

def move_cost(current, node):
    cross = abs(current.x - node.x) == 1 and abs(current.y - node.y) == 1
    return 7 if cross else 5

Він створює бажаний шлях з другого малюнка, але не справляється з перешкодами дуже добре (схильний повзати по стінах) і не може створити оптимальні шляхи іноді на більші відстані.

Які налаштування та оптимізації я можу застосувати, щоб поліпшити його?


2
Що робити, якщо ви використовуєте декартову відстань як свою евристичну?
Джиммі

2
ось лише ідея, збільшуйте витрати на перехід від однієї плитки до іншої за кожен крок руху агента в одному напрямку.
Ali1S232

@Jimmy Я спробував sqrt (pow (Goal.x - node.x, 2) + pow (Goal.y - node.y, 2)), і для мого невеликого прикладу шлях фактично повертає точно так само, як картинка в моєму запитанні .
Анонімний суб’єкт

Відповіді:


10

A * дає вам найкоротший шлях у графі. При використанні сітки в якості графіку часто є кілька найкоротших шляхів. У вашій першій схемі це один з найкоротших шляхів. Він ставить усі осьові рухи спочатку, а всі діагональні рухи - згодом. Але це така ж довжина шляху, як якщо б ви поставили спочатку всі діагоналі, або якщо ви змішали осьові та діагональні рухи. Усі ці еквівалентно короткі, і який вибір A * залежить від того, як записаний код та як представлений графік.

Я думаю, що ви хочете:

  1. Вам потрібно рухатись по сітці, але ви хочете змішати осьову та діагональну сходинки, щоб вона виглядала краще. Один із підходів - вибрати один з іншого однаково короткими шляхами; продовжуйте читати цю евристичну сторінку, щоб знайти "розрив зв'язків". Інший підхід - коли ви оцінюєте сусідів, вибирайте випадковим чином, який слід оцінити першим, щоб він не завжди вибирав одного перед іншим. Я не рекомендую використовувати евклідову / декартову відстань, якщо ви хочете рухатись по сітці; це невідповідність, що робить A * біг повільніше.
  2. Вам не потрібно рухатись по сітці, а хочете рухатися по прямій лінії. Один із підходів полягає у вирівнюванні контурів за допомогою «натягування струни». Ви шукаєте місця, де обертається шлях, і малюєте прямі лінії між цими точками. Інший підхід - застосувати це до самого основного графіка. Замість наведення маршруту на сітці, знайдіть контур на ключових точках на карті, а потім рухайтеся по прямих лініях між цими ключовими точками. Приклад ви можете побачити тут . Ще один підхід - алгоритм Theta * .

Гарна відповідь. Я оновив своє запитання якоюсь новою інформацією, сподіваюся, ви зможете трохи уточнити свою відповідь.
Анонімний суб’єкт

Я думаю, що трохи про перешкоди очікується; на сторінці евристики є діаграма під назвою «менш симпатична з перешкодами». Підходи до розриву краватки не допомагають багато навколо перешкод. Один з інших підходів (наприклад, Theta *) може бути тим, що ви хочете.
amitp

2

Алгоритм A * дозволяє призначити різні витрати на краї контуру. Ви також можете призначити витрати залежно від обставин. Це ваш головний інструмент для формування шляхів A *, щоб виглядати так, як ви хочете.

Коли ви хочете відмовити довгі діагоналі, ви можете їх штрафувати. Додайте невелику вартість за кожен раз, коли шлях іде в одному напрямку. Коли ви це зробите, алгоритм автоматично спробує розподілити діагональні кроки максимально рівномірно по всьому шляху. Просто переконайтеся, що ця додаткова вартість ніколи не перевищує витрати на додатковий край, інакше алгоритм почне робити абсолютно непотрібні об'їзди, щоб уникнути прямих ліній.

Гарною формулою може бути:

cost = normal_cost * (1.1 - 0.1 / num_of_steps_in_the_same_direction)

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


1

Адаптація A *

Як сказав Філіп, вам слід додати витрати, коли напрямок не змінюється протягом тривалого часу. Але функція Філіпа може швидко призвести до підсумовування додаткових витрат, які перевищують витрати на проїзд додаткової плитки. Але його ключова ідея правильна!

Здається легко адаптувати A * для обчислення "всіх" оптимальних шляхів (з найкоротшою довжиною), а потім вибору одного з них іншим евристичним. Але є проблема. Якщо у вас довгий шлях, можливо, буде багато рішень з оптимальною довжиною. Це призводить до того, що алгоритм A * потребує набагато більше часу для обчислення всіх цих інших рішень. Це тому, що сітка. Не можна ходити на 80 градусів замість 90 градусів, що призводить до кількох неоптимальних рішень замість одного оптимального рішення. Для уяви уявіть карту без перешкод. Відстань x дорівнює 2, відстань y - 3. Це означає, що всі найкоротші шляхи мають 2 діагональних ходи та 1 прямий хід. Для цього простого шляху є 3 допустимих комбінації: SDD, DSD, DDS (де D = діагональ, S = прямий). Справжня "забава" починається вже тоді, коли у вас є шляхи, наприклад 3 прямі та 2 діагональні ходи: SSSDD, SSDSD, SSDDS, SDSSD, SDSDS, SDDSS, DSSSD, DSSDS, DSDSS, DDSSS (10 варіацій найкоротшого шляху, якщо я не пропустив жодного). Я думаю, ви повинні мати ідею ...

Тому ми повинні це виправити, адаптувавши функцію витрат таким чином, щоб менше рішень (або навіть лише одне рішення) були "оптимальними".

Адаптація функції витрат

Виконання адаптації, як пропонує Філіп у своїй прикладі формули, дасть вам набагато кращі результати, але все ж є деякі проблеми. Він не буде рівномірно розподіляти коротші / довші "частини" по шляху, тобто: зміни напрямку будуть частіше на початку шляху або навпаки.

Крім того, здається, що шлях з нескінченною здатністю актора "повернути" виявляється неоптимальним, коли спостерігається людиною. Оскільки потрібен час (щоб показати анімацію повороту), і тому він повинен бути повільніше.

Як ніколи, замість використання поплавків для витрат ви можете реалізувати "вторинну вартість" або критерії вторинного сортування. Якщо первинні витрати однакові, то для визначення рішення, яке слід віддати перевагу, використовується вторинна вартість. Це не випадково призведе до збільшення первинних витрат (довжина маршруту в сітці).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.