Code Monkey home page Code Monkey logo

pyquaternion's Introduction

pyquaternion

GitHub release (latest SemVer) Build Status PyPI - Downloads

A fully featured, pythonic library for quaternion representation, manipulation, 3D animation and geometry.

Please visit the pyquaternion homepage for full information and the latest documentation.

Designed for Python 2.7+ and 3.0+

demo

Example: Smooth animation with interpolation between random orientations using the pyquaternion module.

Quickstart

Install from PyPI

$ pip install pyquaternion

Run the following for a basic overview. A copy of this example can be found in demo.py.

import pyquaternion


# Create a quaternion representing a rotation of +90 degrees about positive y axis.
my_quaternion = pyquaternion.Quaternion(axis=[0, 1, 0], degrees=90)

my_vector = [0, 0, 4]
my_rotated_vector = my_quaternion.rotate(my_vector)

print('\nBasic Rotation')
print('--------------')
print('My Vector: {}'.format(my_vector))
print('Performing rotation of {angle} deg about {axis}'.format(angle=my_quaternion.degrees, axis=my_quaternion.axis))
print('My Rotated Vector: {}'.format(my_rotated_vector))


# Create another quaternion representing no rotation at all
null_quaternion = pyquaternion.Quaternion(axis=[0, 1, 0], angle=0)

print('\nInterpolated Rotation')
print('---------------------')

# The following will create a sequence of 9 intermediate quaternion rotation objects
for q in pyquaternion.Quaternion.intermediates(null_quaternion, my_quaternion, 9, include_endpoints=True):
    my_interpolated_point = q.rotate(my_vector)
    print('My Interpolated Point: {point}\t(after rotation of {angle} deg about {axis})'.format(
        point=my_interpolated_point, angle=round(q.degrees, 4), axis=q.axis
    ))
    
print('Done!')

Example output:

Basic Rotation
--------------
My Vector: [0, 0, 4]
Performing rotation of 90.0 deg about [ 0.  1.  0.]
My Rotated Vector: [4.0, 0.0, 0.0]

Interpolated Rotation
---------------------
My Interpolated Point: [0.0, 0.0, 4.0]	(after rotation of 0.0 deg about [ 0.  0.  0.])
My Interpolated Point: [0.62573786016092348, 0.0, 3.9507533623805511]	(after rotation of 9.0 deg about [ 0.  1.  0.])
My Interpolated Point: [1.2360679774997898, 0.0, 3.8042260651806146]	(after rotation of 18.0 deg about [ 0.  1.  0.])
My Interpolated Point: [1.8159619989581872, 0.0, 3.5640260967534712]	(after rotation of 27.0 deg about [ 0.  1.  0.])
My Interpolated Point: [2.3511410091698921, 0.0, 3.2360679774997894]	(after rotation of 36.0 deg about [ 0.  1.  0.])
My Interpolated Point: [2.8284271247461903, 0.0, 2.8284271247461898]	(after rotation of 45.0 deg about [ 0.  1.  0.])
My Interpolated Point: [3.2360679774997894, 0.0, 2.3511410091698921]	(after rotation of 54.0 deg about [ 0.  1.  0.])
My Interpolated Point: [3.5640260967534712, 0.0, 1.8159619989581879]	(after rotation of 63.0 deg about [ 0.  1.  0.])
My Interpolated Point: [3.8042260651806146, 0.0, 1.2360679774997898]	(after rotation of 72.0 deg about [ 0.  1.  0.])
My Interpolated Point: [3.9507533623805515, 0.0, 0.62573786016092403]	(after rotation of 81.0 deg about [ 0.  1.  0.])
My Interpolated Point: [4.0, 0.0, 0.0]	(after rotation of 90.0 deg about [ 0.  1.  0.])
Done!

pyquaternion's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyquaternion's Issues

Error in the __float__ method

hey, guys in the float method we have an error:

DeprecationWarning: Quaternion.float returned non-float (type numpy.float64). The ability to return an instance of a strict subclass of float is deprecated, and may be removed in a future version of Python.
print(float(dq.real))

I can pull this to the master

Numerical error when accessing the rotation angle in quaternion

pyquaternion version: 0.9.9, python3 version: 3.8.0

I create a quaternion object explicitly by rotation parameters. (explicitly-by-rotation-parameters).
I access the rotation angle like here.

>>> import math
>>> from pyquaternion.quaternion import Quaternion 
>>> Quaternion(axis=[0.0,0.0,1.0], radians=4.0*np.pi).radians
4.440892098500626e-16

I expect that the example outcome is 0.0 (float type) but there seems to be numerical error.
Is there any idea to eliminate the numerical error?
Does rotation parameter have the range limit?

