Làm cách nào để tính đường dẫn cho các đối tượng có gia tốc hạn chế?


9

Ví dụ: giả sử tôi có một chiếc ô tô và một chiếc ô tô có bán kính quay vòng tối thiểu cụ thể và tôi muốn lái chiếc xe đó từ điểm a đến điểm b, nhưng chiếc xe không phải đối mặt với điểm b. Làm thế nào để tôi tính một đường dẫn đến điểm b? Có thể chỉ định hướng tại điểm b cũng sẽ tốt (giả sử bạn muốn lái xe đến đường lái xe của bạn và sau đó kéo vào nhà để xe của bạn - sẽ không tốt lắm nếu bạn đến đường lái xe bằng cách lái xe qua bãi cỏ của bạn và đang đi ngang :)

Một con trỏ đến tài liệu (hoặc thậm chí chỉ là một cái tên) sẽ hoàn toàn ổn - tôi gặp khó khăn trong việc tìm kiếm bất cứ điều gì.

Trong nỗ lực của tôi, chúng hoạt động trong các trường hợp đơn giản, nhưng thất bại thảm hại trong các tình huống như khi điểm b gần với bán kính quay vòng tối thiểu.

Ví dụ: làm thế nào bạn sẽ xác định một đường dẫn tương tự như thế này (đường dẫn đậm):

Chỉ là một con đường cong cho mục đích minh họa

chỉnh sửa: Trong vấn đề thực tế của tôi, có một số hạn chế đường dẫn đơn giản, nhưng tôi đã có thuật toán A * hoạt động, nhưng nó cho phép mọi thứ thay đổi tiêu đề tức thời, vì vậy có vẻ ngớ ngẩn khi thấy một chiếc xe đột nhiên quay đầu 90˚ trên một xu khi họ đến một điểm rẽ.


gamedev.stackexchange.com/questions/86881/, nhưng tôi không chắc mình hiểu câu trả lời về cách thiết lập không gian 3d
xaxxon

"lý tưởng là thuật toán này sẽ có thể đối phó với tốc độ thay đổi" Bán kính quay vòng tối thiểu có liên quan đến tốc độ khi nó thay đổi, hay nó không đổi đối với bất kỳ một chiếc xe nào?
DMGregory

Tôi sẽ xóa phần đó. Đối với những gì tôi đang làm, đó là "thành phố sim" hơn là "gran tourismo". Tôi hiểu lý do tại sao bạn hỏi điều đó và tôi không chắc mình đã nghĩ gì khi tôi thêm điều đó, vì tôi hiểu rằng điều đó không liên quan.
xaxxon

Biểu đồ đường cong Bezier gợi cho tôi một chút về câu trả lời khác này, cũng liên quan đến việc lập kế hoạch đường đi với gia tốc hạn chế - trong trường hợp đó, gia tốc được mô phỏng như một máy phóng tên lửa định hướng thay vì bán kính quay, nhưng nó vẫn có thể gây ra một số ý tưởng hữu ích.
DMGregory

Câu trả lời:


7

Tôi chưa làm việc thông qua các phương trình đầy đủ cho điều này, nhưng đây là một số hình ảnh để giúp giải quyết vấn đề. Nó sôi sùng sục xuống một số hình học:

Một chiếc xe với các vòng tròn cho thấy bán kính quay vòng của nó. ( Biểu tượng xe hơi qua Kenney )

Từ bất kỳ điểm bắt đầu và hướng nào, chúng ta có thể vẽ hai vòng tròn với bán kính quay tối thiểu - một ở bên trái, một ở bên phải. Chúng mô tả các điểm trên bắt đầu chặt chẽ nhất có thể đến con đường của chúng tôi.

Chúng tôi có thể làm tương tự cho bất kỳ vị trí và định hướng kết thúc mong muốn. Những vòng tròn này mô tả kết thúc chặt chẽ nhất có thể trên con đường của chúng tôi.

Bây giờ vấn đề giảm xuống để tìm một đường dẫn nối một trong các vòng tròn bắt đầu đến một trong các vòng kết thúc, hôn từng người dọc theo tiếp tuyến của nó.

