Jump to content
Unity Insider Forum

Rotationswinkel der x-Achse genau bestimmen


minuschki
 Share

Recommended Posts

Hallo zusammen

Ich brauche mal wieder eure Hilfe, da es mir unmöglich ist, ein Winkelproblem zu lösen! Folgende Problematik (siehe Bild): Ich ändere die Richtung eines Roboterarms mit Hilfe der Pfeiltasten. Die Rotation startet bei 0 und soll auf ein Maximum von 160 und ein Minimum von -120 beschränkt sein. Ich mach dies mittels einer float-Variabel www, welche sozusagen als Winkelzähler fungiert. www wird also durch die Pfeiltasten entweder schrittweise inkrementiert oder dekrementiert und solange der Wert innerhalb der Grenzen ist, wird dann das Teilstück des Roboterarms gedreht. Im anderen Fall setze ich Mathf.Clamp ein, um www in seinen Grenzen zu halten. Das funktioniert soweit alles gut! Der Arm dreht sich und stoppt jeweils bei -120 oder +160.

Nun das eigentliche Problem: Angenommen, der Arm wird durch einen Zufallwert in eine neue Position gebracht, dann müsste ich den Winkel auslesen und dementsprechend den Zähler www neu setzen! Leider ist es aber so, dass der angezeigte Winkel im Inspektor und der Wert, den mir "transform.localRotation.eulerAngles.x" zurückgibt, sehr oft nicht identisch sind. Innerhalb -90 und +90 funktioniert "transform.localRotation.eulerAngles.x" eigentlich, aber nicht, wenn diese Werte überschritten werden. Dann werden für zwei unterschiedliche Winkel die gleichen Werte ausgegeben. Meine Recherchen haben ergeben, dass die ganze Problematik mit Quaternions zusammenhängt und dass man eben "transform.localRotation.eulerAngles.x" nicht benutzen soll, um den Rotationswinkel einer Achse zu ermitteln. Leider steht aber auch nirgends geschrieben, wie man es denn richtig machen soll, um den Winkel der x-Achse verlässlich zu erhalten. Ich habe viel rumprobiert und versucht mit Vector3.Angle das Problem zu lösen, habe es aber nicht hingekriegt.

Also: Wie muss man vorgehen, um einen unbekannten Winkel der x-Achse exakt zu bestimmen?

Besten Dank für eure Bemühungen!

 

using System.Collections;
using UnityEngine;


public class Drehen : MonoBehaviour
{

	public float speed;

	public float www; // Winkelzähler
	public float wmin = -120;
	public float wmax = 160;


	void Update ()
	{
		speed = Input.GetAxis ("Horizontal");
		www = www + speed;


		if (www > wmin && www < wmax) {
			transform.Rotate (speed, 0, 0); // nur rotieren, wenn die Grenzen eingehalten werden
		} else {
			www = Mathf.Clamp (www, wmin, wmax);
		}


		if (Input.GetMouseButton (1)) {
			float zufallwinkel = Random.Range (wmin, wmax); // Zufallswinkel setzen z.B. 150

			// mit "transform.localRotation" die Rotation neu setzen und den Winkelzähler auf den neuen Wert setzen 

			www = transform.localRotation.eulerAngles.x;

			// Das klappt nicht, weil die Variable zufallwinkel und transform.localRotation.eulerAngles.x oft nicht identisch sind
		}
	}
}

 

Winkel.jpg

Link to comment
Share on other sites

Ja, das ist so ein Problem.
Wenn du transform.Rotate nutzt dann rotiert er um die Werte, die du ihm übergibst. Das ist einfach, weil du nicht wissen musst, welche Werte vorher da waren.
Ist aber blöd, wenn man das wo anders setzen will. Da intern die Quaternions genommen werden, die den sogenannten Gimbal Lock unterbinden, können theoretisch alle 3 Achsen verändert werden, obwohl du nur über die X Achse drehen wolltest.

