The airplane orientation / attitude value blues. 17.08.2011 Update!

Hi

17.08.2011 Update!

See a attached demo file(orientation_b2581_0503.blend) on this post.

I added a two cosine’s to get smooth transitions. Now it’s spot on.

Comments?


Sample file contains a ‘ATTITUDE’ group, which contains a ATTITUDE, SENSOR1 & SENSOR2 objects, logics, properties and needed scripts.

NOTE! Do not move the SENSOR1 and SENSOR2 object’s relative the ATTITUDE object or the calculations will be in-accurate.

Feel free to use it.

--------------------------------------------------------------------------

ORIGINAL POST ON BELOW

Hi

I try to figure out this old problem again, how to get a ‘absolute axis angle values’ out from the orientation.

With ‘absolute axis angle values’ I mean a axis figures, which are not effected by other axis figures.

Lets assume,
the z-axis rotation is heading(where the nose is pointing) versus compass direction.
The x-axis rotation is plane pitch angle i.e. nose up/down versus horizon.
The y-axis rotation is plane roll angle i.e. rolling left/ right versus horizon.

Example 1
Plane compass heading is 45 deg or North-East. This heading figure should stay same, despite the plane pitch and/or roll angle, excluding the death vertical up/down situation.

Example 2
Plane is having a steady 15deg pitch climbing angle. During this climb the pilot is t performing a full roll (around y-axis) . The pitch angle(x-axis) figure should stay 15deg during this move, despite the roll attitude.

Got my point?

Any help to solve this orientation blues?

Attachments

orientation_b2581_0503.blend (373 KB)

If you want to use Python, you could use own.localOrientation[0], and own.localOrientation[1] to get pitch and roll.

I tried that, but the result is odd.

Controls

  • Pitch > up/down arrow keys
  • Roll > left/right arrow keys
  • Heading > left/right arrow keys with shift key

Any advises?

It looks like, it’s impossible to get any useful figures out from the BGE.

The euler is behaving oddly or what do you think about following:

  • The plane heading figure is -40 deg, which actually means 40deg, almost NE.
  • The pitch figure is -15 deg, which means a steady 15deg climb.

I roll plane to the right, 45deg bank. What are the heading and pitch figures?

  • pitch shows -22deg, which means a 22deg climb
  • heading figure shows -25deg

Just nonsense. The plane heading is still to the NE and climb angle is still same 15deg (value -15deg)

What are the heading and pitch figures during right 68deg bank?

  • pitch figure shows -45deg, which means a 45deg climb
  • heading figure shows 0deg, which is North, but actually plane heading is still NE…

Dear Devs
Why there’s no API to get your objects XYZ world/local attitude in absolutely figures?
The scale could be a -180.0 - 180.0deg or 0.0 - 359deg. The radians would good as well.

Sorry for not getting back sooner. It is quite possible to get the orientation of an object. I’ll try and post a .blend tonight.

The following formulas relate the aircraft angular rates around its body axis (p, q, r, or roll, pitch and yaw) to the aircraft orientation rates (phi_rate, theta_rate, psi_rate):


phi_rate = p + sin(phi)*tan(theta)*q + cos(phi)*tan(theta)*r
theta_rate = cos(phi)*q - sin(phi)*r
psi_rate = sin(phi)/cos(theta)*q + cos(phi)/cos(theta)*r

Here, the (usual) conventions are used where the euler angles psi, theta, phi are defined as successive counterclockwise rotations around z, y, and x axis, respectively, performed IN THIS ORDER.

Referring to Xjazz’s Example 1, the third relationship shows that the yaw (heading) angle (psi) is independent from the roll rate § but it changes, in general, in presence of a non-zero pitch rate (q).

To verify the above relationships, use a motion actuator to apply constant p, q, r to a game object. At the same time implement the above formulas in python, retrieve its euler angles at each iteration, using:

phi, theta, psi = obj.worldOrientation.to_euler()

and compare the results.

Here is your blend back.

orientation_b2581_01.blend (356 KB)

One problem is that if you turn the plane around the pitch is around 180 degrees (as if you were flying upside down.)

Thank you for your help.

Infininte,
Unfortunate the result is still odd and axes values are effecting to each others.

mb10,
I’m not sure, how to implement your script to the BGE. Hints?

See the attached example (tested under version 2.58a, probably works for any 2.5x, surely not applicable to 2.49): [ATTACH]150630[/ATTACH]

You may also be interested in reading this paper concerning the various attitude representations for aircraft (but take into account the long list of “ERRATA” at the end of the article…):
http://www.google.it/url?sa=t&source=web&cd=1&sqi=2&ved=0CB0QFjAA&url=http%3A%2F%2Fenac-lara.googlecode.com%2Fsvn%2Ftrunk%2Fmaster_gustavo%2Fbiblio%2Fquaternions%2FQuaternions.pdf&rct=j&q=review%20of%20attitude%20representations%20used%20for%20aircraft%20kinematics&ei=imdATvSQKovesgaxucinBg&usg=AFQjCNEATSe1z5ZsHGKEgQr6A0N_FRSOAQ&cad=rja