. để vượt qua, chúng ta có thể áp dụng phương pháp dưới đây cho từng phân đoạn của kế hoạch.)

Nếu, để đơn giản, chúng tôi sử dụng các đường thẳng, chúng tôi nhận được một cái gì đó như thế này:

Sơ đồ hiển thị các con đường khác nhau mà một chiếc xe có thể đi.

Điều này cho chúng ta trường hợp hạn chế. Khi bạn đã tìm thấy một con đường bằng phương pháp này, bạn có thể thổi phồng một hoặc cả hai vòng tròn bắt đầu và kết thúc một cách giả tạo để có được một con đường ít trực tiếp hơn nhưng mượt mà hơn, cho đến khi hai vòng tròn hôn nhau.

Tính toán các đường dẫn này

Chúng ta hãy giải quyết các trường hợp cho một hướng rẽ - giả sử chúng ta bắt đầu con đường của mình bằng cách rẽ phải.

Trung tâm của vòng tròn bên phải của chúng tôi là:

startRightCenter = carStart.position + carStart.right * minRadius

Hãy gọi góc của phần thẳng của đường đi của chúng tôi (được đo từ trục x dương) pathAngle

Nếu chúng ta vẽ một vectơ từ rightCenterđến điểm mà chúng ta rời khỏi vòng tròn xoay (tại điểm đó chúng ta phải đối mặt với pathAngle), thì vectơ đó là ...

startOffset = minRadius * (-cos(pathAngle), sin(pathAngle))

Điều đó có nghĩa là điểm chúng ta rời khỏi vòng tròn phải là ...

departure = startRightCenter + startOffset

Điểm mà chúng tôi nhập lại vòng tròn phụ thuộc vào việc chúng tôi đang nhắm đến kết thúc bằng rẽ trái hay rẽ phải:

// To end with a right turn:
reentry = endRightCenter + startOffset

// To end with a left turn: (crossover)
reentry = endLeftCenter - startOffset

Bây giờ, nếu chúng ta làm đúng công việc của chúng tôi, đường nối departuređến reentrynó đã có vuông góc với startOffset:

dot(reentry - departure,  startOffset) = 0

Và giải phương trình này sẽ cho chúng ta các góc mà điều này đúng. (Tôi sử dụng số nhiều ở đây vì về mặt kỹ thuật có hai góc như vậy, nhưng một trong số chúng liên quan đến việc lái xe ngược lại thường không phải là điều chúng ta muốn)

Hãy thay thế rẽ phải sang trường hợp rẽ phải làm ví dụ:

dot(endRightCenter + startOffset - startRightCenter - startOffset, startOffset) = 0
dot(endRightCenter - startRightCenter, startOffset) = 0
pathAngle = atan2(endRightCenter - startRightCenter)

Trường hợp chéo là phức tạp hơn - đó là trường hợp tôi chưa làm hết toán học. Bây giờ tôi sẽ đăng câu trả lời, trong trường hợp nó hữu ích cho bạn trong khi tôi tìm ra các chi tiết còn lại.

Chỉnh sửa: Điểm đến trong bán kính quay tối thiểu

Hóa ra, phương pháp này thường hoạt động vượt trội ngay cả khi đích đến gần hơn khoảng cách quay tối thiểu của chúng tôi. Ít nhất một phần của một trong các vòng tròn nhập lại kết thúc bên ngoài bán kính rẽ, cho phép chúng tôi tìm thấy một con đường khả thi miễn là chúng tôi không bận tâm rằng nó có một chút giống như bánh quy cây ...

Thể hiện các lựa chọn khi lập kế hoạch đường dẫn đến đích gần.

Nếu chúng ta không thích con đường chúng ta đi theo cách đó (hoặc nếu không khả thi - tôi đã kiểm tra kỹ mọi trường hợp - có thể có những trường hợp không thể), chúng ta luôn có thể lái thẳng hoặc lùi cho đến khi chúng ta thấy phù hợp tiếp xúc hôn giữa một vòng tròn bắt đầu và kết thúc, như sơ đồ ở trên.


