Làm cách nào để tạo các đường dẫn trông tự nhiên với A * trên lưới?


13

Tôi đã đọc bài này: http://theory.stanford.edu/~amitp/GameProgramming/Heuristic.html

Nhưng có một số điều tôi không hiểu, ví dụ như bài báo nói rằng hãy sử dụng một cái gì đó như thế này để tìm đường với chuyển động chéo:

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

Tôi không biết làm thế nào để thiết lập D để có được một con đường trông tự nhiên như trong bài viết, tôi đặt D ở mức chi phí thấp nhất giữa các hình vuông liền kề như nó đã nói, và tôi không biết ý nghĩa của những gì về heuristic nên là 4 * D, điều đó dường như không thay đổi bất kỳ điều gì.

Đây là chức năng heuristic và chức năng di chuyển của tôi:

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

Kết quả:

nhập mô tả hình ảnh ở đây

Con đường thuận buồm xuôi gió chúng tôi muốn xảy ra:

nhập mô tả hình ảnh ở đây

Phần còn lại của mã của tôi: http://pastebin.com/TL2cEkeX


Cập nhật

Đây là giải pháp tốt nhất tôi tìm thấy cho đến nay:

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

Nó tạo ra đường dẫn mong muốn từ pic thứ hai, nhưng không xử lý chướng ngại vật rất tốt (có xu hướng bò trên tường) và đôi khi không tạo ra đường dẫn tối ưu trên khoảng cách xa hơn.

Một số điều chỉnh và tối ưu hóa tôi có thể áp dụng để cải thiện nó là gì?


2
Điều gì nếu bạn sử dụng khoảng cách cartesian như heuristic của bạn?
Jimmy

2
Đây chỉ là một ý tưởng, tăng chi phí di chuyển từ gạch này sang gạch khác cho mỗi bước đại lý di chuyển theo cùng một hướng.
Ali1S232

@Jimmy Tôi đã thử sqrt (pow (Goal.x - node.x, 2) + pow (Goal.y - node.y, 2)) và đối với đường dẫn ví dụ nhỏ của tôi, nó thực sự trả về chính xác như hình ảnh trong câu hỏi của tôi .
Thực thể ẩn danh

Câu trả lời:


10

A * cung cấp cho bạn đường dẫn ngắn nhất trong biểu đồ. Khi sử dụng lưới làm biểu đồ của bạn thường có nhiều đường dẫn ngắn nhất. Trong sơ đồ đầu tiên của bạn, đó một trong những con đường ngắn nhất. Nó đặt tất cả các chuyển động dọc trục trước và tất cả các chuyển động chéo sau đó. Nhưng đó là cùng một đường dẫn dài như thể bạn đặt tất cả các đường chéo trước hoặc nếu bạn trộn lẫn các chuyển động dọc trục và đường chéo. Tất cả đều ngắn tương đương, và cái nào A * chọn phụ thuộc vào cách viết mã và cách biểu đồ được biểu diễn.

Tôi nghĩ những gì bạn muốn là:

  1. Bạn cần di chuyển trên lưới, nhưng bạn muốn trộn các bước trục và đường chéo để nó trông đẹp hơn. Một cách tiếp cận là chọn một trong những con đường ngắn không kém khác; hãy tiếp tục đọc trang Heuristic đó để tìm kiếm tie tie phá vỡ. Một cách tiếp cận khác là khi bạn đánh giá hàng xóm, hãy chọn ngẫu nhiên cái nào để đánh giá trước để nó không luôn chọn cái này trước cái kia. Tôi không khuyên bạn nên sử dụng khoảng cách Euclide / Cartesian nếu bạn muốn di chuyển trên lưới; đó là một sự không phù hợp làm cho A * chạy chậm hơn.
  2. Bạn không cần phải di chuyển trên lưới và muốn di chuyển theo đường thẳng. Một cách tiếp cận là làm thẳng các đường dẫn bằng cách sử dụng chuỗi kéo kéo. Bạn đang tìm kiếm những nơi đường dẫn rẽ và vẽ các đường thẳng giữa các điểm đó. Một cách tiếp cận khác là áp dụng điều này cho chính đồ thị bên dưới. Thay vì tìm đường trên lưới, tìm đường dẫn trên các điểm chính trên bản đồ, rồi di chuyển dọc theo các đường thẳng giữa các điểm chính đó. Bạn có thể xem một ví dụ ở đây . Một cách tiếp cận khác là thuật toán Theta * .

Câu trả lời tốt. Tôi đã cập nhật câu hỏi của tôi với một số thông tin mới, tôi hy vọng bạn có thể chỉ định câu trả lời của mình một chút.
Thực thể ẩn danh