mb

Hi mb10,

Thank you for your file and link.

I was testing the .blend file and I was think ‘I don’t get this…’.

Then I start reading the Quaternions.pdf.
Right after the first page I was thinking 'I really don’t get this… and just eyeballed rest of the pages down the conclusion part.

Interesting. Turning / rotation seems to be a major PITA for the flight sim… Who could though.

So, I developed my own concept, which gives at this point a compass heading as I want it.

It calculates the heading from Cube and c1 object x and y coordinates.

Keys:

  • Pitch > up/down arrow keys
  • Roll > left/right arrow keys
  • Heading left Del
  • Heading right Page down
  • Move forward Space
  • Reset scene

Attachments

orientation_b2581_0201.blend (435 KB)

Hi

I found a very simple way to get a pitch attitude angle versus ground.

The roll attitude is not effecting at all. Sometimes the coding is pretty fun :yes:

import bge

from math import *

#-----------------------------------------------------------------------------
controller = bge.logic.getCurrentController()
own = controller.owner
#-----------------------------------------------------------------------------

if 'int0' not in own:
    own['int0']=1

    bge.logic.globalDict["loc"] = [0.0, 0.0, 0.0]

def comp(v1, v2):
    try:
        val = atan(v1/v2)

        if v1 != 0.0:
            if (-v1 > 0.0) and (-v2 > 0.0):
                #print '1'
                return round(degrees(val),1)
            
            if (-v1 > 0.0) and (-v2 < 0.0):
                #print '2'
                return 180.0 + round(degrees(val),1)    

            if (-v1 <0.0) and (-v2 < 0.0):
                #print '3', degrees(val)
                return 180.0 + round(degrees(val),1)

            if (-v1 < 0.0) and (-v2 > 0.0):
                #print '4'
                return 360.0 + round(degrees(val),1)

        else:
            if v2 > 0.0:
                return 180.0
            else:
                return 0.0
            
    except ZeroDivisionError:

        if v1 < 0.0:
            return 90.0
        else:
            return 270.0
      

own['heading'] = comp(own.position[0] - bge.logic.globalDict["loc"][0] , own.position[1] - bge.logic.globalDict["loc"][1])

own['pitch'] = -round(degrees(asin(own.position[2] - bge.logic.globalDict["loc"][2] / 1.0)),1)

SOLVED!

See first post

Hi

I’m working with python module version and for some reason I got following error:

"
SystemError: Blender Game Engine data has been freed, cannot use this python variable
Python script error - object ‘ATTITUDE’, controller ‘Python#CONTR#1’:
Traceback (most recent call last):
File “D:\B2581\ATTITUDE.py”, line 59, in att
a_x = -asin(own.worldPosition[2] - bge.logic.globalDict[‘SENSOR1’][2] / 1.0) # Pitch attitude (radians)
"

# ATTITUDE scrpit by Xjazz, 11082011.
# Use freely
import bge
from math import degrees, asin, atan
#-----------------------------------------------------------------------------
controller = bge.logic.getCurrentController()
own = controller.owner
#-----------------------------------------------------------------------------

if 'int0' not in own:
    own['int0']=1

    # DO NOT MOVE THESE SENSOR OBJECTS!    
    bge.logic.globalDict['SENSOR1'] = [0.0, 0.0, 0.0]
    bge.logic.globalDict['SENSOR2'] = [0.0, 0.0, 0.0]
    bge.logic.globalDict['SENSOR3'] = [0.0, 0.0, 0.0]    

    own['a_x'], own['a_y'], own['a_z'] = 0.0, 0.0, 0.0 # radians
    own['a_p'], own['a_r'], own['a_h'] = 0.0, 0.0, 0.0 # degrees
#    bge.logic.globalDict['ATTITUDE'] = [0.0, 0.0, 0.0] # global radians
    a_x, a_y, a_z  = 0.0, 0.0, 0.0
    
def loc(): # for sensors 1-3
#    own = controller.owner
    bge.logic.globalDict[own.name] = own.position

 
def adjust_roll(pos1, pos2, val):
    pos = pos1 - pos2
    if (val > 0.0) and (pos  > 0.0):
        return 3.14159265359 - val
        
    elif (val < 0.0) and (pos  > 0.0):
        return -3.14159265359 - val
    
    else:
        return val

def adjust_heading(pos1, pos2, val):
    pos = pos1 - pos2    

    if (val > 0.0) and (pos > 0.0):
        return 3.141592653590 - val
        
    elif (val < 0.0) and (pos > 0.0):
        return 3.14159265359 - val
    
    elif (val < 0.0) and (pos < 0.0):
        return 6.28318530718 + val

    elif (val == 0.0) and (pos > 0.0):
        return 3.14159265359
    
    else:
        return val