Đó là một cách đơn giản để suy nghĩ về nó và tiếp tuyến trên các vòng tròn khá dễ làm việc. Tôi chỉ đọc lướt câu trả lời của bạn cho đến nay, nhưng một vấn đề mà mọi cách tiếp cận tôi thực hiện là nếu mục tiêu nằm trong vòng xoay của điểm bắt đầu.
xaxxon

Chính sách đơn giản nhất mà tôi biết để đối phó với điều đó là đảo ngược cho đến khi mục tiêu nằm trên một trong những vòng tròn của bạn, sau đó biến thành nó. Với hướng đích bạn sẽ đảo ngược cho đến khi vòng tròn bắt đầu và kết thúc hôn ở đâu đó. Tôi sẽ thêm một sơ đồ để hình dung trường hợp đó.
DMGregory

2
Một tháng (và một vài phiền nhiễu) sau đó tôi đã làm việc này. Tôi tính 4 tiếp tuyến - tiếp tuyến "bên ngoài" và "bên trong" (hoặc "giao nhau"). Vì vậy, start.left_circle đến Goal.left_circle, start.left_circle "băng qua" tới Goal.right_circle (và sau đó hai người kia chỉ chuyển đổi các vòng tròn). Đây là đường dẫn "bên ngoài": youtube.com/watch?v=99e5Wm8OKb0 và đây là đường dẫn "cắt ngang": youtube.com/watch?v=iEMt8mBheZU
xaxxon

1

Điều này phụ thuộc rất nhiều vào phần còn lại của mô hình dữ liệu của bạn để điều hướng. I E. dữ liệu nào bạn có sẵn, những gì bạn có thể dễ dàng thêm dữ liệu và cách bạn tiêu thụ dữ liệu đó.

Lấy một kịch bản tương tự từ một hệ thống giao thông trên mặt nước, và với giả định rằng

  • bạn đang ở trong một vòng lặp trò chơi
  • bạn có một hệ thống đường dẫn nút
  • xe ô tô của bạn hoạt động như một vật thể tự trị điều khiển "từ bên trong", sử dụng lực và tay lái riêng
  • xe của bạn không di chuyển như trên đường ray

bạn có thể có một cái gì đó như dưới đây (tha thứ cho tôi vì sự xuất hiện trẻ con của những bức ảnh)

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

(Các ô vuông màu đỏ là các nút, các đường màu đỏ là các kết nối nút. Giả sử bạn đã sử dụng một bộ giải tìm đường dẫn cho các nút 1-9 để lái xe qua; các nút 4-9 được nhìn thấy trên hình và bạn muốn đi qua các nút được chỉ định bởi đường màu lục , đến nhà để xe ở nút số 9, tuy nhiên bạn không muốn đi chính xác ở vạch xanh, thay vào đó hãy ở tự nhiên ở làn bên phải và thực hiện các thao tác trơn tru).

Mỗi nút sẽ có siêu dữ liệu chứa một bán kính, hoặc nhiều cái, cho các mục đích khác nhau. Một trong số đó là vòng tròn màu xanh, cung cấp hướng dẫn nhắm cho những chiếc xe .

Bất cứ lúc nào , chiếc xe cần phải nhận thức được hai điểm nút tiếp theo P (tiếp theo) và P (tiếp theo + 1) và vị trí của chúng. Đương nhiên, chiếc xe có một vị trí là tốt. Một chiếc xe nhắm vào bên phải tiếp tuyến của vòng tròn siêu dữ liệu màu xanh của P (tiếp theo). Xe ô tô đi ngược chiều, do đó chúng sẽ không va chạm. Nhắm vào tiếp tuyến có nghĩa là chiếc xe có thể tiếp cận vòng tròn từ bất kỳ hướng nào, và luôn luôn giữ đúng. Đây là một nguyên tắc cơ bản thô, có thể được cải thiện theo nhiều cách.

P (next + 1) là cần thiết để xác định khoảng cách - khi xe đến P (tiếp theo) hoặc vào trong bán kính của siêu dữ liệu, nó có thể điều chỉnh góc lái tùy thuộc vào khoảng cách P (next + 1). I E. Nếu nó gần, hãy quay nhiều, nếu nó ở xa, hãy quay ít. Rõ ràng cũng cần phải có các quy tắc & điều kiện cạnh khác, ví dụ tính khoảng cách giữa xe và đường trợ giúp dựa trên các tiếp tuyến bên phải của P (tiếp theo) và P (tiếp theo + 1), và điều chỉnh theo đó - một ý chí để ở trên đường đứt nét (trên pic) và đường chấm chấm (dưới pic).

