मैं सीमित त्वरण वाली वस्तुओं के लिए पथों की गणना कैसे करूं?


9

उदाहरण के लिए, मान लें कि मेरे पास एक कार है और एक कार में एक विशिष्ट न्यूनतम मोड़ त्रिज्या है और मैं उस कार को बिंदु a से बिंदु b तक चलाना चाहता हूं, लेकिन कार बिंदु b का सामना नहीं कर रही है। मैं बिंदु b पर पथ की गणना कैसे करूं? बिंदु b पर अभिविन्यास निर्दिष्ट करने में सक्षम होने के नाते भी अच्छा होगा (कहते हैं कि आप अपने ड्राइववे के लिए ड्राइव करना चाहते हैं और फिर अपने गैरेज में खींच सकते हैं - यह बहुत अच्छा नहीं है यदि आप अपने लॉन पर ड्राइविंग करके अपने ड्राइववे पर पहुंच गए हैं और बग़ल में सामना कर रहे हैं :)

प्रलेखन के लिए एक सूचक (या यहां तक ​​कि सिर्फ एक नाम) पूरी तरह से ठीक होगा - मुझे कुछ भी खोजने में परेशानी हो रही है।

मेरे प्रयासों में, वे साधारण मामलों में काम करते हैं, लेकिन ऐसी स्थितियों में बुरी तरह से विफल हो जाते हैं जैसे कि बिंदु बी न्यूनतम मोड़ त्रिज्या की तुलना में करीब है।

उदाहरण के लिए, आप इस (बोल्ड पथ) के समान पथ का निर्धारण कैसे करेंगे:

चित्रमय उद्देश्यों के लिए बस एक घुमावदार रास्ता

संपादित करें: मेरी वास्तविक समस्या में, कुछ सरल पथ अवरोध हैं, लेकिन मेरे पास पहले से ही एक ए * एल्गोरिथ्म है जो काम करता है, लेकिन यह चीजों को तात्कालिक रूप से शीर्षक परिवर्तन करने की अनुमति देता है, इसलिए यह मूर्खतापूर्ण लगता है कि कार को अचानक 90 make मोड़ दें एक मोड़ पर जब वे एक मोड़ पर पहुंचते हैं।


gamedev.stackexchange.com/questions/86881/… लेकिन मुझे यकीन नहीं है कि मैं 3 डी स्पेस सेट करने के तरीके के बारे में उत्तर को समझ गया हूं
xaxxon

"आदर्श रूप से यह एल्गोरिथ्म बदलती गति से निपटने में सक्षम होगा" क्या यह न्यूनतम मोड़ त्रिज्या गति से संबंधित है जैसा कि यह बदलता है, या क्या यह किसी एक कार के लिए स्थिर है?
DMGregory

मैं उस हिस्से को हटा दूंगा। मैं जो कर रहा हूं, उसके लिए "ग्रैन टूरिस्मो" से ज्यादा "सिम सिटी" है। मैं समझता हूं कि आप ऐसा क्यों पूछ रहे हैं और मुझे यकीन नहीं है कि जब मैं इसे जोड़ रहा था तो मैं क्या सोच रहा था, जैसा कि मैं समझता हूं कि यह अप्रासंगिक है।
xaxxon

बेज़ियर वक्र आरेख ने मुझे इस दूसरे उत्तर की याद दिला दी , जो कि सीमित त्वरण के साथ पथ नियोजन से संबंधित है - उस मामले में त्वरण को एक मोड़ त्रिज्या के बजाय एक दिशात्मक रॉकेट थ्रस्टर की तरह बनाया गया था, लेकिन यह अभी भी कुछ उपयोगी विचारों को चिंगारी दे सकता है।
DMGregory

जवाबों:


7

मैंने अभी तक इसके लिए पूर्ण समीकरणों के माध्यम से काम नहीं किया है, लेकिन समस्या के चारों ओर हमारे सिर को लपेटने में मदद करने के लिए यहां कुछ दृश्य हैं। यह कुछ ज्यामिति को उबालता है:

हलकों के साथ एक कार जिसके मोड़ त्रिज्या का संकेत देते हैं। ( कार प्रतीक के माध्यम से केनी )