Versuch mal deine Umgebung so umzubauen, dass du keine negativen Winkel hast. Du fängst also nicht bei 0 an, sondern bei 180. Deine Endwerte wären dann 20° und 340°.
Und immer wenn durch Zufall der Wert negativ sein sollte, addierst du einfach 360° drauf.
Jetzt nutzt du auch nicht Rotate, sondern setzt einfach die neuen Werte, so wie du es willst.

Außerdem solltest du in der Update immer die Time.deltaTime als Multiplikator für Bewegungen und Drehungen nutzen, damit es auf allen Rechnern und mit allen Framerates gleich schnell abläuft.

Also:
Dreh dein Dingen so, dass oben jetzt nicht mehr 0° sondern 180° ist.
Das Folgende passiert jetzt in der Update, also jedes Frame. Die Variablen sind lokal in der Update deklariert. Du kannst die natürlich auch oben deklarieren.
Zuerst lokale Winkel auslesen und und einer Vector3 Variable übergeben. - Vector3 theRotation = transform.localEulerAngles;
Sollte theRotation.x negativ sein, dann addiere gleich 360° dazu. Wenn sie über 360° ist, ziehst du halt 360° ab.

Dann den Speed mit der deltaTime und einem frei einstellbaren Faktor mutiplizieren. Du musst dir überlegen, wieviele Grad/Sekunde gedreht werden sollen.
Nehmen wir mal maximal 20°/Sekunde an. -  float speed=Input.GetAxis("Horizontal")*Time.deltaTime*20;

Dann den neuen Winkel einklemmen dem x der Vector3 Variable übergeben - theRotation.x= Mathf.Clamp(theRotation.x+speed, wmin,wmax);

Dann dem Transform übergeben. - transform.localEulerAngles = theRotation;

Das zufällige Setzen per Maus machste genauso, nur ohne den Speed mit einzubauen. Also auslesen, dem theRotation.x  einen Zufallswert geben und alles wieder dem Transform übergeben.

Transform.Rotate ist auch nichts anderes als ein Setzen von Werten. Das passiert nur intern und du brauchst dich nicht um die vorigen Werte kümmern.
Dadurch verlierst du aber auch Kontrolle, deswegen mach es mal so, wie ich geschrieben habe.
Ich habe das aus dem Kopf da hin geklatscht! Können also Fehler drin sein. Du weißt aber jetzt was ich meine.

Link to comment
Share on other sites

Hallo malzbie

Vielen Dank für deine wertvollen Inputs! Ich denke, du hast mein Problem voll durchschaut und werde natürlich versuchen deine Ratschläge umzusetzen!

"Und immer wenn durch Zufall der Wert negativ sein sollte, addierst du einfach 360° drauf." Tatsächlich handelt es sich aber nicht um einen Zufallswert, sondern um eine gespeicherte "transform.localRotation", die dem Objekt wieder als Quternion.Euler zugewiesen wird. Nun fehlt mir aber die Möglichkeit, die einzelnen Winkel auszulesen!

 

P.s.: Ich bin heute Morgen noch auf dieses Script gestossen und habe es ausprobiert! Die Werte für den X Winkel werden korrekt berechnet, aber nur wenn Y und Z beide 0 sind. Ansonsten verschiebt sich der berechnete X Wert leider um ein paar Grad. Scheint eventuell ein Problem von "insrinsic" oder "extrinsic" zu sein. Wüsste aber nicht, wie man die Berechnung umbauen müsste, dass sie immer korrekte Winkel zurückgibt.

Würdest du mir von dieser Methode abraten und auf jeden Fall deinen Lösungsvorschlag bevorzugen oder wäre das eventuell auch eine Lösung?

Danke für deine Mühe

 