Trong mọi trường hợp, khi chiếc xe vượt qua một nút , nó sẽ quên nó và bắt đầu nhìn vào hai nút tiếp theo .

Cho câu hỏi của bạn. Rõ ràng, khi đến nút 7 (trong ảnh trên, được xem là nút 2 trong hình bên dưới), nó không thể quay đủ .

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

Một giải pháp khả thi là xây dựng một số đường dây trợ giúpduy trì mục tiêu mọi lúc , sau đó cho xe di chuyển bằng các cài đặt vật lý của riêng nó (tăng tốc ở tốc độ xác định, đảo ngược chậm hơn, tính đến tốc độ siêu dữ liệu nút, phanh theo tốc độ hoặc tính toán G, v.v.). Như đã nói, chiếc xe là một đối tượng tự trị, tự mô tả, tự mang trong kịch bản này.

Có các dòng trợ giúp màu xanh 1,2,3 . Khi chiếc xe đến vòng tròn màu đỏ tươi , nó bắt đầu rẽ sang phải. Tại thời điểm này, bạn đã có thể tính toán rằng nó sẽ không thành công (bạn biết tốc độ quay tối đa và có thể tính toán đường cong và có thể thấy rằng nó sẽ vượt qua cả hai đường dây trợ giúp 2 và 3). Xoay tay lái hoàn toàn bên phải và để nó lái về phía trước (theo mức tăng vật lý) và giảm tốc độ khi nó chạm tới đường trợ giúp 3 (tiến gần đến ngưỡng sử dụng, f (từ xa tới đường dây trợ giúp), v.v.). Khi nó đường trợ giúp 3, chuyển sang chế độ đảo ngược , chuyển tay lái sang chế độ đối diện hoàn toàn . Hãy để nó đảo ngược cho đến khi nó đạt đến dòng 4(đường kết nối giữa nút 1 và 2 - google cho "điểm ở bên cạnh thuật toán đường"). Hãy chậm lại, khi nó chạm tới nó, đi vào chế độ lái trước một lần nữa, xoay bánh xe. Lặp lại cho đến khi đường rõ ràng - rõ ràng là đủ với 1 man thêm lần này.

Đây là ý tưởng chung: Trong vòng lặp trò chơi hoặc khi kiểm tra hệ thống xếp hàng nhiệm vụ trò chơi:

  • Kiểm tra vị trí xe, tốc độ, góc, vv so với giới hạn và mục tiêu cạnh hiện tại ,
  • Nếu chưa đạt được , hãy tiếp tục với những gì bạn đang làm (hãy để vật lý di chuyển nó; chiếc xe có vòng tua và bánh răng). Chèn một kiểm tra mới trong hệ thống hàng đợi của bạn, để xảy ra trong ví dụ 0,1 s.
  • Nếu đạt được , tính toán các điều kiện mới, đặt dữ liệu và bắt đầu . Chèn một kiểm tra mới xảy ra trong hệ thống hàng đợi trong ví dụ 0,1 s.
  • Hoàn thành chu trình vòng lặp - tiếp tục, lặp lại.

Bằng cách cung cấp cho các nút và những chiếc xe đủ dữ liệu, sẽ có sự di chuyển và tiếp tục.

Chỉnh sửa: Và thêm: Điều này tự nhiên cần tinh chỉnh. Hành vi mô phỏng của bạn có thể yêu cầu các dòng trợ giúp, siêu dữ liệu, vòng tròn, bất cứ thứ gì khác. Điều này sẽ cho một ý tưởng về một giải pháp có thể mặc dù.


Tôi sẽ mất một chút để đọc câu trả lời của bạn. Tôi có các đường dẫn chung chung đã được thiết lập và hoạt động, nhưng nó cho phép các đối tượng thực hiện gia tốc vô hạn tại bất kỳ điểm nào.
xaxxon