किसी भी शुरुआती बिंदु और अभिविन्यास से, हम अपने न्यूनतम मोड़ त्रिज्या के साथ दो वृत्त खींच सकते हैं - एक बाईं ओर, एक दाईं ओर। ये हमारे पथ पर सबसे कठिन संभव शुरुआत के बिंदुओं का वर्णन करते हैं।

हम किसी भी वांछित अंत स्थिति और अभिविन्यास के लिए ऐसा ही कर सकते हैं। ये मंडलियाँ हमारे पथ के सबसे कठिन अंत का वर्णन करती हैं।

अब समस्या एक रास्ता है कि अंत समुदायों में से एक शुरुआत समुदायों में से एक में शामिल है अपने स्पर्श के साथ हर एक चुंबन पाने के लिए कम कर देता है।

(यह मानकर है कि हमें बीच-बीच में बाधाओं को दूर करने की आवश्यकता नहीं है, जो कि प्रश्न में वर्णित नहीं है। स्टॉर्मविंड का जवाब मिलता है कि हम इन प्रकार की समस्याओं के लिए नेविगेशन ग्राफ की जानकारी का उपयोग कैसे कर सकते हैं। एक बार जब हमारे पास नोड्स का क्रम होता है। से गुजरने के लिए, हम योजना के प्रत्येक खंड के नीचे की विधि लागू कर सकते हैं।)

यदि, सादगी के लिए, हम सीधी रेखाओं का उपयोग करते हैं, तो हमें कुछ ऐसा मिलता है:

एक कार ले जा सकने वाले विभिन्न रास्तों को दिखाने वाला आरेख।

यह हमें सीमित मामला देता है। एक बार जब आप इस विधि से एक रास्ता मिल गया है, तो आप कृत्रिम रूप से एक या दोनों शुरुआत और अंत हलकों बढ़ बिंदु है जहां दो हलकों चुंबन तक एक कम प्रत्यक्ष लेकिन चिकनी पथ प्राप्त करने के लिए, कर सकते हैं।

इन रास्तों की गणना

चलो एक मोड़ दिशा के लिए मामलों को काम करते हैं - कहते हैं कि हम सही मोड़ से अपना रास्ता शुरू करते हैं।

हमारे दाईं ओर घूमने का केंद्र है:

startRightCenter = carStart.position + carStart.right * minRadius

चलो हमारे पथ के सीधे खंड के कोण को कहते हैं (धनात्मक x- अक्ष से मापा जाता है) pathAngle

यदि हम rightCenterउस बिंदु से एक सदिश खींचते हैं , जहाँ हम टर्निंग सर्कल छोड़ते हैं (जिस बिंदु पर हमें pathAngle का सामना करना पड़ता है), तो सदिश है ...

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

इसका मतलब है कि वह बिंदु जहां हम सर्कल छोड़ते हैं, होना चाहिए ...

departure = startRightCenter + startOffset

वह बिंदु जहां हम एक टर्निंग सर्कल में फिर से प्रवेश करते हैं, यह इस बात पर निर्भर करता है कि क्या हम बाएं या दाएं मोड़ के साथ समाप्त करना चाहते हैं:

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

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

अब, अगर हम हमारा काम सही किया है, लाइन में शामिल होने departureके लिए reentryकरने के लिए खड़ा होना चाहिए startOffset:

dot(reentry - departure,  startOffset) = 0

और इस समीकरण को हल करने से हमें वह कोण मिलेगा, जिस पर यह सत्य है। (मैं यहां एक बहुवचन का उपयोग करता हूं क्योंकि तकनीकी रूप से दो ऐसे कोण हैं, लेकिन उनमें से एक में रिवर्स में ड्राइविंग शामिल है जो आमतौर पर हम नहीं जानते हैं)

आइए एक उदाहरण के रूप में दाएं मोड़ को दाईं ओर मोड़ें।

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

क्रॉसओवर मामला अधिक जटिल है - यह वह है जो मैंने अभी तक सभी गणित के लिए काम नहीं किया है। जब तक मैं शेष विवरणों पर काम करता हूं, मैं आपके लिए बिना उत्तर के पोस्ट करूंगा, यह आपके लिए उपयोगी है।

संपादित करें: न्यूनतम मोड़ त्रिज्या के अंदर गंतव्य

