In addition to singularity at R=I there exist also singularity at phi = pi:
~~~~
from pytorch3d.transforms.so3 import so3_log_map
import torch
R = torch.tensor([[-0.99970424, 0.000973952, 0.024300903],
[ 0.000737710, -0.99752367, 0.070327967],
[ 0.024309222, 0.070325091, 0.99722791]]).unsqueeze(0)
ax_angle = so3_log_map(R)
print(ax_angle)
~~~~
The computation near phi = pi blows up the output.
=> ax_angle = [ 51.6741, 149.4666, 4244.7529]
Unfortunately, I do not believe that the instability you mention can be avoided. The computation of the log of a near-identity matrix involves division by very tiny numbers. You can improve the robustness by tweaking the eps parameter of so3_log_map.
Tweaking eps doesn't help since phi is only checked that it is not near zero. In this case phi=piN, where N is integer. So we have singularities when rotation angle is pi, 2pi, 3*pi, .... If no one's going to fix this at least it should be very clearly documented that there is singularity around phi=pi. I spent quite a while finding bug in my code because of this. Basically this makes the function useless in many use cases.
Ok, you are right. I have implemented a fix that allows for log_rots containing multiples of PI. We should push the fix in the course of today.
The fix looks as follows:
phi_sin = phi.sin()
phi_denom = (
torch.clamp(phi_sin.abs(), eps) * phi_sin.sign()
+ (phi_sin == 0).type_as(phi) * eps
)
log_rot_hat = (phi / (2.0 * phi_denom))[:, None, None] * (R - R.permute(0, 2, 1))
It passes all numeric tests that convert log_rots with multiples of Pi in them.
Thank you very much. I really appreciate this and I think other users will also
Hi, we pushed a fix:
https://github.com/facebookresearch/pytorch3d/commit/34a0df0630c964d4e4be225b1dc0ccf166743e75
Please let us know whether this resolved your issue.
Hey
I tried the fix but unfortunately it still seems to do strange things around pi:
~~~~
R1 = torch.tensor([[-0.99007475, -0.02610438, 0.13809596],
[ 0.0309009, -0.9989872, 0.0327038 ],
[ 0.13710241, 0.0366465, 0.9898788 ]]).unsqueeze(0)
R2 = torch.tensor([[-0.9902569, 0.00336199, 0.13921246],
[ 0.00223879, -0.999195, 0.04005579],
[ 0.13923505, 0.03997718, 0.98945206]]).unsqueeze(0)
so3_log_map(R1, eps=0.0001) => tensor([[0.2147, 0.0541, 3.1049]])
so3_log_map(R2, eps=0.0001) => tensor([[-0.2529, -0.0727, -3.6134]])
matrix_to_euler_angles(R1, convention='XYZ') => tensor([[-0.0330, 0.1385, 3.1152]])
matrix_to_euler_angles(R2, convention='XYZ') => tensor([[-0.0405, 0.1397, -3.1382]])
~~~~
When comparing outputs between so3_log_map() and matrix_to_euler_angles() there is quite large jump in the outputs of so3_log_map(). Running the code I'm currently developing also confirms this behaviour in so3_log_map().
I tried with different epsilons but it didn't help.
Hi, thanks a lot for the feedback. This really helps us to make the library more useful.
Indeed, the instability around multiples of PI is hard to avoid. E.g. the lower part of the "Log map from SO(3) to so(3)" section in https://en.wikipedia.org/wiki/Axis%E2%80%93angle_representation describes the issues you are currently running into.
There might a solution to do the logmap using quaternions, which do not have this kind of instability. I will investigate more and keep you posted.