Ngẫu nhiên, tôi thực sự có một cái gì đó khá gần với những gì bạn mô tả. Đường "di chuyển" màu tím được tạo hoàn toàn theo thủ tục từ hai đường thẳng: youtube.com/watch?v=EyhBhrkmRiY nhưng nó không hoạt động trong các tình huống "chặt chẽ" và đường cong không được sử dụng cho tìm đường thực tế.
xaxxon

0

Tôi đã kết thúc việc làm những gì DMGregory đề xuất và nó hoạt động tốt. Đây là một số mã có liên quan (mặc dù không độc lập) có thể được sử dụng để tính toán hai kiểu tiếp tuyến. Tôi chắc chắn rằng mã này không hiệu quả và có thể nó thậm chí không chính xác trong mọi tình huống, nhưng cho đến nay nó vẫn hoạt động với tôi:

bool Circle::outer_tangent_to(const Circle & c2, LineSegment & shared_tangent) const {
    if (this->direction != c2.direction) {
        return false;
    }
    if (this->radius != c2.radius) {
        // how to add it: http://mathworld.wolfram.com/Circle-CircleTangents.html
        // just subtract smaller circle radius from larger circle radius and find path to center
        //  then add back in the rest of the larger circle radius
        throw ApbException("circles with different length radius not supported");
    }

    auto vector_to_c2 = c2.center - this->center;
    glm::vec2 normal_to_c2;
    if (this->direction == Circle::CW) {
        normal_to_c2 = glm::normalize(glm::vec2(-vector_to_c2.y, vector_to_c2.x));
    } else {
        normal_to_c2 = glm::normalize(glm::vec2(vector_to_c2.y, -vector_to_c2.x));
    }

    shared_tangent = LineSegment(this->center + (normal_to_c2 * this->radius),
                                 c2.center + (normal_to_c2 * this->radius));
    return true;
}


bool Circle::inner_tangent_to(const Circle & c2, LineSegment & tangent) const {

    if (this->radius != c2.radius) {
        // http://mathworld.wolfram.com/Circle-CircleTangents.html
        // adding this is non-trivial
        throw ApbException("inner_tangents doesn't support circles with different radiuses");
    }

    if (this->direction == c2.direction) {
        // inner tangents require opposing direction circles
        return false;
    }

    auto vector_to_c2 = c2.center - this->center;
    auto distance_between_circles = glm::length(vector_to_c2);

    if ( distance_between_circles < 2 * this->radius) {
//      throw ApbException("Circles are too close and don't have inner tangents");
        return false;
    } else {
        auto normalized_to_c2 = glm::normalize(vector_to_c2);
        auto distance_to_midpoint = glm::length(vector_to_c2) / 2;
        auto midpoint = this->center + (vector_to_c2 / 2.0f);

        // if hypotenuse is oo then cos_angle = 0 and angle = 90˚
        // if hypotenuse is radius then cos_angle = r/r = 1 and angle = 0
        auto cos_angle = radius / distance_to_midpoint;
        auto angle = acosf(cos_angle);

        // now find the angle between the circles
        auto midpoint_angle = glm::orientedAngle(glm::vec2(1, 0), normalized_to_c2);

        glm::vec2 p1;
        if (this->direction == Circle::CW) {
            p1 = this->center + (glm::vec2{cos(midpoint_angle + angle), sin(midpoint_angle + angle)} * this->radius);
        } else {
            p1 = this->center + (glm::vec2{cos(midpoint_angle - angle), sin(midpoint_angle - angle)} * this->radius);
        }

        auto tangent_to_midpoint = midpoint - p1;
        auto p2 = p1 + (2.0f * tangent_to_midpoint);
        tangent = {p1, p2};

        return true;
    }
};

Đây là hai bộ phim về đoạn mã trên đang hoạt động:

Đây là đường dẫn "bên ngoài": http://youtube.com/watch?v=99e5Wm8OKb0 và đây là đường dẫn "băng qua": http://youtube.com/watch?v=iEMt8mBheZU

Nếu mã này có ích nhưng bạn có câu hỏi về một số phần không được hiển thị ở đây, chỉ cần đăng nhận xét và tôi sẽ thấy nó trong một hoặc hai ngày.

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.