if(Input.GetMouseButton(1))
	{

	//	Quaternion q = transform.localRotation;

	//	var xWinkel = Mathf.Atan2(2.0f * (q.y * q.z + q.w * q.x), q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z) * Mathf.Rad2Deg;
	//	var yWinkel = Mathf.Asin(-2.0f * (q.x * q.z - q. w* q.y)) * Mathf.Rad2Deg;
	//	var zWinkel = Mathf.Atan2(2.0f * (q.x * q.y + q.w * q.z), q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z) * Mathf.Rad2Deg;
	
	//	Debug.Log ("X Winkel = " + xWinkel + "   Y Winkel = " + yWinkel + "   Z Winkel = " + zWinkel);

	//	This should fit for intrinsic tait-bryan rotation of xyz-order. For other rotation orders,
	//	extrinsic and proper-euler rotations other conversions have to be used.


		Quaternion q = transform.localRotation;
		www = Mathf.Atan2(2.0f * (q.y * q.z + q.w * q.x), q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z) * Mathf.Rad2Deg;

		Debug.Log("Der berechnete X Winkel beträgt: " + www);
	}

 

Link to comment
Share on other sites

Hallo,

vor 1 Stunde schrieb minuschki:

Nun fehlt mir aber die Möglichkeit, die einzelnen Winkel auszulesen!

Hab mal ein Beispiel von Unity eingedeutscht. Vielleicht hilft das dir weiter.

Quaternion.eulerAngles

public Vector3 eulerAngles;

Gibt die Euler-Winkeldarstellung der Drehung zurück oder legt diese fest.

Euler-Winkel können eine dreidimensionale Drehung darstellen, indem drei separate Drehungen um einzelne Achsen durchgeführt werden. In Unity werden diese Drehungen um die Z-Achse, die X-Achse und die Y-Achse in dieser Reihenfolge durchgeführt.

Sie können die Drehung einer Quaternion festlegen, indem Sie diese Eigenschaft festlegen, und Sie können die Euler-Winkelwerte lesen, indem Sie diese Eigenschaft auslesen.

Wenn Sie die .eulerAngles-Eigenschaft zum Festlegen einer Drehung verwenden, ist es wichtig zu verstehen, dass Sie zwar X-, Y- und Z-Drehungswerte zur Beschreibung der Drehung angeben, diese Werte jedoch nicht in der Drehung gespeichert werden. Stattdessen werden die X-, Y- und Z-Werte in das interne Format der Quaternion konvertiert.

Wenn Sie die .eulerAngles-Eigenschaft lesen, konvertiert Unity die interne Darstellung der Drehung der Quaternion in Euler-Winkel. Da es mehr als eine Möglichkeit gibt, eine bestimmte Drehung mithilfe von Euler-Winkeln darzustellen, können sich die Werte, die Sie zurücklesen, erheblich von den von Ihnen zugewiesenen Werten unterscheiden. Dies kann zu Verwirrung führen, wenn Sie versuchen, die Werte schrittweise zu erhöhen, um eine Animation zu erzeugen. Weitere Informationen finden Sie im unteren Skripting-Beispiel.

Um diese Art von Problemen zu vermeiden, wird empfohlen, mit Drehungen zu arbeiten, indem Sie beim Lesen von .eulerAngles nicht auf konsistente Ergebnisse angewiesen sind, insbesondere wenn Sie versuchen, eine Drehung schrittweise zu erhöhen, um eine Animation zu erzeugen.

