एक वेक्टर से दूसरे वेक्टर के रोटेशन का प्रतिनिधित्व करने वाले चतुर्धातुक का पता लगाना


105

मेरे पास दो वैक्टर यू और वी हैं। क्या यू से वी के रोटेशन का प्रतिनिधित्व करने वाले क्वाटर्नियन को खोजने का एक तरीका है?

जवाबों:


115
Quaternion q;
vector a = crossproduct(v1, v2);
q.xyz = a;
q.w = sqrt((v1.Length ^ 2) * (v2.Length ^ 2)) + dotproduct(v1, v2);

क्यू को सामान्य करने के लिए मत भूलना।

रिचर्ड के बारे में सही है कि एक अनोखा घुमाव नहीं है, लेकिन ऊपर "सबसे छोटा चाप" देना चाहिए, जो कि शायद आपको चाहिए।


30
ज्ञात हो कि यह समानांतर वैक्टर (दोनों एक ही दिशा में या विपरीत दिशाओं में इंगित करते हुए) के मामले को नहीं संभालता है। crossproductइन मामलों में मान्य नहीं होगा, इसलिए आपको पहले जाँच करने की आवश्यकता है dot(v1, v2) > 0.999999और dot(v1, v2) < -0.999999क्रमशः, और या तो समानांतर वैक्टर के लिए एक पहचान क्वाट लौटाएं, या विपरीत वैक्टर के लिए 180 डिग्री रोटेशन (किसी भी अक्ष के बारे में) लौटाएं।
सिनिस्टरचिपमंक

11
इसका एक अच्छा कार्यान्वयन ओग्रे 3 डी स्रोत कोड
जोओ पोर्टेला

4
@sinisterchipmunk वास्तव में, अगर v1 = v2, क्रॉसप्रोडक्ट (0,0,0) होगा और w सकारात्मक होगा, जो पहचान को सामान्य करता है। Gamedev.net/topic/… के अनुसार, यह केवल v1 = -v2 के लिए और उनके निकटवर्ती क्षेत्र में भी ठीक काम करना चाहिए।
jpa

3
किसी को भी यह तकनीक काम करने के लिए कैसे मिली है? एक के लिए, sqrt((v1.Length ^ 2) * (v2.Length ^ 2))सरल करता है v1.Length * v2.Length। समझदार परिणाम देने के लिए मुझे इसका कोई रूपांतर नहीं मिला।
जोसेफ थॉमसन

2
हां, यह काम करता है। स्रोत कोड देखें । यदि वैक्टर विपरीत दिशाओं का सामना करता है तो L61 संभालता है (पीआई वापस करें, अन्यथा यह प्रति @ जेपा की टिप्पणी पर पहचान लौटाएगा)। L67 समानांतर वैक्टर को संभालता है: गणितीय रूप से अनावश्यक, लेकिन तेज। L72 पोलारिस878 का उत्तर है, यह मानते हुए कि दोनों वैक्टर यूनिट लंबाई हैं (एक sqrt से बचा जाता है)। इकाई परीक्षण भी देखें ।
sinisterchipmunk

63

आधा रास्ता वेक्टर समाधान

मैं इस समाधान के साथ आया था कि मुझे विश्वास है कि इम्ब्रोन्डर पेश करने की कोशिश कर रहा था (यद्यपि एक छोटी सी गलती के साथ, शायद यही कारण था कि सिनरिस्टरचिपमंक को इसे सत्यापित करने में परेशानी हुई)।

यह देखते हुए कि हम एक धुरी का निर्माण कर सकते हैं जैसे कि धुरी के चारों ओर घूर्णन का प्रतिनिधित्व करना:

q.w == cos(angle / 2)
q.x == sin(angle / 2) * axis.x
q.y == sin(angle / 2) * axis.y
q.z == sin(angle / 2) * axis.z

और दो सामान्यीकृत वैक्टर के डॉट और क्रॉस उत्पाद हैं:

dot     == cos(theta)
cross.x == sin(theta) * perpendicular.x
cross.y == sin(theta) * perpendicular.y
cross.z == sin(theta) * perpendicular.z

लंबवत वेक्टर के चारों ओर थीटा (वैक्टर के बीच का कोण) से घुमाकर यू से वी तक घुमाव के रूप में देखा जा सकता है, ऐसा लगता है कि हम सीधे डॉट और क्रॉस उत्पादों के परिणामों से इस तरह के रोटेशन का प्रतिनिधित्व करते हुए एक चतुर्भुज का निर्माण कर सकते हैं। ; हालाँकि, जैसा कि यह खड़ा है, थीटा = कोण / 2 , जिसका अर्थ है कि ऐसा करने से वांछित घुमाव दोगुना हो जाएगा।