[Edited]
This can be addressed by #79

Integration not behaving as expected

Hi, I'm having unexpected behavior that could just be me misusing the library, or could be a problem with the library itself. Either way, I'd greatly appreciate if someone could help me understand what is going on, and sorry if this kind of question doesn't belong here.

I have an object with an initial orientation described by orientation (active rotation from global to body axis). I then get a body angular rate measurement (e.g. from a gyro) and want to apply this to get the new orientation. So I have this test case, in which I start with positive pitch and apply a roll on the body axis:

def test_integrate_quaternion_pitch_roll():
    # Arrange
    # Start with positive pitch
    orientation = Quaternion(axis=[0, 1, 0], degrees=80).unit
    # Roll on body axis
    angular_rates_body = np.deg2rad(np.array([30, 0, 0])).T
    delta_t = 1

    # Act
    angular_rates_global = orientation.inverse.rotate(angular_rates_body)
    orientation.integrate(angular_rates_global, delta_t)

    print(np.rad2deg(orientation.yaw_pitch_roll))

This gives me the expected yaw_pitch_roll of [ 0. 80. 30.] in degrees.

Next, I have this test case, where I start with a positive roll and apply a pitch on the body axis:

def test_integrate_quaternion_roll_pitch():
    # Arrange
    # Start with positive roll
    orientation = Quaternion(axis=[1, 0, 0], degrees=80).unit
    # Pitch on body axis
    angular_rates_body = np.deg2rad(np.array([0, 30, 0])).T
    delta_t = 1

    # Act
    angular_rates_global = orientation.inverse.rotate(angular_rates_body)
    orientation.integrate(angular_rates_global, delta_t)

    print(np.rad2deg(orientation.yaw_pitch_roll))

My expected result would be something with positive yaw, a small amount of positive pitch, and positive roll. Instead, I get [-29.62165188 4.98092532 81.3177961 ], i.e. the yaw appears to be negated.

What's going wrong?

AttributeError: 'Quaternion' object has no attribute 'rotation_matrix'

@KieranWynn Hi, thanks for share!
When I use quaternion.rotation_matrix , I got some error like AttributeError: 'Quaternion' object has no attribute 'rotation_matrix'.
The codes I use pyquaternion are:
quaternion = Quaternion(w=orientation.w, x=orientation.x, y=orientation.y, z=orientation.z) R = quaternion.rotation_matrix # 3x3 rotation matrix T = quaternion.transformation_matrix # 4x4 transformation matrix

Can you help me figure out what's wrong when I used these codes?
Thanks!

Init with tolerance test fails

test_init_from_explicit_matrix_with_optional_tolerance_arguments fails complaining that the matrix is not orthogonal within tolerance 1e-07. When I compute the dot of the matrix and its transpose using numpy I get:

[[ 1.00585937,  0.00373457,  0.00419048, -0.07654548],
[ 0.00373457,  1.00238021,  0.00267095, -0.04878795],
[ 0.00419048,  0.00267095,  1.00299717, -0.05474513],
[-0.07654548, -0.04878795, -0.05474513,  1.        ]]

suggesting that it is indeed very far out of tolerance.

Quaternion exponents appear to be broken with python 3.6.2 and numpy 1.12.1

It appears that quaternion exponents are broken for me for some reason:

Python 3.6.2 (default, Jul 19 2017, 13:09:21) 
[GCC 7.1.1 20170622 (Red Hat 7.1.1-3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyquaternion
>>> q = pyquaternion.Quaternion(axis=[1.0, 0, 0], angle=0.0)
>>> q ** 0.5
/home/e/Develop/simplehorror/pyquaternion/quaternion.py:420: RuntimeWarning: invalid value encountered in true_divide
  n = self.vector / np.linalg.norm(self.vector)
Quaternion(nan, nan, nan, nan)
>>> 

Quaternion.distance seems to produce incorrect result

I'm evaluating the angular distance between two quaternions and it seems that values produced by Quaternion.distance are twice less in comparison with this formula:

The UTs checking angular distance for original and modified distance functions (test_angular_distance.py):

import pyquaternion as quat
import unittest
import math
import numpy as np


def angle_distance_orig(q0, q1):
    return quat.Quaternion.distance(q0, q1) / math.pi * 180


def angle_distance(q0, q1):
    q0 = q0.elements
    q1 = q1.elements
    dotProd = q0[0]*q1[0] + q0[1]*q1[1] + q0[2]*q1[2] + q0[3]*q1[3]
    return 2.0 * math.acos(max(0.0, min(abs(dotProd), 1.0))) / math.pi * 180.0


if __name__ == '__main__':

    orig_quat = quat.Quaternion(axis=[1, 0, 0], degrees=0)
    for angle in range(0, 361, 15):
        for ax in range(0, 3):
            new_quat = quat.Quaternion(axis=np.roll(
                [1, 0, 0], ax), degrees=angle)

            a0 = angle_distance_orig(orig_quat, new_quat) # 0 - 360
            a1 = angle_distance(orig_quat, new_quat) # 0 - 180
            print(f"Angle: {min(angle, 360 - angle):5}, axis: {ax}, original={a0:5.1f}, modified={a1:5.1f}")

The output:

$ python test_angular_distance.py

Angle:     0, axis: 0, original=  0.0, modified=  0.0
Angle:     0, axis: 1, original=  0.0, modified=  0.0
Angle:     0, axis: 2, original=  0.0, modified=  0.0
Angle:    15, axis: 0, original=  7.5, modified= 15.0
Angle:    15, axis: 1, original=  7.5, modified= 15.0
Angle:    15, axis: 2, original=  7.5, modified= 15.0
Angle:    30, axis: 0, original= 15.0, modified= 30.0
Angle:    30, axis: 1, original= 15.0, modified= 30.0
Angle:    30, axis: 2, original= 15.0, modified= 30.0
Angle:    45, axis: 0, original= 22.5, modified= 45.0
Angle:    45, axis: 1, original= 22.5, modified= 45.0
Angle:    45, axis: 2, original= 22.5, modified= 45.0
Angle:    60, axis: 0, original= 30.0, modified= 60.0
Angle:    60, axis: 1, original= 30.0, modified= 60.0
Angle:    60, axis: 2, original= 30.0, modified= 60.0
Angle:    75, axis: 0, original= 37.5, modified= 75.0
Angle:    75, axis: 1, original= 37.5, modified= 75.0
Angle:    75, axis: 2, original= 37.5, modified= 75.0
Angle:    90, axis: 0, original= 45.0, modified= 90.0
Angle:    90, axis: 1, original= 45.0, modified= 90.0
Angle:    90, axis: 2, original= 45.0, modified= 90.0
Angle:   105, axis: 0, original= 52.5, modified=105.0
Angle:   105, axis: 1, original= 52.5, modified=105.0
Angle:   105, axis: 2, original= 52.5, modified=105.0
Angle:   120, axis: 0, original= 60.0, modified=120.0
Angle:   120, axis: 1, original= 60.0, modified=120.0
Angle:   120, axis: 2, original= 60.0, modified=120.0
Angle:   135, axis: 0, original= 67.5, modified=135.0
Angle:   135, axis: 1, original= 67.5, modified=135.0
Angle:   135, axis: 2, original= 67.5, modified=135.0
Angle:   150, axis: 0, original= 75.0, modified=150.0
Angle:   150, axis: 1, original= 75.0, modified=150.0
Angle:   150, axis: 2, original= 75.0, modified=150.0
Angle:   165, axis: 0, original= 82.5, modified=165.0
Angle:   165, axis: 1, original= 82.5, modified=165.0
Angle:   165, axis: 2, original= 82.5, modified=165.0
Angle:   180, axis: 0, original= 90.0, modified=180.0
Angle:   180, axis: 1, original= 90.0, modified=180.0
Angle:   180, axis: 2, original= 90.0, modified=180.0
Angle:   165, axis: 0, original= 97.5, modified=165.0
Angle:   165, axis: 1, original= 97.5, modified=165.0
Angle:   165, axis: 2, original= 97.5, modified=165.0
Angle:   150, axis: 0, original=105.0, modified=150.0
Angle:   150, axis: 1, original=105.0, modified=150.0
Angle:   150, axis: 2, original=105.0, modified=150.0
Angle:   135, axis: 0, original=112.5, modified=135.0
Angle:   135, axis: 1, original=112.5, modified=135.0
Angle:   135, axis: 2, original=112.5, modified=135.0
Angle:   120, axis: 0, original=120.0, modified=120.0
Angle:   120, axis: 1, original=120.0, modified=120.0
Angle:   120, axis: 2, original=120.0, modified=120.0
Angle:   105, axis: 0, original=127.5, modified=105.0
Angle:   105, axis: 1, original=127.5, modified=105.0
Angle:   105, axis: 2, original=127.5, modified=105.0
Angle:    90, axis: 0, original=135.0, modified= 90.0
Angle:    90, axis: 1, original=135.0, modified= 90.0
Angle:    90, axis: 2, original=135.0, modified= 90.0
Angle:    75, axis: 0, original=142.5, modified= 75.0
Angle:    75, axis: 1, original=142.5, modified= 75.0
Angle:    75, axis: 2, original=142.5, modified= 75.0
Angle:    60, axis: 0, original=150.0, modified= 60.0
Angle:    60, axis: 1, original=150.0, modified= 60.0
Angle:    60, axis: 2, original=150.0, modified= 60.0
Angle:    45, axis: 0, original=157.5, modified= 45.0
Angle:    45, axis: 1, original=157.5, modified= 45.0
Angle:    45, axis: 2, original=157.5, modified= 45.0
Angle:    30, axis: 0, original=165.0, modified= 30.0
Angle:    30, axis: 1, original=165.0, modified= 30.0
Angle:    30, axis: 2, original=165.0, modified= 30.0
Angle:    15, axis: 0, original=172.5, modified= 15.0
Angle:    15, axis: 1, original=172.5, modified= 15.0
Angle:    15, axis: 2, original=172.5, modified= 15.0
Angle:     0, axis: 0, original=180.0, modified=  0.0
Angle:     0, axis: 1, original=180.0, modified=  0.0
Angle:     0, axis: 2, original=180.0, modified=  0.0

Is it a bug in function or I'm confusing the usage of the function?

What to do when matrix is not quite within orthogonality tolerance

Hi,

Thanks for writing this library. I use it all the time and love its simplicity for 3D representations. I have lately been dealing with an issue pretty commonly where I am getting a transformation matrix from somewhere and it's very close to being orthogonal, but not quite within the tolerance. These transformations should be valid, but I'm guessing there's some lost precision when using floats vs double. Here is an example:

import numpy as np
from pyquaternion import Quaternion

a = np.array([[-7.0710677e-01, -7.0710677e-01,  0.0000000e+00], 
    [-7.0710677e-01,  7.0710677e-01, -9.7931776e-12], 
    [ 6.9248223e-12, -6.9248223e-12, -1.0000000e+00], 
    [ 0.0000000e+00,  0.0000000e+00,  0.0000000e+00]], 
    dtype=np.float32) 
    
 print(np.dot(a, a.conj().transpose()))

Gives

array([[ 9.99999940e-01, -1.26880515e-08, -8.15222941e-20],
      [-1.26880515e-08,  9.99999940e-01,  0.00000000e+00],
      [-8.15222941e-20,  0.00000000e+00,  1.00000000e+00]], dtype=float32)

This looks really close to the identity, but not quite. Any tips on how to project this back onto SO3 so I can produce the corresponding quaternion?

Thanks!

Demo2 can't be runned

Traceback (most recent call last):
File "C:\Program Files\Python36\lib\site-packages\matplotlib\projections_init_.py", line 63, in get_projection_class
return projection_registry.get_projection_class(projection)
File "C:\Program Files\Python36\lib\site-packages\matplotlib\projections_init_.py", line 29, in get_projection_class
return self._all_projection_types[name]
KeyError: '3d'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "D:\工作空间\L\Python\pyquaternion\2.py", line 51, in
ax = fig.add_axes([0, 0, 1, 1], projection='3d')
File "C:\Program Files\Python36\lib\site-packages\matplotlib\figure.py", line 1126, in add_axes
self, *args, **kwargs)
File "C:\Program Files\Python36\lib\site-packages\matplotlib\projections_init_.py", line 91, in process_projection_requirements
projection_class = get_projection_class(projection)
File "C:\Program Files\Python36\lib\site-packages\matplotlib\projections_init_.py", line 65, in get_projection_class
raise ValueError("Unknown projection '%s'" % projection)
ValueError: Unknown projection '3d'

change of basis two 3D coordinate systems Quaternion

Hello

I have a point cloud in the coordinate system of a LIDAR sensor and a frenet-serret coordinate system defined by three orthogonal uniot vectors in the cartesian system. I want transpose the pointcloud's points into the frenet-serret coordinates.

Can you please give me a hint how I can do it with pyquaternion ?

Thanks

Reference frame not documented

It would be very helpful if the reference frame for your Quaternions was documented. I assumed that it would be the "traditional" right-handed right-forward-up / east-north-up. But, when I looked at the Basic Usage example with v = [0, 0, 1], q1 = Quaternion(axis=[1, 0, 0], angle=3.14159265), q2 = Quaternion(axis=[0, 1, 0], angle=3.14159265 / 2) I expected to get negative x-axis [-1, 0, 0] for the result of (q1*q2).rotate(v) but the documentation/output is [1, 0, 0].

Perhaps I am making a mistake about quaternion composition and handedness. In any case, it would be helpful to document the reference frame.

Thank you for making this!

Installation Problem

Hi,

I've been trying to install the package through pip install pyquaternion, but got the following problem:

Collecting pyquaternion
  Downloading pyquaternion-0.9.0.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
    ImportError: No module named setuptools
    
    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-6K5E4d/pyquaternion/

I have the last version of setuptools (upgraded it by pip install -U setuptools)

Any ideas?

1.0 Goals

Planned objectives for 1.0 release:

  1. Remove implicit normalisation (#47)
  2. Find a better way to convert to-and-from Euler Angles. There are several conventions here that need to be supported. (#38) - alternatively, remove them entirely and avoid the confusion and support. #52 (comment)
  3. Deprecate Python 2.7
  4. Typing throughout
  5. Improvements to testing (use pytest?)

Rotation matrix convention (angle sequence order)

Hello @KieranWynn,

What is the rotation matrix convention used in q.rotation_matrix ? There are many different conventions so it's helpful to know which one is used here.

For example, is the sequence x-y-z (rotation matrix multiplication order is Rz*Ry*Rx) ? this would mean applying a rotation in x then y then z.

I'd like to get the 3 Euler angles out of this matrix, so it matters to know the convention used.

Migrate CI to travis-ci.com and integrate with GitHub Apps

At the time of writing, Travis is in a transitional period where they are encouraging users to transition to travis-ci.com along with a switch to the GitHub Apps API. Public Open Source repos are not eligible for the transition yet, outside of an opt-in beta.

Once the transition is publically available, migrate and set up GitHub Apps for Travis, allowing easier integration for pages and releases.

rotation matrix convertion to quaternion

you code validate rotation matrix must be orthogonal and its determinant must be precisely 1., which is not the case for many real datset like 7scenes.

I wonder if it's possible for you to ease the limitation for this? We can set a relative big number for np.allclose and np.isclose to tolerate many datasets.

Thank you

Add averaging function for quaternions

When working with noisy orientation data averaging is often needed to combine multiple orientations. Quaternions are difficult to use for this, since there is no trivial average. However, I found a paper that explains how to do an average and a weighted average:

Markley, F. Landis, Yang Cheng, John Lucas Crassidis, and Yaakov Oshman.
"Averaging quaternions." Journal of Guidance, Control, and Dynamics 30, no. 4 (2007): 1193-1197.
Link: https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20070017872.pdf

There is also a Matlab implementation:
Tolga Birdal. "averaging_quaternions" Matlab code.
http://jp.mathworks.com/matlabcentral/fileexchange/40098-tolgabirdal-averaging-quaternions

I wrote two python functions that use numpy:
https://github.com/christophhagen/averaging-quaternions

It would be nice to have this feature integrated in this library.

We have Python type stub file (*.pyi) for Pyquaternion

Hi,
To be able to run type checks on our code using Pyquaternion, we have created a pyi file for pyquaternion. This allows mypy to check if we are using it correctly (and makes IDEs like PyCharm autocomplete methods for Quaternion objects). Do you want to include it in your package?

The file is attached, so that you can have a look:
quaternion.zip

Bug in Quaternion Interpolation

Hi,

I have observed a bug in Quaternion.Intermediates function. I originally had the setting to have 4 interpolated values, when I observed the following:
screen shot 2018-09-12 at 2 47 34 pm

In the animation based of the demo, I also observed jerky movement in the figure, whereas the recording of the object that I had collected the quaternion measurements from, had no such movement.

Even with 1 interpolated value, this extremity is calculated:
screen shot 2018-09-12 at 2 49 49 pm
It seems very unreasonable for the w quaternion value to go from 0.002 to 0.839 and then back to 0.006.

Without any interpolation, this are the values in my data, which have no unexplainable change in the quaternion numbers, although the animation is jerky without the interpolation:
screen shot 2018-09-12 at 2 48 23 pm

current demo2.py AttributeError: 'list' object has no attribute 'shape'

Good day and thanks for your work.
I'm trying to run the demo2.py to get the rotating 3d image, and the image window does appear
with the axes, but no drawing of the rotating figure.
I'm getting a traceback:

Exception in Tkinter callback
Traceback (most recent call last):
File "c:\bin\python38\lib\tkinter_init_.py", line 1883, in call
return self.func(*args)
File "c:\bin\python38\lib\tkinter_init_.py", line 804, in callit
func(*args)
File "c:\bin\python38\lib\site-packages\matplotlib\backends_backend_tk.py", line 253, in idle_draw
self.draw()
File "c:\bin\python38\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 9, in draw
super(FigureCanvasTkAgg, self).draw()
File "c:\bin\python38\lib\site-packages\matplotlib\backends\backend_agg.py", line 407, in draw
self.figure.draw(self.renderer)
File "c:\bin\python38\lib\site-packages\matplotlib\artist.py", line 41, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "c:\bin\python38\lib\site-packages\matplotlib\figure.py", line 1870, in draw
self.canvas.draw_event(renderer)
File "c:\bin\python38\lib\site-packages\matplotlib\backend_bases.py", line 1759, in draw_event
self.callbacks.process(s, event)
File "c:\bin\python38\lib\site-packages\matplotlib\cbook_init_.py", line 229, in process
self.exception_handler(exc)
File "c:\bin\python38\lib\site-packages\matplotlib\cbook_init_.py", line 81, in exception_printer
raise exc
File "c:\bin\python38\lib\site-packages\matplotlib\cbook_init
.py", line 224, in process
func(*args, **kwargs)
File "c:\bin\python38\lib\site-packages\matplotlib\animation.py", line 975, in _start
self._init_draw()
File "c:\bin\python38\lib\site-packages\matplotlib\animation.py", line 1722, in _init_draw
self._drawn_artists = self._init_func()
File "demo2-new.py", line 89, in init
line.set_3d_properties([])
File "c:\bin\python38\lib\site-packages\mpl_toolkits\mplot3d\art3d.py", line 143, in set_3d_properties
zs = np.broadcast_to(zs, xs.shape)
AttributeError: 'list' object has no attribute 'shape'

Windows 10

Python: 3.8.6
matplotlib: 3.3.4
numpy: 1.20.1
pyquaternion: 0.9.9

I found one post on stackoverflow that says to use numpy.array, but you're doing that already.
Thank you again.......
Tom C.

"join forces" with scikit-kinematics?

Hi Kieran,
this is not really an "issue". But I did not know how else to contact you.
I have a similar toolbox, on "scikit-kinematics"
docs
github
Should we "join forces"? I think we have the same idea, and we should make it as simple as possible for the user.
Best regards down under
thomas

Unexpected normalization in rotation functions

There is an undocumented implicit normalization in the functions Quaternion.rotate() and Quaternion.rotation_matrix(), which should be either documented or removed. I would lean toward the latter. If a user tries to rotate with a quaternion that is not unit, then the expected behaviour would be to either use that quaternion's normalized form without changing the actual object, or to raise an error.

>>> q = Quaternion([1, 0, 1, 0])
>>> q.magnitude
1.4142135623730951
>>> q.rotate([1, 0, 0])
[0.0, 0.0, -0.9999999999999998]
>>> q.magnitude # expected: 1.4142135623730951
0.9999999999999999

As an example of a situation where it would make sense to remove the implicit normalization, I'm making a simulation that uses Quaternions to represent orientation. My Quaternion magnitude naturally drifts over time due to the imperfection of ODE solvers, so I tried to add a normalizing restoring force. However, I found that my Quaternion (whose rotation matrix I took a few lines before) was already normalized, even though the state vector I had derived it from clearly was not.

Bug in Slerp Interpolation

Hi,
I have observed a bug in the slerp interpolation offered by pyquaternion. Following is my test case to show case the same. I compared it against Matlab's implementation, the answer from matlab appears to be correct.

Python

image

Matlab
image

The answers from Matlab and pyquaternion do not match.

Misleading docstring for yaw_pitch_roll

The docstring for yaw_pitch_rolls states as follows:
"""Get the equivalent yaw-pitch-roll angles aka. intrinsic Tait-Bryan angles following the z-y'-x'' convention

    Returns:
        yaw:    rotation angle around the z-axis in radians, in the range `[-pi, pi]`
        pitch:  rotation angle around the y'-axis in radians, in the range `[-pi/2, -pi/2]`
        roll:   rotation angle around the x''-axis in radians, in the range `[-pi, pi]`

    The resulting rotation_matrix would be R = R_x(roll) R_y(pitch) R_z(yaw)

"""
From the resulting rotation matrix formula we could see this post-multiplication implies this is extrinsic angles. I examined the results with https://github.com/ros/geometry/blob/hydro-devel/tf/src/tf/transformations.py#L1100 as well, and indeed the yaw_pitch_roll is giving values as 'szyx', which is static frame rotation, not intrinsic Tait-Bryan angles. This function should return the same result as "rzyx".

the exponential map in the quaternion

Hi,
To my understanding, the exponential map for the quaternion takes an element of the tangent space (i.e. a rotation vector omega of size 3) and then returns the corresponding elements of the SO3 group. (Equivalent to expmat(hat(omega))). However, in PyQuaternion, we pass a quaternion object.

For example, see https://github.com/strasdat/Sophus/blob/13fb3288311485dc94e3226b69c9b59cd06ff94e/sophus/so3.hpp#L528

However I don't understand how this is implemented in PyQuaternion.
Thanks
Adel

Support for rotating a batch of vectors

First of all, thanks for the great library!

I am wondering if the support for batch operations is in plans.
My user case is that I need to rotate 1000 vectors using a single rotation quaternion.

deepcopy error

Thanks for this library! Calling deepcopy gives me an error:

from pyquaternion import Quaternion
from copy import deepcopy
a = Quaternion()
b = deepcopy(a)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-71-679cde0c5577> in <module>()
----> 1 b = deepcopy(a)

/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/copy.py in deepcopy(x, memo, _nil)
    159             copier = getattr(x, "__deepcopy__", None)
    160             if copier:
--> 161                 y = copier(memo)
    162             else:
    163                 reductor = dispatch_table.get(cls)

TypeError: __deepcopy__() takes 1 positional argument but 2 were given

Besides commenting out your definition, changing it to

    def __deepcopy__(self, memo):
        cls = self.__class__
        result = cls.__new__(cls)
        memo[id(self)] = result
        for k, v in self.__dict__.items():
            setattr(result, k, deepcopy(v, memo))
        return result

seems to fix the problem for me. But I haven't tested it much yet.

yaw_pithc_roll

Hello Kieran,

I am trying to obtain the euler angles (yaw, pitch, roll sequence) from yaw_pitch_roll but when introducing a Quaternion class type it does not work. It returns me a 'tuple' object is not callable error.

I am not sure what is going on, or if it is possible to perform that conversion another way.

Sorry for the inconveniences.

Kinds regards,
Jose Herrera

Permission to submit PEP484 stubs to Typeshed

Hi,

I am requesting permission from the authors of this project to write and submit PEP 484 type stubs for this project to typeshed.

This will allow users of static type checkers such as mypy and pycheck to typecheck their use of pyquaternion. As this module supports versions of Python < 3.5, it is unfortunately not possible to include them as inline stubs.

The maintainers of typeshed require that I obtain consent from the maintainers of this project before submitting to them:

NOTE: When you're contributing a new stub for a package that you did not develop, please obtain consent of the package owner (this is specified in PEP 484). The best way to obtain consent is to file an issue in the third-party package's tracker and include the link to a positive response in your PR for typeshed.

Thanks,

Dan

Not hashable

The following fixes the issue of the Quaternion not being hashable:

def __hash__(self):
     """Make the quaternion hashable

     Returns:
        A hash that tries to be unique. 
     """
     return hash(tuple(self.q))

Apply a rotation to an entire matrix of points

image

from pyquaternion import Quaternion
import numpy as np
import matplotlib.pyplot as plt

rc = 4
angles = np.linspace(0, 2*np.pi, 200)
xc = rc*np.cos(angles)
yc = rc*np.sin(angles)
zc = [0]*200
circle = np.array([xc, yc, zc]).T

my_quaternion = Quaternion(axis=[1, 0, 0], degrees=90)

ncircle = []
for ix in circle:
    v = ix.copy()
    vp = my_quaternion.rotate(v)
    ncircle.append(vp)
ncircle = np.array(ncircle)

fig = plt.figure()
ax = fig.add_subplot(projection='3d')
origin = np.array([0, 0, 0])
ax.plot(circle[:, 0],
        circle[:, 1],
        circle[:, 2])

ax.plot(ncircle[:, 0],
        ncircle[:, 1],
        ncircle[:, 2])
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')

Instead of using the above for-loop, is there anyway to apply the 90-degree rotation as a matrix operation on the first (blue) circle?

Enhancement for converting to yaw-pitch-roll

Hi,
I saw that this library offers the function yaw_pitch_roll to get the Euler angles from a quaternion. However I see they are calculated based on a fixed application of the rotations (I guess XYZ). When using this library, I had the necessity to calculate these angles with respect to a different rotation order (YXZ). Looking deeply on the internet, I found this wonderful paper where a guy explains how to convert from a quaternion to Euler angles for EVERY possible rotation order.
https://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/Quaternions.pdf

To make it short, a quaternion Q is represented by four numbers p0, p1, p2, p3 where p0 is always Q.w while p1, p2, p3 are the components of the quaternion with respect of rotation order (e.g. if the rotation order is XYZ then p1 = Q.x, p2 = Q.y, p3 = Q.z, if the rotation order is ZYX then p1 = Q.z, p2 = Q.y, p3 = Q.x...) plus a variable "e" which equals 1 for right handed systems and -1 for left-handed systems. The three angles alpha, beta, gamma are then calculated as:

  • alpha = asin(2*(p0*p2 + e*p1*p3))
  • beta = atan2(2*(p0*p1 - e*p2*p3), 1-2*(p1**2 + p2**2))
  • gamma = atan2(2*(p0*p3 - e*p1*p2), 1-2*(p2**2 + p3**2))

and it magically works.

This could be implemented in the library by adding a parameter to yaw_pitch_roll expressing the rotation order (it could be optional for backward compatibility).

Quaternion rotation of vector modifies object instance

I'm using pyquaternion to generate unit tests for some C code which performs various quaternion and vector operations such as the quaternion Hamilton product, rotation a vector with a quaternion, etc.

I noticed that the results from my C code was not matching the pyquaternion result, and then I noticed that when using basic numpy arrays I get matching answers, but not when using the Quaternion class.

Digging further into it, I found that it was related to the fact that I was calling q.rotate(v) to rotate a vector with a quaternion, and that the rotate method is normalizing q and that modifies the object thereafter, so methods later in my code that used the same quaternion were working with a different quaternion.

This short snippet shows the behaviour:

from pyquaternion.quaternion import Quaternion
import numpy as np

a = Quaternion(0.0875, -0.8330, 0.2902, -0.4629)
b = Quaternion(-0.4156, 0.8189, -0.3817, 0.1043)
v = [1.0000, 1.0000, 1.0000]

print([np.float32(i) for i in a * b])
a.rotate(v)
print([np.float32(i) for i in a * b])

Prints

[0.8048285, 0.27142748, -0.44619277, 0.2818188]
[0.8048133, 0.27142236, -0.44618437, 0.2818135]

The first answer (before the quaternion was normalized) matches my C code and also WolframAlpha.

While I think the case could be made either way, I think the least surprising thing for the user would be to create a temporary quaternion object from the parameter, normalize that, and return the result of the rotation. This way the input quaternion remains the same and the result is unchanged.

PyPI source distribution lacks VERSION.txt

This is needed to install the package from source, as it's required with grayskull-generated conda packages. For now I am using the GitHub release, which is ok, but I thought you'd like to know!

type object 'Quaternion' has no attribute 'distance'

I am implementing a project that requires me to use the distance and slerp functions on a set of quaternion data which I have in a file.

Whenever I call the method Quaternion.distance(q0, q1), I get the following error:
"AttributeError: type object 'Quaternion' has no attribute 'distance'"
The same happens for the remaining distance methods (absolute_distance, sym_distance) and the log methods.

How can I fix this?

Matrix to quaternion

I found a possible conversion bug that caused by this line.

Should the w(real) be M[0, 2]-M[2, 0]?

I use this website for verification.

[ -0.4285714,  0.2857143,  0.8571429;
   0.8571429,  0.4285714,  0.2857143;
  -0.2857143,  0.8571429, -0.4285714 ]

w[ 0.3780]
x [ 0.3780]
y [ 0.7559]
z [ 0.3780]

Matlab rotm2quat gave the same answer. (positive w)

If I use M[2, 0]-M[0, 2], the w(real) term will be negative.

Error in Basic Usage Documents

There appears to be an error in the section about quaternion chaining in Basic Usage.

Either pyQuaternion does not follow standard quaternion multiplication conventions (I don't think so) or the example is incorrect, more likely and what a brief test implies.

>>> q3 = q1 * q2 # Composite rotation of q1 then q2 expressed as standard multiplication

This should instead be:
q3 = q2 * q1 ...

Note the swapped order of q2 and q1.

I initially thought this meant that pyquaternion does not follow the standard convention but testing suggests that it is correct but this documentation is quite misleading.

The library is otherwise awesome so thank you!

Easy access to quaternion elements

Hey. Thanks for the fantastic work on this library.

I was wondering if it would be good to add properties to access the quaternion elements directly, i.e. instead of doing q.real and q.vector[0], q.vector[1] & q.vector[0], a user could simply do q.w, q.x, q.y & q.z?
This would be 100% backwards compatible since we are only adding new instance properties, but these should make testing and debugging much easier and more readable.

I'd be happy to submit a PR for this.

How to initialize Quaternion from yaw, pitch, roll?

Hi,

Suppose that I get yaw, pitch, roll from method 'yaw_pitch_roll' then I change some values of them. How can I get/initialize a Quaternion object from my updated yaw, pitch, roll?

Please help. Thank you so much.

Quaternion constructor is broken (at least for some values of axis=)

In the latest revision b30857a which fixes real number exponents, the constructor for Quaternions seems to be broken when constructing them from an axis + angle:

e@e:~/Develop/e$ python3
Python 3.6.2 (default, Jul 19 2017, 13:09:21) 
[GCC 7.1.1 20170622 (Red Hat 7.1.1-3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pyquaternion import Quaternion
>>> q = Quaternion(axis=[0.0, 1.0, 0.0], angle=0.0)
>>> q.axis
array([ 0.,  0.,  0.])
>>>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.