mechanizmos58

QUATERNION_RODRIGUES_ALTMANN_COMPOSITE_AXIS_ANGLE_ROTATION

Dec 19th, 2023 (edited)
2,280
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.52 KB | None | 0 0
  1. '''
  2.  
  3. USE A MINIMALIST FLIGHT SIMULATOR TO TEST AXIS-ANGLE ROTATION AND ALGEBRA
  4.  
  5. # 07_10_23
  6. # ...
  7. # 05_01_24
  8.  
  9. # MOUSE & KEYS
  10. #
  11. UP KEY - INCREASE VELOCITY
  12. DOWN KEY - BRAKE
  13. MOUSE LEFT, RIGHT - TURN
  14. MOUSE UP, DOWN - PITCH
  15. LEFT, RIGHT KEYS - ROLL
  16. PAUSE, BREAK - PAUSE
  17. SPACE -  VELOCITY = 0
  18. ENTER, ESC - EXIT
  19.  
  20. '''
  21.  
  22. from math import acos, degrees, sin, cos, sqrt
  23. from numpy import arange, array, concatenate, empty, identity, zeros
  24. from OpenGL.GL import *
  25. from OpenGL.GLU import *
  26. import pygame
  27. from pygame.locals import *
  28. import sys
  29.  
  30.  
  31. def main():
  32.  
  33.     # clock = pygame.time.Clock()
  34.  
  35.     screenwidth = 1920
  36.     screenheight = 1080
  37.     sw2 = screenwidth/2.0
  38.     sh2 = screenheight/2.0
  39.  
  40.     pygame.init()
  41.     pygame.display.set_caption('ESC or RETURN exits')
  42.     gl_init(screenwidth, screenheight, FULL_SCREEN=1)
  43.  
  44.     make_hoops1()
  45.     make_hoops2()
  46.     make_ground()
  47.  
  48.     pygame.mouse.set_visible(0)
  49.     pygame.mouse.set_pos([sw2, sh2])
  50.  
  51.     PAUSE = 0
  52.     JOYSTICK = 0
  53.  
  54.     # supply a identity matrix to a function
  55.     I3 = identity(3)
  56.     I4 = identity(4)
  57.     # supply a matrix/vector to a function to be used and thrown away
  58.     Z3 = zeros((3,3))
  59.     Z4 = zeros((4,4))
  60.     vec3 = empty((3))
  61.  
  62.     pos = array([0.0, 170.0, 600.0])
  63.  
  64.     velocity = 0.0
  65.     Phi = 0.0
  66.     Theta = 0.0
  67.     Psi = 0.0
  68.  
  69.     R = array([1.0, 0.0, 0.0]) # RIGHT (E1)
  70.     U = array([0.0, 1.0, 0.0]) # UP (E2)
  71.     B = array([0.0, 0.0, 1.0]) # BACK (E3)
  72.  
  73.     glTranslate(0.0, 0.0, -5.0)
  74.  
  75.     if JOYSTICK:
  76.         joystick = pygame.joystick.Joystick(0)
  77.         joystick.init()
  78.  
  79.     while 1:
  80.  
  81.         KEYS = pygame.key.get_pressed()
  82.  
  83.         if KEYS[K_UP]:
  84.             velocity += 0.08
  85.             if velocity > 8.0:
  86.                 velocity = 8.0
  87.  
  88.         if KEYS[K_DOWN]:
  89.             velocity -= 0.04
  90.             if velocity < 0.0:
  91.                 velocity = 0.0
  92.  
  93.         for event in pygame.event.get():
  94.  
  95.             if event.type == QUIT:
  96.                 pygame.quit()
  97.                 return
  98.  
  99.             elif event.type == KEYDOWN:
  100.                 if event.key in [K_ESCAPE, K_RETURN]:
  101.                     pygame.quit()
  102.                     return
  103.                 elif event.key == K_PAUSE:
  104.                     PAUSE ^= 1
  105.                 elif event.key == K_SPACE:
  106.                     velocity = 0.0
  107.  
  108.         if PAUSE: continue
  109.  
  110.         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
  111.  
  112.         mousex, mousey = pygame.mouse.get_pos()
  113.  
  114.         if JOYSTICK:
  115.             # YAW (LEFT, RIGHT)
  116.             Phi  = joystick.get_axis(2) / 80.0
  117.             # PITCH (UP, DOWN)
  118.             Theta = -joystick.get_axis(1) / 80.0
  119.             # ROLL (CW, CCW)
  120.             Psi = -joystick.get_axis(0) / 100.0
  121.         else:
  122.             # YAW (LEFT, RIGHT)
  123.             Phi = (mousex - sw2) / 1.0e5
  124.             # PITCH (UP, DOWN)
  125.             Theta = (mousey - sh2) / 5.0e4
  126.             # ROLL (CW, CCW)
  127.             Psi = 0.0
  128.             if KEYS[K_LEFT]:
  129.                 Psi = 0.01
  130.             if KEYS[K_RIGHT]:
  131.                 Psi = -0.01
  132.  
  133.         # COMPOSE A SINGLE ANGLE-AXIS ROTATION FROM BODY FRAME AXIS ROTATIONS
  134.         #
  135.         Gamma, E = composite_rotation(-B, Psi, U, Phi, vec3)
  136.         Gamma, E = composite_rotation(E, Gamma, R, Theta, vec3)
  137.  
  138.         if 1:
  139.             C = rotation_matrix_4x4(Gamma, E, I4, Z4)
  140.             glMultMatrixd(C)
  141.         else:
  142.             glRotatef(degrees(Gamma), *E)
  143.  
  144.         R = rotate_r(R, E, -Gamma, vec3)
  145.         U = rotate_r(U, E, -Gamma, vec3)
  146.         B = rotate_r(B, E, -Gamma, vec3)
  147.  
  148.         glPushMatrix()
  149.  
  150.         pos += -B * velocity
  151.         glTranslatef(*-pos)
  152.  
  153.         glColor3f(0.0, 0.0, 1.0)
  154.         glCallList(g.hatch_ground)
  155.         glColor3f(0.7, 0.0, 0.0)
  156.         glCallList(g.square_hoops1)
  157.         glColor3f(0.0, 0.7, 0.7)
  158.         glCallList(g.square_hoops2)
  159.  
  160.         glPopMatrix()
  161.  
  162.         pygame.display.flip()
  163.         pygame.time.wait(10)
  164.         # clock.tick(100)
  165.  
  166.  
  167. def normalize(v):
  168.     norm = sqrt(v[0]**2 + v[1]**2 + v[2]**2)
  169.     if norm > 0.0:
  170.         return v / norm
  171.     return v
  172.  
  173.  
  174. def magnitude(v):
  175.     return sqrt(v[0]**2 + v[1]**2 + v[2]**2)
  176.  
  177.  
  178. def npcross(u, v):
  179.     x = u[1]*v[2] - u[2]*v[1]
  180.     y = u[2]*v[0] - u[0]*v[2]
  181.     z = u[0]*v[1] - u[1]*v[0]
  182.     return array([x,y,z])
  183.  
  184.  
  185. def rotate_r(r, e, ๐šน, cross_er):
  186.  
  187.     """
  188.  
  189. r is the position vector to rotate
  190. e is the unit direction vector about which to rotate
  191.  ๐šน is the rotation angle in radians
  192. cross_er = empty((3))
  193.  
  194. Does not check if e is normalized
  195.  
  196. Rotation is right-hand direction
  197.  
  198. #------------------------------------------------------------------
  199.  
  200. Rodrigues finite rotation formula for numpy vector format
  201.  
  202. Rotation Transforms for Computer Graphics, John Vince (2011a), p.139
  203. Classical Mechanics, Goldstein et al. p. 162 ("the rotation formula")
  204. Computer Graphics Software Construction, John R. Rankin (1989), p.279
  205. Quaternions for Computer Graphics, John Vince (2011b) pp. 80-, 123(!)
  206. (Demonstrates the equivalence of Quaternion rotation and Rodrigues rotation)
  207.  
  208. In matrix form:
  209. Analytical Mechanics of Space Systems by Schaub & Junkins eq.3.82 (p.105)
  210. in video: Axis-Angles, Euler Parameters, Quaternions Matlab Examples
  211.   by Shane Ross (RossDynamicsLab) starting at 25 minutes
  212. Graphics Gems (1990), p.503-5 (Patrick-Gilles Maillot)
  213. Do We Really Need Quaternions? by Diana Gruber (2000)
  214. Demonstrates the equivalence of the Quaternion matrix and the
  215.    direction cosine matrix from Graphics Gems (1990),
  216.    and Schaub & Junkins p.102, which can be generated by the
  217.    Rodrigues matrix formulation:
  218.  
  219.    Crodrigues0 = I*cos๐ž - sin๐ž*Ex + (1.0-cos๐ž)*E*E.T
  220.    Crodrigues1 = I*cos๐ž - sin๐ž*Ex + (1.0-cos๐ž)*[email protected]
  221.    where:
  222.  
  223.        |  0.0  -E[2]  E[1] |
  224.   Ex = |  E[2]  0.0  -E[0] |
  225.        | -E[1]  E[0]  0.0  |
  226.  
  227. Rotations, Quaternions, and Double Groups
  228. by Simon L. Altmann (1986 book), p.159
  229.  
  230. Hamilton, Rodrigues, and the Quaternion Scandal
  231. by Simon L. Altmann (1989 paper), p.302-3
  232. "...Rodrigues's couples are, therefore, quaternions..."
  233.  
  234.    """
  235.  
  236.     ## 26.6 microseconds
  237.     # np.cross(e, r)
  238.  
  239.     ## 2.35 microseconds
  240.     # x = e[1]*r[2] - e[2]*r[1]
  241.     # y = e[2]*r[0] - e[0]*r[2]
  242.     # z = e[0]*r[1] - e[1]*r[0]
  243.     # cross_er = array([x, y, z])
  244.  
  245.     ## 1.42 microseconds
  246.     e0, e1, e2 = e[0], e[1], e[2]
  247.     r0, r1, r2 = r[0], r[1], r[2]
  248.     cross_er[0] = e1*r2 - e2*r1
  249.     cross_er[1] = e2*r0 - e0*r2
  250.     cross_er[2] = e0*r1 - e1*r0
  251.  
  252.     ## 1.16 microseconds
  253.     # dot_er = np.dot(e, r)
  254.  
  255.     ## 829 nanoseconds
  256.     # dot_er = e[0]*r[0] + e[1]*r[1] + e[2]*r[2]
  257.  
  258.     ## 348 nanoseconds
  259.     dot_er = e0*r0 + e1*r1 + e2*r2
  260.  
  261.     cos๐šน = cos(๐šน)
  262.  
  263.     return r*cos๐šน + e*dot_er*(1.0-cos๐šน) + cross_er*sin(๐šน)
  264.  
  265.     # Altmann (1986) p.163, (same as above)
  266.     # return r*cos๐šน  + sin(๐šน)*cross_er + (1.0-cos๐šน)*dot_er*e  # (4)
  267.     # Vince (2011b) p.123 (same as above)
  268.     # (1-cos๐šน)*dot_er*e + cos๐šน*r + sin๐šน*cross_er
  269.  
  270.  
  271. def composite_rotation(e1, Alpha, e2, Beta, cross_e1e2):
  272.  
  273.     """
  274. COMPOSE A SINGLE AXIS-ANGLE ROTATION FROM TWO AXIS-ANGLE ROTATIONS
  275.  
  276. Olinde Rodrigues, 1840
  277.  
  278. Rotations, Quaternions, and Double Groups
  279. by Simon L. Altmann (1986 book), p.159
  280.  
  281. Hamilton, Rodrigues, and the Quaternion Scandal
  282. by Simon L. Altmann (1989 paper), p.302
  283.  
  284. Analytical Mechanics of Space Systems (Fourth Edition, 2018)
  285. by Schaub & Junkins, p.106 (3.85, 3.86)
  286.  
  287. Quaternions for Computer Graphics (2011) by John Vince, pp.90-91 (7.1, 7.2)
  288.  
  289.    """
  290.  
  291.     sinAlpha = sin(Alpha / 2.0)
  292.     sinBeta =  sin(Beta / 2.0)
  293.     cosAlpha = cos(Alpha / 2.0)
  294.     cosBeta =  cos(Beta / 2.0)
  295.  
  296.     e10, e11, e12 = e1[0], e1[1], e1[2]
  297.     e20, e21, e22 = e2[0], e2[1], e2[2]
  298.  
  299.     cross_e1e2[0] = e11*e22 - e12*e21
  300.     cross_e1e2[1] = e12*e20 - e10*e22
  301.     cross_e1e2[2] = e10*e21 - e11*e20
  302.  
  303.     dot_e1e2 = e10*e20 + e11*e21 + e12*e22
  304.  
  305.     sinAlpha_sinBeta = sinAlpha*sinBeta
  306.  
  307.     Gamma = 2.0 * acos(cosAlpha*cosBeta - sinAlpha_sinBeta * dot_e1e2)
  308.  
  309.     if Gamma == 0.0:
  310.         return 0.0, cross_e1e2 #array([1.0, 0.0, 0.0])
  311.  
  312.     E = (sinAlpha*cosBeta * e1 +
  313.          cosAlpha*sinBeta * e2 +
  314.          sinAlpha_sinBeta * cross_e1e2) / sin(Gamma/2.0)
  315.  
  316.     return Gamma, E
  317.  
  318.  
  319. def relative_rotation(Alpha, e1, Beta, e2):
  320.  
  321.     """
  322.  
  323.    FIND THE RELATIVE ORIENTATION FROM TWO "PRINCIPAL ROTATION ELEMENT SETS"
  324.  
  325.    Analytical Mechanics of Space Systems (Fourth Edition, 2018)
  326.    by Schaub & Junkins p.106 (3.87, 3.88)
  327.  
  328.    """
  329.  
  330.     sinAlpha = sin(Alpha / 2.0)
  331.     sinBeta =  sin(Beta / 2.0)
  332.     cosAlpha = cos(Alpha / 2.0)
  333.     cosBeta =  cos(Beta / 2.0)
  334.  
  335.     # 14 times faster than np.cross, 3 times faster than np.dot
  336.     #todo if you pass cross_e1e2 to the function, it's 17 times faster
  337.     e10, e11, e12 = e1[0], e1[1], e1[2]
  338.     e20, e21, e22 = e2[0], e2[1], e2[2]
  339.     x = e11*e22 - e12*e21
  340.     y = e12*e20 - e10*e22
  341.     z = e10*e21 - e11*e20
  342.     cross_e1e2 = np.array([x, y, z])
  343.     dot_e1e2 = e10*e20 + e11*e21 + e12*e22
  344.  
  345.     sinAlpha_sinBeta = sinAlpha*sinBeta
  346.  
  347.     Gamma = 2.0 * acos(cosAlpha*cosBeta + sinAlpha_sinBeta * dot_e1e2)
  348.  
  349.     if Gamma == 0.0:
  350.         return 0.0, np.array([1.0, 0.0, 0.0])
  351.  
  352.     E = (cosBeta*sinAlpha * e1 -
  353.            cosAlpha*sinBeta * e2 +
  354.              sinAlpha_sinBeta * cross_e1e2) / sin(Gamma/2.0)
  355.  
  356.     return Gamma, E
  357.  
  358.  
  359. def rotation_matrix_3x3(Phi, E, I3, Ex):
  360.  
  361.     '''
  362.    CONSTRUCT 3x3 ROTATION MATRIX WITH RODRIGUES EQUATION IN MATRIX FORM
  363.    (Wikipedia with a sign change)
  364.  
  365.    Phi = angle to rotate in radians
  366.    E = unit axis vector
  367.    I = np.identity(3)
  368.    Ex = np.zeros((3,3))
  369.  
  370.    # MAKE 4x4 MATRIX
  371.    C = rotation_matrix_3x3(Gamma, E, I3, Z3)
  372.    C = hstack( (C, array([[0.],[0.],[0.]])) )
  373.    C = vstack( (C, array([[0.],[0.],[0.],[1.]]).T) )
  374.  
  375.        |  0.0  -E[2]  E[1] |
  376.   Ex = |  E[2]  0.0  -E[0] |
  377.        | -E[1]  E[0]  0.0  |
  378.  
  379.    '''
  380.  
  381.     Ex[0,1] = -E[2]
  382.     Ex[0,2] =  E[1]
  383.  
  384.     Ex[1,0] =  E[2]
  385.     Ex[1,2] = -E[0]
  386.  
  387.     Ex[2,0] = -E[1]
  388.     Ex[2,1] =  E[0]
  389.  
  390.     return I3 - sin(Phi)*Ex + (1.0-cos(Phi)) * Ex@Ex
  391.  
  392.  
  393. def rotation_matrix_4x4(Phi, E, I4, Ex):
  394.  
  395.     '''
  396.    CONSTRUCT 4x4 ROTATION MATRIX WITH RODRIGUES IN MATRIX NOTATION
  397.    (Wikipedia with a sign change)
  398.  
  399.    Phi = angle to rotate in radians
  400.    E = unit axis vector
  401.    I = np.identity(4)
  402.    Ex = np.zeros((4,4))
  403.  
  404.    The cross product matrix or "tilde matrix operator" (Schaub, Junkins)
  405.  
  406.         |  0.0  -E[2]  E[1] |
  407.    Ex = |  E[2]  0.0  -E[0] |
  408.         | -E[1]  E[0]  0.0  |
  409.  
  410.    '''
  411.  
  412.     Ex[0,1] = -E[2]
  413.     Ex[0,2] =  E[1]
  414.  
  415.     Ex[1,0] =  E[2]
  416.     Ex[1,2] = -E[0]
  417.  
  418.     Ex[2,0] = -E[1]
  419.     Ex[2,1] =  E[0]
  420.  
  421.     if 1:
  422.  
  423.         return I4 - sin(Phi)*Ex + (1.0-cos(Phi)) * Ex@Ex
  424.  
  425.     else:
  426.  
  427.         # Eulers' representation (?)
  428.         E = concatenate((E, [0.0])) # because 4x4
  429.         EE_T = E*E.T # = np.outer(E,E)
  430.         return cos(Phi) * (I4 - EE_T) - sin(Phi)*Ex + EE_T
  431.  
  432.  
  433. def make_ground():
  434.  
  435.     g.hatch_ground = glGenLists(1)
  436.     glNewList(g.hatch_ground, GL_COMPILE)
  437.  
  438.     gr = 0.0
  439.     s = 200.0
  440.     xmin = -15000
  441.     xmax =  15000
  442.     zmin = -15000
  443.     zmax =  15000
  444.  
  445.     glLineWidth(2.0)
  446.  
  447.     glBegin(GL_LINES)
  448.     for x in arange(xmin, xmax, s):
  449.         for z in arange(zmin, zmax, s):
  450.             glVertex3f(x,   gr, z)
  451.             glVertex3f(x+s, gr, z)
  452.             glVertex3f(x,   gr, z)
  453.             glVertex3f(x,   gr, z+s)
  454.     glEnd()
  455.  
  456.     glEndList()
  457.  
  458.  
  459. def make_hoops1():
  460.  
  461.     g.square_hoops1 = glGenLists(1)
  462.     glNewList(g.square_hoops1, GL_COMPILE)
  463.  
  464.     x = 0.0
  465.     y = 300.0
  466.     size = 50.0
  467.     angle = 0.0
  468.     glLineWidth(3.0)
  469.  
  470.     for z in arange(-15000., 5250., 600.):
  471.         y += 100*sin(-z/500.0)
  472.         x += 170*sin(-z/500.0)
  473.         glBegin(GL_QUADS)
  474.         glVertex3f(x-size, y-size, z)
  475.         glVertex3f(x+size, y-size, z)
  476.         glVertex3f(x+size, y+size, z)
  477.         glVertex3f(x-size, y+size, z)
  478.         glEnd()
  479.  
  480.     glEndList()
  481.  
  482.  
  483. def make_hoops2():
  484.  
  485.     g.square_hoops2 = glGenLists(1)
  486.     glNewList(g.square_hoops2, GL_COMPILE)
  487.  
  488.     z = 0.0
  489.     y = 300.0
  490.     size = 50.0
  491.     angle = 0.0
  492.     glLineWidth(3.0)
  493.  
  494.     for x in arange(-15090., 5250., 600.):
  495.         y += 100*sin(-x/500.0)
  496.         z += 170*sin(-x/500.0)
  497.         glBegin(GL_QUADS)
  498.         glVertex3f(x, y-size, z-size)
  499.         glVertex3f(x, y-size, z+size)
  500.         glVertex3f(x, y+size, z+size)
  501.         glVertex3f(x, y+size, z-size)
  502.         glEnd()
  503.  
  504.     glEndList()
  505.  
  506.  
  507. def gl_init(screenwidth, screenheight, FULL_SCREEN):
  508.  
  509.     g.screen = pygame.display.set_mode((screenwidth, screenheight),
  510.                     DOUBLEBUF|OPENGL|FULL_SCREEN*FULLSCREEN)
  511.  
  512.     # THIS IS A PROBLEM ON LENOVO
  513.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  514.  
  515.     glEnable(GL_BLEND)
  516.     # glDisable(GL_BLEND)
  517.  
  518.     glEnable(GL_LINE_SMOOTH)
  519.     glEnable(GL_POLYGON_SMOOTH)
  520.  
  521.     glEnable(GL_DEPTH_TEST)
  522.     glDepthFunc(GL_LESS)
  523.     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
  524.  
  525.     glEnable(GL_FOG)
  526.     fogColor = (0.0, 0.0, 0.0, 1.0)
  527.     glFogi(GL_FOG_MODE, GL_LINEAR)
  528.     glFogfv(GL_FOG_COLOR, fogColor)
  529.     glFogf(GL_FOG_DENSITY, 0.3)
  530.     glHint(GL_FOG_HINT, GL_NICEST)
  531.     glFogf(GL_FOG_START, 1000.0)
  532.     glFogf(GL_FOG_END, 5000.0)
  533.  
  534.     glClearColor(0.0, 0.0, 0.0, 1.0)
  535.     glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
  536.  
  537.     glMatrixMode(GL_PROJECTION)
  538.     glLoadIdentity()
  539.     gluPerspective(60.0, screenwidth/screenheight, 1.0, 10000.0)
  540.  
  541.     glMatrixMode(GL_MODELVIEW)
  542.     glLoadIdentity()
  543.  
  544.  
  545. class g: ...
  546.  
  547. if __name__ == '__main__':
  548.     sys.exit(main())
  549.  
  550.  
  551.  
  552.  
Advertisement
Add Comment
Please, Sign In to add comment