यह पता चला है, यह विधि अक्सर आउट-ऑफ-द-बॉक्स तब भी काम करती है, जब गंतव्य हमारी न्यूनतम मोड़ की तुलना में करीब हो। री-एंट्री सर्किलों में से कम से कम कुछ हिस्सा टर्न रेडियस के बाहर समाप्त हो जाता है, जिससे हमें तब तक एक व्यवहार्य मार्ग मिल जाता है जब तक कि हमें यह बुरा न लगे कि यह थोड़ा प्रेट्ज़ेल जैसा है ...

किसी नजदीकी गंतव्य के लिए पथ-नियोजन के दौरान विकल्पों का प्रदर्शन।

यदि हमें वह रास्ता पसंद नहीं है जो हमें उस तरह से मिलता है (या यदि कोई संभव नहीं है - तो मैंने हर मामले को पूरी तरह से जाँच नहीं किया है - शायद असंभव हैं), हम हमेशा सीधे या पीछे ड्राइव कर सकते हैं जब तक कि हम एक उपयुक्त नहीं मिलते एक शुरुआत और अंत सर्कल के बीच संपर्क चुंबन, जैसा कि ऊपर diagrammed।


इसके बारे में सोचने का एक अच्छा सरल तरीका है और हलकों पर स्पर्शरेखा के साथ काम करना बहुत आसान है। मैंने केवल आपके उत्तर को अभी तक स्किम्ड किया है, लेकिन एक समस्या जो मैंने ली है, वह यह है कि यदि लक्ष्य शुरुआत बिंदु के मोड़ के अंदर है तो लक्ष्य है।
xaxxon

सबसे आसान नीति जो मुझे पता है कि इससे निपटना है जब तक कि लक्ष्य आपके किसी भी मोड़ पर नहीं होता है, तब तक इसे चालू करें। एक गंतव्य उन्मुखीकरण के साथ आप जब तक शुरुआत और अंत मोड़ हलकों कहीं चुंबन रिवर्स चाहते हैं। मैं उस मामले की कल्पना करने के लिए एक आरेख जोड़ूंगा।
DMGregory

2
एक महीने (और कई विचलित) बाद में मुझे यह काम मिला। मैं 4 स्पर्शरेखाओं की गणना करता हूं - "बाहरी" और "आंतरिक" (या "क्रॉसिंग") स्पर्शरेखा। तो start.left_circle को goal.left_circle, start.left_circle को "crossing" करने के लिए goal.right_circle (और फिर अन्य दो बस हलकों को स्विच करना)। यहाँ एक "बाहरी" पथ है: youtube.com/watch?v=99e5Wm8OKb0 और यहाँ एक "क्रॉसिंग" पथ है: youtube.com/watch?v=iEMt8mBheZU
xaxxon

1

यह बहुत नेविगेशन के लिए आपके डेटा मॉडल के बाकी हिस्सों पर निर्भर करता है। अर्थात। आपके पास कौन सा डेटा है, आप क्या आसानी से डेटा जोड़ सकते हैं और आप इसका उपभोग कैसे कर सकते हैं।

पानी पर एक यातायात प्रणाली से एक समान परिदृश्य लेना, और इस धारणा के साथ

  • आप एक गेम लूप में हैं
  • आपके पास एक नोड पथ प्रणाली है
  • आपकी कारें एक स्वायत्त वस्तुओं की तरह व्यवहार करती हैं जो खुद को "अंदर से" नियंत्रित करती हैं, स्वयं के बल और स्टीयरिंग का उपयोग करती हैं
  • आपकी कार रेल की तरह नहीं चलती है

आपके पास नीचे जैसा कुछ हो सकता है (चित्रों के बचकाने रूप के लिए मुझे क्षमा करें)

यहाँ छवि विवरण दर्ज करें

