在3D空间中丢失-来自旋转矩阵(javafx仿射)的倾斜值(euler?)仅部分起作用 [英] lost in 3D space - tilt values (euler?) from rotation matrix (javafx affine) only works partially

查看:61
本文介绍了在3D空间中丢失-来自旋转矩阵(javafx仿射)的倾斜值(euler?)仅部分起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前一段时间我问了这个问题:javafx-如何相对于节点旋转轴而不是场景旋转轴将偏航,俯仰和横滚增量(不是欧拉)应用于节点?

it is a while ago that I asked this question: javafx - How to apply yaw, pitch and roll deltas (not euler) to a node in respect to the nodes rotation axes instead of the scene rotation axes?

今天我想问的是,如何从旋转矩阵中获得相对于身体(而不是房间)的倾斜(前后和横向).为了使问题易于理解,我从乔斯·佩雷达(JoséPereda)的绝妙答案中获得了最终代码,并基本添加了方法"getEulersFromRotationMatrix".这有点奏效,但在某些时候吓坏了.

Today I want to ask, how I can get the tilt (fore-back and sideways) relative to the body (not to the room) from the rotation matrix. To make the problem understandable, I took the final code from the fantastic answer of José Pereda and basicly added a method "getEulersFromRotationMatrix". This is working a bit, but at some point freaks out.

附带找到整个工作示例.通过以下点击路径,该问题变得很明显:

Attached find the whole working example. The problem becomes clear with the following click path:

// right after start
tilt fore
tilt left  // all right
tilt right
tilt back  // all right

// right after start
turn right
turn right
turn right
tilt fore
tilt back  // all right
tilt left  // bang, tilt values are completely off

当按钮按预期方式移动躯干时,倾斜值(在按钮下方打印)在某些时候表现不佳.

While the buttons move the torso as expected, the tilt values (printed out under the buttons) behave wrong at some point.

import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.transform.Affine;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;


public class PuppetTestApp extends Application {
    
    private final int width = 800;
    private final int height = 500;
    private XGroup torsoGroup;
    private final double torsoX = 50;
    private final double torsoY = 80;

    private Label output = new Label();

    public Parent createRobot() {
        Box torso = new Box(torsoX, torsoY, 20);
        torso.setMaterial(new PhongMaterial(Color.RED));
        Box head = new Box(20, 20, 20);
        head.setMaterial(new PhongMaterial(Color.YELLOW.darker()));
        head.setTranslateY(-torsoY / 2 -10);

        Box x = new Box(200, 2, 2);
        x.setMaterial(new PhongMaterial(Color.BLUE));
        Box y = new Box(2, 200, 2);
        y.setMaterial(new PhongMaterial(Color.BLUEVIOLET));
        Box z = new Box(2, 2, 200);
        z.setMaterial(new PhongMaterial(Color.BURLYWOOD));

        torsoGroup = new XGroup();
        torsoGroup.getChildren().addAll(torso, head, x, y, z);
        return torsoGroup;
    }

    public Parent createUI() {
        HBox buttonBox = new HBox();

        Button b;
        buttonBox.getChildren().add(b = new Button("Exit"));
        b.setOnAction( (ActionEvent arg0) -> { Platform.exit(); } );

        buttonBox.getChildren().add(b = new Button("tilt fore"));
        b.setOnAction(new TurnAction(torsoGroup.rx, 15) );

        buttonBox.getChildren().add(b = new Button("tilt back"));
        b.setOnAction(new TurnAction(torsoGroup.rx, -15) );

        buttonBox.getChildren().add(b = new Button("tilt left"));
        b.setOnAction(new TurnAction(torsoGroup.rz, 15) );

        buttonBox.getChildren().add(b = new Button("tilt right"));
        b.setOnAction(new TurnAction(torsoGroup.rz, -15) );

        buttonBox.getChildren().add(b = new Button("turn left"));
        b.setOnAction(new TurnAction(torsoGroup.ry, -28) ); // not 30 degree to avoid any gymbal lock problems

        buttonBox.getChildren().add(b = new Button("turn right"));
        b.setOnAction(new TurnAction(torsoGroup.ry, 28) ); // not 30 degree to avoid any gymbal lock problems

        VBox vbox = new VBox();
        vbox.getChildren().add(buttonBox);
        vbox.getChildren().add(output);
        return vbox;
    }

    class TurnAction implements EventHandler<ActionEvent> {
        final Rotate rotate;
        double deltaAngle;

        public TurnAction(Rotate rotate, double targetAngle) {
            this.rotate = rotate;
            this.deltaAngle = targetAngle;
        }

        @Override
        public void handle(ActionEvent arg0) {
            addRotate(torsoGroup, rotate, deltaAngle);
        } 
    }

    private void addRotate(XGroup node, Rotate rotate, double angle) {
        Affine affine = node.getTransforms().isEmpty() ? new Affine() : new Affine(node.getTransforms().get(0));
        double A11 = affine.getMxx(), A12 = affine.getMxy(), A13 = affine.getMxz(); 
        double A21 = affine.getMyx(), A22 = affine.getMyy(), A23 = affine.getMyz(); 
        double A31 = affine.getMzx(), A32 = affine.getMzy(), A33 = affine.getMzz(); 

        Rotate newRotateX = new Rotate(angle, new Point3D(A11, A21, A31));
        Rotate newRotateY = new Rotate(angle, new Point3D(A12, A22, A32));
        Rotate newRotateZ = new Rotate(angle, new Point3D(A13, A23, A33));

        affine.prepend(rotate.getAxis() == Rotate.X_AXIS ? newRotateX : 
                rotate.getAxis() == Rotate.Y_AXIS ? newRotateY : newRotateZ);

        EulerValues euler= getEulersFromRotationMatrix(affine);
        output.setText(String.format("tilt fore/back=%3.0f    tilt sideways=%3.0f", euler.forward, euler.leftSide));
        
        node.getTransforms().setAll(affine);
    }