Im folgenden Beispiel wird die Drehung eines GameObject mithilfe von eulerAngles basierend auf dem Input des Benutzers veranschaulicht. Das Beispiel zeigt, dass wir uns nie darauf verlassen, die Quanternion.eulerAngles zu lesen, um die Drehung zu erhöhen, sondern sie mit unseren Vector3 AktuellerEulerWinkel festlegen. Alle Rotationsänderungen treten in der AktuellerEulerWinkel-Variablen auf, die dann auf die Quaternion angewendet werden, um das oben erwähnte Problem zu vermeiden.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ExampleScript : MonoBehaviour
{
    float rotationSpeed = 45;
    Vector3 AktuellerEulerWinkel;
    Quaternion AktuelleRotation;
    float x;
    float y;
    float z;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.X)) x = 1 - x;
        if (Input.GetKeyDown(KeyCode.Y)) y = 1 - y;
        if (Input.GetKeyDown(KeyCode.Z)) z = 1 - z;

        // Ändern des Vector3, basierend auf der Eingabe multipliziert mit Geschwindigkeit und Zeit
        AktuellerEulerWinkel += new Vector3(x, y, z) * Time.deltaTime * rotationSpeed;

        // Verschieben des Werts von Vector3 in das Quanternion.eulerAngle-Format
        AktuelleRotation.eulerAngles = AktuellerEulerWinkel;

        //Wende die Quaternion.eulerAngles-Änderung auf das gameObject an
        transform.rotation = AktuelleRotation;
    }

    void OnGUI()
    {
        GUIStyle style = new GUIStyle();
        style.fontSize = 24;
        // Verwende eulerAngles, um die Euler-Winkel der in Transform.Rotation gespeicherten Quaternion anzuzeigen
        GUI.Label(new Rect(10, 10,0, 0), "Rotation X:" + x + " Y:" + y + " Z:" + z, style);

        //gibt den Quanternion.eulerAngles-Wert aus
        GUI.Label(new Rect(10, 60, 0, 0), "AktuelleRotation: " + AktuellerEulerWinkel, style);

        //gibt die transform.eulerAngles des GameObject aus
        GUI.Label(new Rect(10, 110, 0, 0), "GameObject World Euler Winkel: " + transform.eulerAngles, style);
    }
}

Bin auch nicht gerade ein Quaternion Freak.🙂 

Gruß Jog

Link to comment
Share on other sites

Hallo Jog

Danke für deinen Beitrag!

So wie ich das sehe, funktioniert der "AktuellerEulerWinkel" genau gleich, wie mein Winkelzähler "www", nämlich, dass der bestehende Wert erhört oder erniedrigt wird. Die Winkelangabe ist daher auch analog zum Inspektorwinkel. Mein Problem ist aber, dass ich irgendeine Rotation vor mir habe, deren Winkel ich zwar optisch im Inspektor ablesen kann, aber eben nicht auf Grund der vorliegenden Rotation einzeln berechnen kann.

Marzbie hat mir einige Vorschläge gemacht, um das Problem zu lösen. Ich habe inzwischen die Drehung so umgesetzt, dass sie nicht mehr auf "transform.rotate" basiert, sondern durch "transform.localRotation =" direkt gesetzt wird.

Der andere Vorschlag, die Objekte um 180° zu drehen, habe ich noch nicht verstanden!

Link to comment
Share on other sites

@ malzbie

Ich habe inzwischen die Drehung auf "transfom.localRotation =" umgebaut und das klappt soweit gut. Allerdings ist mir leider gar nicht klar, was genau gemeint ist mit: "Versuch mal deine Umgebung so umzubauen, dass du keine negativen Winkel hast. Du fängst also nicht bei 0 an, sondern bei 180. Deine Endwerte wären dann 20° und 340°."

Ich hoffe sehr, dass damit ein Offset des Winkelwertes um 180 gemeint ist und nicht das physische Drehen der Objekte um 180°!

Nehmen wir einmal an, es geht um ein menschliches Bein. Gesteckt im Stehen ist es bei 0°. Eine flexible Ballerina kann es wahrscheinlich ohne Probleme um 160° nach vorne beugen und vielleicht um -120° nach hinten biegen können. Wie würde man denn nun deine Idee an diesem Beispiel konkret anwenden?

Link to comment
Share on other sites

Schau dir deine Grafik von ganz oben an. Da hast du 0° als gerade nach oben. Willst du jetzt vor und zurück drehen, geht's einmal ins Positive und einmal ins Negative.

Wäre der Grundwinkel jetzt nicht 0° sondern 180°, dann würde es niemals zu einem negativen Winkel kommen und alles ist viel leichter zu bewerkstelligen.
das ist alles. Aber naürlich nicht auf alle Gegebenheiten anzuwenden.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

×
×
  • Create New...