(लाल वर्ग नोड्स हैं, लाल लाइनें नोड इंटरकनेक्ट हैं। मान लें कि आपने एक पाथफाइंडिंग सॉल्वर का उपयोग किया है, जिससे ड्राइव करने के लिए नोड्स 1-9 दिए गए हैं; चित्र पर देखे गए नोड्स 4-9 हैं और आप ग्रीन लाइन द्वारा इंगित नोड्स से गुजरना चाहते हैं। , नोड # 9 पर गेराज के लिए; हालांकि आप ग्रीन लाइन पर ठीक से नहीं जाना चाहते हैं, इसके बजाय स्वाभाविक रूप से दाईं ओर की गली में रहें और चिकनी पैंतरेबाज़ी करें)।

प्रत्येक नोड में मेटाडेटा होता है जो विभिन्न उद्देश्यों के लिए एक त्रिज्या, या कई लोगों के लिए उदाहरण के लिए होता है। उनमें से एक ब्लू सर्कल है, जो कारों के लिए लक्ष्य मार्गदर्शन प्रदान करता है ।

पर किसी भी अवसर , वाहन आवश्यकताओं के अगले दो नोड अंक के बारे में पता होना (अगले) पी और पी (अगले +1), और उनके पदों। स्वाभाविक रूप से, कार की एक स्थिति भी है। पी (अगले) के नीले मेटाडेटा-सर्कल के दाईं ओर स्पर्शरेखा पर एक कार का लक्ष्य है । इसलिए कारें विपरीत दिशा में जा रही हैं, इसलिए वे टकराएंगी नहीं। स्पर्शरेखा पर निशाना लगाने का मतलब है कि कार किसी भी दिशा से सर्कल का रुख कर सकती है, और हमेशा सही रख सकती है। यह एक मोटा मूल सिद्धांत है, जिसे कई तरीकों से बेहतर बनाया जा सकता है।

दूरी तय करने के लिए P (अगले + 1) की आवश्यकता होती है - जैसे कार पी (अगले) तक पहुंचती है, या इसके मेटाडेटा के कुछ त्रिज्या के अंदर हो जाती है, यह कितनी दूर है इसके आधार पर इसे स्टीयरिंग कोण समायोजित कर सकता है। P (अगला + 1) के है। अर्थात। यदि यह करीब है, तो बहुत दूर है, अगर यह बहुत दूर है, तो थोड़ा मुड़ें। जाहिरा तौर पर अन्य नियम और किनारे की स्थिति के रूप में अच्छी तरह से करने की जरूरत है, उदाहरण के लिए कार और पी के दाईं ओर स्पर्शरेखा (अगले) और पी (अगले + 1) के आधार पर एक हेल्प लाइन, और उसके द्वारा एक सुधार की गणना। - धराशायी (तस्वीर के ऊपर) और बिंदीदार (नीचे तस्वीर) लाइन पर रहने की इच्छा।

किसी भी मामले में, जैसे ही कार एक नोड से गुजरती है , वह इसे भूल जाता है और अगले दो लोगों को देखना शुरू कर देता है

आपके प्रश्न के लिए जाहिरा तौर पर, जब नोड 7 तक पहुँच (ऊपर की तस्वीर में, नीचे की तस्वीर में नोड 2 के रूप में देखा गया), तो यह पर्याप्त नहीं हो सकता

यहाँ छवि विवरण दर्ज करें

एक संभव समाधान यह है कि कुछ हेल्प लाइनों का निर्माण किया जाए और हर समय एक लक्ष्य बनाए रखा जाए , और फिर कार को अपनी भौतिकी सेटिंग्स (एक निर्दिष्ट दर पर तेजी, रिवर्स धीमी गति, नोड मेटाडाटा गति को ध्यान में रखकर, किसी दिए गए या गणना में ब्रेक लें) जी, आदि)। जैसा कि कहा गया है, कार इस परिदृश्य में एक स्वायत्त, आत्म-वर्णन करने वाली, स्व-ले जाने वाली वस्तु है।