एक समाधान के बीच एक वेक्टर अर्ध-मार्ग की गणना करना है यू और वी , और डॉट और के पार उत्पाद का उपयोग यू और आधे रास्ते एक चौका के रोटेशन का प्रतिनिधित्व करने के निर्माण के लिए वेक्टर दो बार के बीच के कोण यू और आधे रास्ते वेक्टर, जो हम सभी को वी के लिए ले जाता है !

एक विशेष मामला है, जहां u == -v और एक अद्वितीय अर्ध-मार्ग वेक्टर की गणना करना असंभव हो जाता है। यह अपेक्षित है, असीम रूप से कई "सबसे छोटी चाप" घुमावों को देखते हुए जो हमें यू से वी तक ले जा सकते हैं , और हमें किसी विशेष वेक्टर-ऑर्थोगोनल से यू (या वी ) के आसपास 180 डिग्री तक घूमना चाहिए । इस की सामान्यीकृत पार उत्पाद लेने के द्वारा किया जाता है यू किसी अन्य वेक्टर के साथ नहीं के समानांतर यू

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

यह भी ध्यान दें कि कोई विशेष मामला नहीं है जब u == v (पहचान चतुष्कोण उत्पन्न होता है - जाँच करें और अपने लिए देखें)।

// N.B. the arguments are _not_ axis and angle, but rather the
// raw scalar-vector components.
Quaternion(float w, Vector3 xyz);

Quaternion get_rotation_between(Vector3 u, Vector3 v)
{
  // It is important that the inputs are of equal length when
  // calculating the half-way vector.
  u = normalized(u);
  v = normalized(v);

  // Unfortunately, we have to check for when u == -v, as u + v
  // in this case will be (0, 0, 0), which cannot be normalized.
  if (u == -v)
  {
    // 180 degree rotation around any orthogonal vector
    return Quaternion(0, normalized(orthogonal(u)));
  }

  Vector3 half = normalized(u + v);
  return Quaternion(dot(u, half), cross(u, half));
}

orthogonalसमारोह को देखते हुए वेक्टर के लिए किसी भी वेक्टर ओर्थोगोनल देता है। यह कार्यान्वयन सबसे अधिक ऑर्थोगोनल आधार वेक्टर के साथ क्रॉस उत्पाद का उपयोग करता है।

Vector3 orthogonal(Vector3 v)
{
    float x = abs(v.x);
    float y = abs(v.y);
    float z = abs(v.z);

    Vector3 other = x < y ? (x < z ? X_AXIS : Z_AXIS) : (y < z ? Y_AXIS : Z_AXIS);
    return cross(v, other);
}

आधा रास्ता चतुर्भुज समाधान

यह वास्तव में स्वीकृत उत्तर में प्रस्तुत किया गया समाधान है, और यह अर्ध-सदिश सॉल्यूशन समाधान की तुलना में मामूली रूप से तेज लगता है (मेरे माप से 20% तेज, हालांकि इसके लिए मेरा शब्द नहीं लेते हैं)। मैं इसे यहाँ जोड़ रहा हूँ अगर मेरे जैसे अन्य लोग स्पष्टीकरण में रुचि रखते हैं।

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

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

q.w   == dot(u, v)
q.xyz == cross(u, v)

और शून्य रोटेशन के लिए चतुर्भुज है:

q.w   == 1
q.xyz == (0, 0, 0)

आधे रास्ते के चतुर्भुज की गणना करना केवल चतुर्भुजों को समेटने और वैक्टर के साथ परिणाम को सामान्य करने की बात है। हालांकि, जैसा कि वैक्टरों के मामले में भी है, चतुर्धातुक का परिमाण समान होना चाहिए, अन्यथा परिणाम बड़े परिमाण के साथ चतुर्भुज की ओर तिरछा हो जाएगा।

दो वैक्टरों के डॉट और क्रॉस उत्पाद से निर्मित चतुष्कोण का उन उत्पादों के समान परिमाण होगा length(u) * length(v):। इस कारक द्वारा सभी चार घटकों को विभाजित करने के बजाय, हम इसके बजाय पहचान चतुर्धातुकता को बढ़ा सकते हैं। और अगर आप सोच रहे थे कि स्वीकृत उत्तर का उपयोग करके मामलों को जटिल क्यों लगता है sqrt(length(u) ^ 2 * length(v) ^ 2), तो यह है क्योंकि एक वेक्टर की चुकता लंबाई लंबाई की तुलना में जल्दी होती है, इसलिए हम एक को बचा सकते हैंsqrt गणना । परिणाम है:

q.w   = dot(u, v) + sqrt(length_2(u) * length_2(v))
q.xyz = cross(u, v)

और फिर परिणाम को सामान्य करें। छद्म कोड निम्नानुसार है:

Quaternion get_rotation_between(Vector3 u, Vector3 v)
{
  float k_cos_theta = dot(u, v);
  float k = sqrt(length_2(u) * length_2(v));

  if (k_cos_theta / k == -1)
  {
    // 180 degree rotation around any orthogonal vector
    return Quaternion(0, normalized(orthogonal(u)));
  }

  return normalized(Quaternion(k_cos_theta + k, cross(u, v)));
}

12
+1: महान! इसने आकर्षण का काम किया। स्वीकृत उत्तर होना चाहिए।
22

1
कुछ उदाहरण (Quaternion (xyz, w) और Quaternion (w, xyz)) पर Quaternion सिंटैक्सिस को स्विच किया जाता है। यह भी लगता है कि अंतिम कोड ब्लॉक रेडियन और डिग्री को कोण (180 बनाम k_cos_theta + k) व्यक्त करने के लिए मिलाया जाता है।
गुइलेर्मो ब्लास्को

1
क्वाटरनियन (फ्लोट, वेक्टर 3) स्केलर-वेक्टर से निर्माण है, जबकि क्वाटरनियन (वेक्टर 3, फ्लोट) अक्ष-कोण से निर्माण है। शायद संभावित रूप से भ्रमित, लेकिन मुझे लगता है कि यह सही है। मुझे सही करें अगर आप अभी भी सोचते हैं कि यह गलत है!
जोसेफ थॉमसन

इसने काम कर दिया! धन्यवाद! हालांकि, मुझे ऊपर ऑपरेशन करने के लिए एक और समान और अच्छी तरह से समझाया गया लिंक मिला । सोचा कि मुझे रिकॉर्ड के लिए साझा करना चाहिए;)
पापी

1
@JosephThomson अर्ध-चतुर्भुज समाधान यहां से आता है
लीजेंड्स 2k

6

समस्या के रूप में अच्छी तरह से परिभाषित नहीं है: वैक्टर की एक जोड़ी के लिए एक अद्वितीय रोटेशन नहीं है। उदाहरण पर विचार करें, उदाहरण के लिए, जहां u = <1, 0, 0> और v = <0, 1, 0> । U से v तक एक घुमाव z- अक्ष के चारों ओर एक pi / 2 घूर्णन होगा। यू से वी तक एक और घुमाव वेक्टर <1, 1, 0> के चारों ओर एक पाई रोटेशन होगा


1
वास्तव में संभावित उत्तरों की अनंत संख्या नहीं है? क्योंकि आप "सदिश" से "सदिश" के साथ "सदिश" को संरेखित करने के बाद भी परिणाम के चारों ओर घूम सकते हैं? क्या आप जानते हैं कि आम तौर पर इस विकल्प को कसना और समस्या को अच्छी तरह से परिभाषित करने के लिए क्या अतिरिक्त जानकारी का उपयोग किया जा सकता है?
डग मैकक्लीन

5

क्यों शुद्ध quaternions का उपयोग करते हुए वेक्टर का प्रतिनिधित्व नहीं करते हैं? बेहतर होगा कि आप उन्हें पहले सामान्य कर लें।
q 1 = (0 u x u y u z ) '
q 2 = (0 v x v y v z )'
q 1 q rot = q 2
q 1 -1
q rot = q 1 -1 q 2 के साथ पहले से गुणा करें
जहाँ q 1 -1 = q 1 संयुग्मन / q मानदंड
इसे "वाम विभाजन" के रूप में सोचा जा सकता है। राइट डिवीजन, जो आप नहीं चाहते हैं वह है:
क्यू रोट, राइट = क्यू 2 -1 क्यू 1


2
मैं हार गया, q2 से q2 की गणना q_2 = q_rot q_1 q_rot ^ -1 के रूप में नहीं की गई है?
योता

4

मैं Quaternion पर बहुत अच्छा नहीं हूँ। हालाँकि मैंने इस पर घंटों संघर्ष किया, और पोलारिस878 समाधान कार्य नहीं कर सका। मैंने v1 और v2 को सामान्य करने की कोशिश की है। सामान्यीकृत क्यू। सामान्यीकरण q.xyz। फिर भी अभी भी मुझे नहीं मिला। परिणाम अभी भी मुझे सही परिणाम नहीं दिया।

