Math 同一平面内具有相同原点的两个三维向量之间的有符号角度

Math 同一平面内具有相同原点的两个三维向量之间的有符号角度,math,vector,3d,geometry,Math,Vector,3d,Geometry,我需要的是位于同一3D平面内且具有相同原点的两个向量Va和Vb之间的有符号旋转角,知道: 包含两个向量的平面是任意平面,不平行于XY或任何其他基数平面 是一个平面法线吗 两个向量和法线具有相同的原点O={0,0,0} Va-是测量Vn处左手旋转的参考 角度的测量方式应确保,如果平面为XY平面,则Va代表其X轴单位矢量 我想我应该执行一种坐标空间转换,使用Va作为X轴,Vb和Vn的叉积作为Y轴,然后使用一些2d方法,比如atan2()之类的。有什么想法吗?公式?将一个向量交叉到另一个向量中,并进行

我需要的是位于同一3D平面内且具有相同原点的两个向量Va和Vb之间的有符号旋转角,知道:

  • 包含两个向量的平面是任意平面,不平行于XY或任何其他基数平面
  • 是一个平面法线吗
  • 两个向量和法线具有相同的原点O={0,0,0}
  • Va-是测量Vn处左手旋转的参考
  • 角度的测量方式应确保,如果平面为XY平面,则Va代表其X轴单位矢量


    我想我应该执行一种坐标空间转换,使用Va作为X轴,Vb和Vn的叉积作为Y轴,然后使用一些2d方法,比如atan2()之类的。有什么想法吗?公式?

    将一个向量交叉到另一个向量中,并进行规格化以得到单位向量

    两个矢量之间角度的正弦等于叉积的大小除以两个矢量的大小:


    您可以使用符号获取要签名的角度。要获得角度的符号,请取
    Vn*(Va x Vb)
    的符号。在XY平面的特殊情况下,这将减少到仅
    Va_x*Vb_y-Va_y*Vb_x

    让θ为向量之间的角度。设C=Va为叉积Vb。然后

    sinθ=长度(C)/(长度(Va)* 长度(Vb))

    要确定θ是正的还是负的,请记住C垂直于Va和Vb,指向由θ确定的方向。特别是,C与Vn是平行的。在你的例子中,如果C指向与Vn相同的方向,那么θ是负的,因为你想要左手旋转。快速检查Vn和C点是否在同一方向的最简单计算方法可能是只取它们的点积;如果是正数,则它们指向同一方向

    angle = acos(dotProduct(Va.normalize(), Vb.normalize()));
    cross = crossProduct(Va, Vb);
    if (dotProduct(Vn, cross) < 0) { // Or > 0
      angle = -angle;
    }
    

    所有这些都来自于向量的基本性质。

    使用两个向量的叉积来获得由两个向量构成的平面的法线。然后检查该点积与原始平面法线之间的点积,以查看它们是否朝向同一方向

    angle = acos(dotProduct(Va.normalize(), Vb.normalize()));
    cross = crossProduct(Va, Vb);
    if (dotProduct(Vn, cross) < 0) { // Or > 0
      angle = -angle;
    }
    
    angle=acos(点积(Va.normalize(),Vb.normalize());
    交叉=交叉积(Va,Vb);
    if(点积(Vn,cross)<0{//或>0
    角度=-角度;
    }
    
    您可以通过两个步骤完成此操作:

  • 确定两个矢量之间的角度

    θ=acos(Va,Vb的点积)。假设Va、Vb是标准化的。这将给出两个矢量之间的最小角度

  • 确定角度的符号

    求向量V3=Va,Vb的叉积。(订单很重要)

    如果(V3,Vn的点积)为负,则θ为负。否则,θ为正


  • 假设Vx是x轴,给定法线Vn,你可以通过叉积得到y轴,你可以将向量Vb投影到Vx和Vy上(通过点积你可以得到Vb到Vx和Vy的投影长度),给定平面上的(x,y)坐标,你可以使用atan2(y,x)得到范围[-pi,+pi]内的角度

    高级客户提供了以下解决方案(最初是对问题的编辑):

    解决方案:
    新浪=| Va x Vb |/(| Va |*| Vb |)
    cosa=(Va.Vb)/(|Va |*| Vb |)
    角度=atan2(新浪、cosa)
    符号=Vn。(Va x Vb)
    
    如果(sign我当前使用的解决方案似乎在这里丢失了。 假设平面法线已归一化(
    |Vn |==1
    ),则符号角度仅为:

    对于从Va到Vb的右手旋转:

    atan2((Va x Vb.Vn,Va.Vb)

    对于从Va到Vb的左手旋转:

    atan2((vbx Va.Vn,Va.Vb)

    返回范围[-PI,+PI](或可用atan2实现返回的任何值)内的角度

    x
    分别是点积和叉积

    无需显式分支和除法/向量长度计算

    解释为什么这样做:让alpha是向量之间的直接角度(0°到180°)和beta是我们正在寻找的角度(0°到360°),使用
    beta==alpha
    beta==360°-alpha

    Va . Vb == |Va| * |Vb| * cos(alpha)    (by definition) 
            == |Va| * |Vb| * cos(beta)     (cos(alpha) == cos(-alpha) == cos(360° - alpha)
    
    
    Va x Vb == |Va| * |Vb| * sin(alpha) * n1  
        (by definition; n1 is a unit vector perpendicular to Va and Vb with 
         orientation matching the right-hand rule)
    
    Therefore (again assuming Vn is normalized):
       n1 . Vn == 1 when beta < 180
       n1 . Vn == -1 when beta > 180
    
    ==>  (Va x Vb) . Vn == |Va| * |Vb| * sin(beta)
    

    这是一个Matlab代码,用于计算二维或三维两个向量u,v之间的有符号角度。该代码是自解释的。符号约定是在ix和iy([1,0,0],[0,1,0])或iy和iz([0,1,0],[0,0,1])之间输出正+90°

    函数thetaDEG=angDist2Vecs(u,v)
    如果长度(u)==3
    %3D,可以使用十字来解析符号
    uMod=sqrt(总和(u.^2));
    vMod=sqrt(总和(v.^2));
    uvPr=总和(u.*v);
    Costeta=最小值(uvPr/uMod/vMod,1);
    thetaDEG=acos(costheta)*180/pi;
    %解析符号
    cp=(交叉(u,v));
    idxM=find(abs(cp)=max(abs(cp));
    s=符号(cp(idxM(1));
    如果s<0
    thetaDEG=-thetaDEG;
    结束
    elseif长度(u)=2
    %2D使用atan2
    数据=(atan2(v(2),v(1))-atan2(u(2),u(1)))*180/pi;
    其他的
    错误('u,v必须是二维或三维向量');
    结束
    
    还有-是的,我知道“acos(Va.Vb)”但是,由于余弦的性质,它总是给出积极的结果。你能解释一下Va吗?它平行于Vn吗?Vn是平面的法向量,所以它垂直于Va和Vb-Vn最初是已知的。这个问题的任务被简化了。在这种特殊情况下,Vn是唯一一个最初与r一起已知的向量然后将旋转矩阵R.Va计算为Vn和基数向量之一的叉积:Va=normalize(vnx{0,1,0});不需要除以
    (|Va | | Vb |)
    对于
    sin
    cos
    atan2
    的工作方式是分母抵消。我猜这是针对二维向量,而需要针对三维向量。两个三维向量所属的平面与XY不平行,因此仅使用x和y分量
    tan(beta) = sin(beta) / cos(beta) == ((Va x Vb) . Vn) / (Va . Vb)
    
    function thetaDEG = angDist2Vecs(u,v)
    
    if length(u)==3
        %3D, can use cross to resolve sign
        uMod = sqrt(sum(u.^2));
        vMod = sqrt(sum(v.^2));
        uvPr = sum(u.*v);
        costheta = min(uvPr/uMod/vMod,1);
    
        thetaDEG = acos(costheta)*180/pi;
    
        %resolve sign
        cp=(cross(u,v));
        idxM=find(abs(cp)==max(abs(cp)));
        s=sign(cp(idxM(1)));
        if s < 0
            thetaDEG = -thetaDEG;
        end
    elseif length(u)==2
        %2D use atan2
        thetaDEG = (atan2(v(2),v(1))-atan2(u(2),u(1)))*180/pi;
    else
        error('u,v must be 2D or 3D vectors');
    end