    public class XGroup extends Group {
        public Rotate rx = new Rotate(0, Rotate.X_AXIS);
        public Rotate ry = new Rotate(0, Rotate.Y_AXIS);
        public Rotate rz = new Rotate(0, Rotate.Z_AXIS);
    }

    @Override 
    public void start(Stage stage) throws Exception {
        Parent robot = createRobot();
        SubScene subScene = new SubScene(robot, width, height, true, SceneAntialiasing.BALANCED);
        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.setNearClip(0.01);
        camera.setFarClip(100000);
        camera.setTranslateZ(-400);
        subScene.setCamera(camera);

        Parent ui = createUI();
        StackPane combined = new StackPane(ui, subScene);
        combined.setStyle("-fx-background-color: linear-gradient(to bottom, cornsilk, midnightblue);");

        Scene scene = new Scene(combined, width, height);
        stage.setScene(scene);
        stage.show();
    }

    /**
     * Shall return the tilt values relative to the body (not relative to the room)
     * (Maybe euler angles are not the right term here, but anyway)
     */
    private EulerValues getEulersFromRotationMatrix(Affine rot) {
        double eulerX;  // turn left/right
        double eulerY;  // tilt fore/back
        double eulerZ;  // tilt sideways

        double r11 = rot.getMxx();
        double r12 = rot.getMxy();
        double r13 = rot.getMxz();

        double r21 = rot.getMyx();

        double r31 = rot.getMzx();
        double r32 = rot.getMzy();
        double r33 = rot.getMzz();

        // used instructions from https://www.gregslabaugh.net/publications/euler.pdf
        
        if (r31 != 1.0 && r31 != -1.0) {
            eulerX  = -Math.asin(r31);  // already tried with the 2nd solution as well
            double cosX = Math.cos(eulerX);
            eulerY = Math.atan2(r32/cosX, r33/cosX);
            eulerZ = Math.atan2(r21/cosX, r11/cosX);
        }
        else {
            eulerZ = 0;
            if (r31 == -1) {
                eulerX = Math.PI / 2;
                eulerY = Math.atan2(r12, r13);
            }
            else {
                eulerX = -Math.PI / 2;
                eulerY = Math.atan2(-r12, -r13);
            }
        }
        
        return new EulerValues(
                eulerY / Math.PI * 180.0, 
                eulerZ / Math.PI * 180.0, 
                -eulerX / Math.PI * 180.0);     
    }
    
    public class EulerValues {
        public double leftTurn;
        public double forward;
        public double leftSide;

        public EulerValues(double forward, double leftSide, double leftTurn) {
            this.forward = forward;
            this.leftSide = leftSide;
            this.leftTurn = leftTurn;
        }
    }


    public static void main(String[] args) {
        launch(args);
    }
}

PS:看来我几乎没有进展,但这仅仅是因为我试图将问题减少到可能的最低限度.如果您想了解这些内容如何嵌入到我的主项目中,可以观看我刚刚上传的这个小视频(但不向问题添加任何内容):

PS: This may look like I have close to no progress, but this is only because I try to reduce the question to the possible minimum. If you want to see how this stuff is embedded in my main project, you can watch this little video I just uploaded (but does not add anything to the question): https://www.youtube.com/watch?v=R3t8BIHeo7k

推荐答案

我想我现在一个人搞定了:我计算出的是默认"值.欧拉角,有时称为z x'z'',其中第1和第3旋转围绕同一轴.但是我正在寻找的是可以应用于z,y'和x''的角度(以该顺序)以达到旋转矩阵表示的位置的角度.(然后忽略z旋转).

I think I got it by myself now: What I computed was the "default" euler angles, sometimes refered to as z x' z'', where the 1st and 3th rotation is around the same axis. But what I am looking for are the angles that can be applied to the z, y' and x'' achses (in that order) to reach the position presented by the rotation matrix. (and then ignore the z rotation).

或者甚至更好地计算z y'x''euler和z x'y''euler并仅使用x'和y'值.

Or even better compute the z y' x'' eulers and the z x' y'' eulers and only use the x' and y' values.

已添加:不,那是错的.我确实计算了Tait-Bryan x y z的旋转.所以这不是解决方案.

Added: No, that was wrong. I indeed calculated the Tait-Bryan x y z rotations. So this was not the solution.

好的,新的解释:

我计算出的旋转轴是房间的相对旋转(不是对象的相对旋转),第二个旋转是在垂直轴上(我不感兴趣).但是由于它在中间",它可以抵消第1和第3旋转,这就是发生的情况.

The rotation axes wthat I calculate are room relative rotations (not object relative rotations), and the 2nd rotation is at the vertical axe (which I am not interested in). But because it is "in the middle", it can cancel out the 1st and 3th rotation, and this is what happens.

所以解决方案应该是改变旋转顺序,这是从我的矩阵到欧拉算法得出的.但是该怎么做?

So the solution should be the change the rotation order, that comes out of my matrix-to-euler algorithm. But how to do this?

我刚刚交换了所有的"y"和"z":

I just exchanged all "y" and "z":

    r11 = rot.getMxx();
    r12 = rot.getMxz();
    r13 = rot.getMxy();

    r21 = rot.getMzx();

    r31 = rot.getMyx();
    r32 = rot.getMyz();
    r33 = rot.getMyy();

现在它确实可以满足我的要求.:)

and now it really does what I want. :)

这篇关于在3D空间中丢失-来自旋转矩阵(javafx仿射)的倾斜值(euler?)仅部分起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