Tôi nghĩ rằng một chút về những trở ngại được mong đợi; có một sơ đồ trên trang Heuristic có tiêu đề là kém đẹp với chướng ngại vật. Cách tiếp cận phá vỡ cà vạt không giúp ích nhiều cho những trở ngại. Một trong những cách tiếp cận khác (như Theta *) có thể là những gì bạn muốn.
amitp

2

Thuật toán A * cho phép bạn gán các chi phí khác nhau cho các cạnh đường dẫn. Bạn cũng có thể chỉ định chi phí tùy thuộc vào hoàn cảnh. Đây là công cụ chính của bạn để định hình các đường dẫn A * thành nhìn theo cách bạn muốn chúng nhìn.

Khi bạn muốn ngăn cản các đường chéo dài, bạn có thể phạt chúng. Thêm một chút chi phí cho mỗi lần con đường đi vào cùng một hướng. Khi bạn làm điều này, thuật toán sẽ tự động cố gắng phân phối các bước chéo càng đều càng tốt trên toàn bộ đường dẫn. Chỉ cần đảm bảo rằng chi phí bổ sung này không bao giờ là chi phí lấy thêm một cạnh, hoặc thuật toán sẽ bắt đầu thực hiện các đường vòng hoàn toàn không cần thiết chỉ để tránh các đường thẳng.

Một công thức tốt có thể là:

cost = normal_cost * (1.1 - 0.1 / num_of_steps_in_the_same_direction)

Lưu ý rằng điều này đòi hỏi chi phí đường dẫn được theo dõi dưới dạng giá trị dấu phẩy động, không phải là số nguyên.


1

Thích ứng A *

Như Philipp đã nói, bạn nên thêm chi phí khi hướng không thay đổi trong thời gian dài. Nhưng, chức năng của Philipp có thể nhanh chóng dẫn đến tổng hợp các chi phí bổ sung, cao hơn chi phí cho việc vượt qua một ô bổ sung. Nhưng ý chính của anh là chính xác!

Có vẻ dễ dàng điều chỉnh A * để tính toán "tất cả" các đường dẫn tối ưu (với độ dài ngắn nhất) và sau đó chọn một trong số chúng theo một heuristic khác. Nhưng có một vấn đề. Nếu bạn có một con đường dài, có thể có rất nhiều giải pháp với độ dài tối ưu. Điều này khiến thuật toán A * mất nhiều thời gian hơn để tính toán tất cả các giải pháp khác này. Điều này là do lưới điện. Bạn không thể đi bộ 80 độ thay vì 90 độ, điều này dẫn đến nhiều giải pháp tối ưu thay vì một giải pháp tối ưu. Đối với trí tưởng tượng, hãy tưởng tượng một bản đồ mà không có trở ngại. Khoảng cách x là 2 khoảng cách y là 3. Điều này có nghĩa, tất cả các đường đi ngắn nhất có 2 di chuyển chéo và 1 di chuyển thẳng. Có 3 kết hợp hợp lệ: SDD, DSD, DDS (trong đó D = đường chéo, S = thẳng) cho đường dẫn đơn giản này. "Vui vẻ" thực sự bắt đầu khi bạn có đường dẫn với, vd 3 di chuyển thẳng và 2 đường chéo: SSSDD, SSDSD, SSDDS, SDSSD, SDSDS, SDDSS, DSSSD, DSSDS, DSDSS, DDSSS (10 biến thể của con đường ngắn nhất, nếu tôi không bỏ lỡ bất kỳ). Tôi nghĩ bạn nên có ý tưởng ...

Vì vậy, chúng ta nên khắc phục điều này bằng cách điều chỉnh hàm chi phí theo cách ít giải pháp hơn (hoặc thậm chí chỉ có một giải pháp) là "tối ưu".

Điều chỉnh hàm chi phí

Thực hiện việc thích ứng như Philipp gợi ý trong công thức ví dụ của anh ấy sẽ cho bạn kết quả tốt hơn nhiều, nhưng vẫn còn một số vấn đề. Nó sẽ không phân phối đồng đều các "phần" ngắn hơn / dài hơn dọc theo đường dẫn, nghĩa là: sự thay đổi hướng sẽ thường xuyên hơn ở đầu đường dẫn hoặc ngược lại.

Ngoài ra, một con đường vô tận khiến diễn viên phải "rẽ" dường như không tối ưu khi được con người quan sát. Vì nó cần có thời gian (để hiển thị hình động lần lượt) và do đó nó phải chậm hơn.

Tuy nhiên, thay vì sử dụng phao cho các chi phí, bạn có thể thực hiện "chi phí thứ cấp" hoặc tiêu chí sắp xếp thứ cấp. Nếu các chi phí chính là như nhau, chi phí thứ cấp được sử dụng để ước tính giải pháp nào sẽ được ưu tiên. Điều này sẽ không vô tình làm cho chi phí chính (chiều dài tuyến tính trong đo lưới) tăng lên.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.