हरी हेल्प लाइन 1,2,3 है । जैसे ही कार मैजेंटा सर्कल में पहुंचती है , शुरू होती है यह दाईं ओर मुड़ती है। इस बिंदु पर, आप पहले से ही गणना कर सकते हैं कि यह सफल नहीं होगा (आप अधिकतम मोड़ दर जानते हैं और वक्र की गणना कर सकते हैं, और यह देख सकते हैं कि यह 2 और 3 दोनों सहायक को पार करेगा)। स्टीयरिंग को पूर्ण दाईं ओर मोड़ें और इसे आगे बढ़ाएं (भौतिकी वेतन वृद्धि द्वारा) और इसे धीमा करें क्योंकि यह हेल्प लाइन 3 तक पहुंच जाता है (थ्रेसहोल्ड का उपयोग करें, एफ (हेल्पलाइन के लिए दूर) आदि। जब यह है पर हेल्पलाइन 3, में जाने रिवर्स , मोड के लिए स्टीयरिंग बारी पूर्ण विपरीत । इसे तब तक उलटने दें जब तक यह हेल्प लाइन 4 तकपहुंच जाए(नोड 1 और 2 के बीच कनेक्शन लाइन - "लाइन एल्गोरिदम के बिंदु पर" के लिए Google)। धीमा, जैसे ही यह पहुंचता है, फिर से आगे मोड में ड्राइव करें, पहिया चालू करें। सड़क स्पष्ट होने तक दोहराएं - जाहिरा तौर पर यह इस समय 1 अतिरिक्त मैनोवर के साथ पर्याप्त था।

यह सामान्य विचार है: गेम लूप के दौरान, या गेम टास्क कतार प्रणाली की जाँच करते समय:

  • वर्तमान किनारे की सीमा और लक्ष्य के खिलाफ कार की स्थिति, गति, कोण आदि की जाँच करें ,
  • यदि अभी तक नहीं पहुंचा है , तो आप जो कर रहे थे उसे जारी रखें (भौतिक विज्ञान इसे स्थानांतरित करें; कार में एक आरपीएम और एक गियर है)। अपनी पंक्ति प्रणाली में एक नया चेक डालें, उदाहरण के लिए 0.1 s में।
  • यदि पहुंच गया है , तो नई शर्तों को शांत करें , डेटा सेट करें और शुरू करें । पंक्ति प्रणाली में उदाहरण के लिए 0.1 s में एक नया चेक डालें।
  • लूप चक्र पूरा करें - जारी रखें, दोहराएं।

नोड्स और कारों के प्रत्यय डेटा देने से, आंदोलन और निरंतरता होगी।

संपादित करें: और जोड़ना: यह स्वाभाविक रूप से ठीक ट्यूनिंग की जरूरत है। आपके सिमुलेशन व्यवहार को अलग-अलग हेल्प लाइन, मेटाडेटा, सर्कल, कुछ भी करने की आवश्यकता हो सकती है। यह हालांकि एक संभव समाधान का एक विचार देगा।


मुझे आपका उत्तर पढ़ने में थोड़ा समय लगेगा। मेरे पास पहले से ही काम कर रहे सामान्य पथप्रदर्शक हैं, लेकिन यह वस्तुओं को किसी भी बिंदु पर अनंत त्वरण बनाने की अनुमति देता है।
xaxxon

बेतरतीब ढंग से, मेरे पास वास्तव में कुछ बहुत करीब है जो आप वर्णन करते हैं। बैंगनी "चलती" रेखा पूरी तरह से दो सीधी रेखाओं से पूरी तरह से उत्पन्न होती है: youtube.com/watch?v=EyhBhrkmRiY लेकिन यह "तंग" स्थितियों में काम नहीं करती है और वास्तविक पाथफाइंडिंग के लिए वक्र का उपयोग नहीं किया जाता है।
xaxxon

0

मैंने DMGregory ने जो सुझाव दिया, उसे पूरा किया और यह अच्छी तरह से काम करता है। यहाँ कुछ प्रासंगिक कोड (हालांकि स्टैंडअलोन नहीं है) जिसका उपयोग मूर्तियों की दो शैलियों की गणना के लिए किया जा सकता है। मुझे यकीन है कि यह कोड कुशल नहीं है, और यह शायद सभी स्थितियों में सही भी नहीं है, लेकिन यह मेरे लिए अब तक काम कर रहा है:

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;
    }
};

यहां कोड की दो फिल्में ऊपर दी गई हैं:

यहाँ एक "बाहरी" पथ है: http://youtube.com/watch?v=99e5Wm8OKb0 और यहाँ एक "क्रॉसिंग" पथ है: http://youtube.com/watch?v=iEMt8mBheZU

यदि यह कोड मदद करता है, लेकिन आपके पास कुछ ऐसे भागों के बारे में प्रश्न हैं, जो यहां नहीं दिखाए गए हैं, तो बस एक टिप्पणी पोस्ट करें और मुझे इसे एक या दो दिन में देखना चाहिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.