मेरे पास दो वैक्टर यू और वी हैं। क्या यू से वी के रोटेशन का प्रतिनिधित्व करने वाले क्वाटर्नियन को खोजने का एक तरीका है?
मेरे पास दो वैक्टर यू और वी हैं। क्या यू से वी के रोटेशन का प्रतिनिधित्व करने वाले क्वाटर्नियन को खोजने का एक तरीका है?
जवाबों:
Quaternion q;
vector a = crossproduct(v1, v2);
q.xyz = a;
q.w = sqrt((v1.Length ^ 2) * (v2.Length ^ 2)) + dotproduct(v1, v2);
क्यू को सामान्य करने के लिए मत भूलना।
रिचर्ड के बारे में सही है कि एक अनोखा घुमाव नहीं है, लेकिन ऊपर "सबसे छोटा चाप" देना चाहिए, जो कि शायद आपको चाहिए।
sqrt((v1.Length ^ 2) * (v2.Length ^ 2))
सरल करता है v1.Length * v2.Length
। समझदार परिणाम देने के लिए मुझे इसका कोई रूपांतर नहीं मिला।
मैं इस समाधान के साथ आया था कि मुझे विश्वास है कि इम्ब्रोन्डर पेश करने की कोशिश कर रहा था (यद्यपि एक छोटी सी गलती के साथ, शायद यही कारण था कि सिनरिस्टरचिपमंक को इसे सत्यापित करने में परेशानी हुई)।
यह देखते हुए कि हम एक धुरी का निर्माण कर सकते हैं जैसे कि धुरी के चारों ओर घूर्णन का प्रतिनिधित्व करना:
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)));
}
समस्या के रूप में अच्छी तरह से परिभाषित नहीं है: वैक्टर की एक जोड़ी के लिए एक अद्वितीय रोटेशन नहीं है। उदाहरण पर विचार करें, उदाहरण के लिए, जहां u = <1, 0, 0> और v = <0, 1, 0> । U से v तक एक घुमाव z- अक्ष के चारों ओर एक pi / 2 घूर्णन होगा। यू से वी तक एक और घुमाव वेक्टर <1, 1, 0> के चारों ओर एक पाई रोटेशन होगा ।
क्यों शुद्ध 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
मैं 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
एक डॉट उत्पाद से अपना मूल्य प्राप्त करने के बाद से सभी पर काम करेगा ।
उत्तर में से कुछ संभावना पर विचार नहीं करते हैं कि क्रॉस उत्पाद 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
angle
जो क्वाटरनियन के अक्ष-कोण प्रतिनिधित्व का हिस्सा है, जिसे रेडियंस में मापा जाता है।
एल्गोरिथ्म के दृष्टिकोण से, सबसे तेज़ समाधान स्यूडोकोड में दिखता है
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
}
सुनिश्चित करें कि आपको इकाई चतुर्भुज की जरूरत है (सामान्य रूप से, यह प्रक्षेप के लिए आवश्यक है)।
नोट: यूनिट की तुलना में कुछ कार्यों के साथ नॉनिट क्वैटन का उपयोग किया जा सकता है।
crossproduct
इन मामलों में मान्य नहीं होगा, इसलिए आपको पहले जाँच करने की आवश्यकता हैdot(v1, v2) > 0.999999
औरdot(v1, v2) < -0.999999
क्रमशः, और या तो समानांतर वैक्टर के लिए एक पहचान क्वाट लौटाएं, या विपरीत वैक्टर के लिए 180 डिग्री रोटेशन (किसी भी अक्ष के बारे में) लौटाएं।