def att():
    # A line on below is causing a error
    a_x = -asin(own.worldPosition[2] - bge.logic.globalDict['SENSOR1'][2] / 1.0) # Pitch attitude (radians)
    
    a_y = asin(own.worldPosition[2] - bge.logic.globalDict['SENSOR2'][2] / 1.0) # Roll attitude (radians)
    a_y = adjust_roll(own.worldPosition[2], bge.logic.globalDict['SENSOR3'][2], a_y)# adjust: +/- 180 R/L
        
    a_z = asin(bge.logic.globalDict['SENSOR1'][0] - own.worldPosition[0] / 1.0) # Heading attitude (radians) 
    a_z = adjust_heading(own.worldPosition[1], bge.logic.globalDict['SENSOR1'][1], a_z) # adjust: 0-360
    
   

    bge.logic.globalDict['ATTITUDE'] = [a_x, a_y, a_z]

    print (bge.logic.globalDict['ATTITUDE'])

I never seen this before. How to solve?

Hi

Anyone know why the def att() function is giving mentioned error when scene is restarted in module mode?

I have same problem with my FDM4BGE.py module as well.

I tried your script from post 13# with your orientation_b2581_0201.blend and I don’t get any fault.

Blender Game Engine data has been freed, cannot use this python variable

May be you freed your globalDict with another script.

It looks on the error as the object own is a reference to has been ended…

Hi

First of all, I’m sorry for old file versions :o

Please, see now attached files.

The ‘orientation_b2581_05.blend’ is a SCRIPT version, which works ok after the reset (r-key).

The ‘orientation_b2581_05M.blend’ is a MODULE version, which causes an error after the reset (r-key).

Changes

  • Third sensor (Sensor3) added, which allows me to define the roll left/right directions.
  • The ATTITUDE group now contains the sensors, which were missing in previous version…
  • Added error routine allows to place and rotate the ATTITUDE object freely without err message

Anyhow, the module version do not working after the scene reset/restart.

Attachments

orientation_b2581_05M.blend (450 KB)orientation_b2581_05.blend (443 KB)

That make sense… When You reset the scene everything is reinitialized and the own reference in the module is not valid anymore.

After restarting the scene You have to update the own variable. Now, it’s not fore sure that a restart is blocking so it might be dirty to code (BGE needs moor callbacks in general and all non blocking functions should really have a callback that’s run when the function have completed). I take a look - no promises.

Terve LaH

Tack så mycket.

If it’s a messy module code job, I can live with script version.

This is a fixed module - that catch exception in att() and rerun the init. Not pretty - catch all exceptions…


# ATTITUDE scrpit by Xjazz, 11082011.
# Use freely
import bge
from math import degrees, asin, atan

def init():
    global own, POS
    #-----------------------------------------------------------------------------
    controller = bge.logic.getCurrentController()
    own = controller.owner
    #-----------------------------------------------------------------------------
    
    if 'int0' not in own:
        own['int0']=1
    
        # DO NOT MOVE THESE SENSOR OBJECTS!    
        bge.logic.globalDict['SENSOR1'] = [0.0, 0.0, 0.0]
        bge.logic.globalDict['SENSOR2'] = [0.0, 0.0, 0.0]
        bge.logic.globalDict['SENSOR3'] = [0.0, 0.0, 0.0]    

        own['a_x'], own['a_y'], own['a_z'] = 0.0, 0.0, 0.0 # radians
        own['a_p'], own['a_r'], own['a_h'] = 0.0, 0.0, 0.0 # degrees
    #    bge.logic.globalDict['ATTITUDE'] = [0.0, 0.0, 0.0] # global radians
    
    POS = own.worldPosition

init()
 
def adjust_roll(pos1, pos2, val):
    pos = pos1 - pos2
    if (val > 0.0) and (pos  > 0.0):
        return 3.14159265359 - val
        
    elif (val < 0.0) and (pos  > 0.0):
        return -3.14159265359 - val
    
    else:
        return val

def adjust_heading(pos1, pos2, val):
    pos = pos1 - pos2    

    if (val > 0.0) and (pos > 0.0):
        return 3.141592653590 - val
        
    elif (val < 0.0) and (pos > 0.0):
        return 3.14159265359 - val
    
    elif (val < 0.0) and (pos < 0.0):
        return 6.28318530718 + val

    elif (val == 0.0) and (pos > 0.0):
        return 3.14159265359
    
    else:
        return val

def att(): 
    try:       
        own['a_x'] = -asin(POS[2] - bge.logic.globalDict['SENSOR1'][2] / 1.0) # Pitch attitude (radians)
        
        own['a_y'] = asin(POS[2] - bge.logic.globalDict['SENSOR2'][2] / 1.0) # Roll attitude (radians)
        own['a_y'] = adjust_roll(POS[2], bge.logic.globalDict['SENSOR3'][2], own['a_y'])# adjust: +/- 180 R/L
            
        own['a_z'] = asin(bge.logic.globalDict['SENSOR1'][0] - POS[0] / 1.0) # Heading attitude (radians) 
        own['a_z'] = adjust_heading(POS[1], bge.logic.globalDict['SENSOR1'][1], own['a_z']) # adjust: 0-360
    
    except:
        init()
    
    own['a_p'] = degrees(own['a_x'])
    own['a_r'] = degrees(own['a_y'])
    own['a_h'] = degrees(own['a_z'])