अंत में हालांकि मुझे एक समाधान मिला जो उसने किया। अगर यह किसी और की मदद करता है, तो यहां मेरा काम (अजगर) कोड है:

def diffVectors(v1, v2):
    """ Get rotation Quaternion between 2 vectors """
    v1.normalize(), v2.normalize()
    v = v1+v2
    v.normalize()
    angle = v.dot(v2)
    axis = v.cross(v2)
    return Quaternion( angle, *axis )

एक विशेष मामला बनाया जाना चाहिए, अगर v1 और v2 paralell जैसे v1 == v2 या v1 == -v2 (कुछ सहनशीलता के साथ) हैं, जहां मेरा मानना ​​है कि समाधान क्वाटरनियन (1, 0,0,0) होना चाहिए (कोई रोटेशन नहीं) या क्वाटर्शन (0, * v1) (180 डिग्री रोटेशन)


मेरे पास एक कार्यान्‍वयन है, लेकिन यह आपका प्रीतिकर है, इसलिए मैं चाहता था कि यह काम करे। दुर्भाग्य से यह मेरे सभी परीक्षण मामलों में विफल रहा। मेरे परीक्षण सभी कुछ पसंद करते हैं quat = diffVectors(v1, v2); assert quat * v1 == v2
सिनिस्टरचिपमंक

यह संभावना नहीं है कि यह angleएक डॉट उत्पाद से अपना मूल्य प्राप्त करने के बाद से सभी पर काम करेगा ।
सैम होलेश्वर

क्वाटरनियन () फ़ंक्शन कहाँ है?
जून वांग

3

उत्तर में से कुछ संभावना पर विचार नहीं करते हैं कि क्रॉस उत्पाद 0. हो सकता है। नीचे स्निपेट कोण-अक्ष प्रतिनिधित्व का उपयोग करता है:

//v1, v2 are assumed to be normalized
Vector3 axis = v1.cross(v2);
if (axis == Vector3::Zero())
    axis = up();
else
    axis = axis.normalized();

return toQuaternion(axis, ang);

toQuaternionइस प्रकार लागू किया जा सकता:

static Quaternion toQuaternion(const Vector3& axis, float angle)
{
    auto s = std::sin(angle / 2);
    auto u = axis.normalized();
    return Quaternion(std::cos(angle / 2), u.x() * s, u.y() * s, u.z() * s);
}

यदि आप Eigen लाइब्रेरी का उपयोग कर रहे हैं, तो आप भी कर सकते हैं:

Quaternion::FromTwoVectors(from, to)

toQuaternion(axis, ang)-> आप यह ang
बताना

2 पैरामीटर, angleजो क्वाटरनियन के अक्ष-कोण प्रतिनिधित्व का हिस्सा है, जिसे रेडियंस में मापा जाता है।
शीतल शाह

आपको एक वेक्टर से दूसरे वेक्टर को घुमाने के लिए चतुष्कोण प्राप्त करने के लिए कहा गया था। आपके पास कोण नहीं है, आपको पहले इसकी गणना करनी होगी। आपके उत्तर में कोण की गणना होनी चाहिए। चीयर्स!
मक्सिम गानेंको

यह c ++ है? ux () क्या है?
जून वांग

हां, यह C ++ है। यू आइगन लाइब्रेरी से वेक्टर प्रकार है (यदि आप एक का उपयोग कर रहे हैं)।
शीतल शाह

2

एल्गोरिथ्म के दृष्टिकोण से, सबसे तेज़ समाधान स्यूडोकोड में दिखता है

 Quaternion shortest_arc(const vector3& v1, const vector3& v2 ) 
 {
     // input vectors NOT unit
     Quaternion q( cross(v1, v2), dot(v1, v2) );
     // reducing to half angle
     q.w += q.magnitude(); // 4 multiplication instead of 6 and more numerical stable

     // handling close to 180 degree case
     //... code skipped 

        return q.normalized(); // normalize if you need UNIT quaternion
 }

सुनिश्चित करें कि आपको इकाई चतुर्भुज की जरूरत है (सामान्य रूप से, यह प्रक्षेप के लिए आवश्यक है)।

नोट: यूनिट की तुलना में कुछ कार्यों के साथ नॉनिट क्वैटन का उपयोग किया जा सकता है।

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