Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Eigen in DynamicalSystems #70

Merged
merged 10 commits into from
Jul 14, 2020
Merged

Conversation

GiulioRomualdi
Copy link
Member

This goes in the direction of #56

Unfortunately in FloatingBaseSystemDynamics the conversion of the Eigen vectors/matrix in iDynTree objects is done manually. I think that it can be solved with robotology/idyntree#716

A test for the FloatingBaseSystemDynamics class is still missing.

- Store the current state of the system inside the DynamicalSystem class
- dynamics() function does not require the current state as input anymore
- setState() and getState() functions have been implemented
- m_stateAtNextTimeInstant is not required anymore
- oneStepIntegration() function now wants as input only the current time and the samping time of the integrator
- oneStepIntegration() function is now compliant with e612ed8
- From now one we assume that the object contained in DynamicalSystem::StateType and DynamicalSystem::StateDerivativeType have the operator + defined
…st modification of DynamicalSystem and Integrator
… modification of DynamicalSystem and Integrator
Comment on lines 85 to 90
// project the base orientation matrix in SO3
// here we assume that the velocity is expressed using the mixed representation
const Eigen::Matrix3d& baseOrientation = std::get<3>(state);
Eigen::JacobiSVD<Eigen::Matrix3d> svd(baseOrientation,
Eigen::ComputeFullU | Eigen::ComputeFullV);
std::get<3>(m_state) = svd.matrixU() * svd.matrixV().transpose();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be slow, just to understand did you check if that is not a bit bottleneck?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well @traversaro you are actually right. Half of the time is spent in the setState() function and the bottleneck is the svd calculation.

calgrind

Do you think that a Baumgarte stabilization may solve the problem?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what the best option is at the moment, but if this PR is just about using Eigen perhaps we can leave this normalization stuff in a following PR? Or just leave the JacobiSVD for now.

BTW I just found https://github.com/ericjang/svd3, probably not the best option for maintainability but quite a neat idea.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to perform the integration on SO(3)? Why not passing through quaternions?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sure quaternion are much easier to normalize.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually using quaternion is not so trivial.

Indeed If I use Eigen::Quaterniond I can't do this:

Eigen::Quaterniond a;
a.setIdentity();
a = a + Eigen::Vector4d::Zero();

I get the following

 error: no match for ‘operator+’ (operand types are ‘Eigen::Quaterniond {aka Eigen::Quaternion<double>}’ and ‘const ConstantReturnType {aka const Eigen::CwiseNullaryOp<Eigen::internal::scalar_constant_op<double>, Eigen::Matrix<double, 4, 1> >}’

Unfortunately, this kind of code is required in addArea(). This function is template and I would avoid adding specialization there.
For the time being, I would go for the Rotation matrix + Baumgard integration. In the future, If we want to use quaternions instead of matrices we can open a new issue and discuss it there. What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a little lost here. The state derivative and the state are generally supposed to be different right? In this case the state is a quaternion and the derivative a Vector4 and it makes sense for them not to be added together. Before you were dealing with the rotation matrix in a specific case. How would it work now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me explain better. The dynamical system class contains the state, its derivative, and the input. In case of a rotation we have:

  • Eigen::Matrix3d for the state (i.e. the rotation)
  • Eigen::Matrix3d for the state derivative (i.e. the rotation rate of change)
  • Eigen::Vector3d for the input (i.e. angular velocity)

The rate of change the rotation matrix is then computed as

Ṙ = ωx R

Ṙ is then integrated to compute R. The `ForwardEuler` integrator uses the function `addArea()` to compute the integral

https://github.com/dic-iit/bipedal-locomotion-framework/blob/339cf76339622cf496d941258374614f43d526af/src/System/include/BipedalLocomotion/System/ForwardEuler.h#L48

This code can compile only if the operator+() is defined between the state and the state derivative. In this case, since the rate of change of the rotation matrix and the rotation matrix are stored in an Eigen::Matrix3d, the operator+() is well defined.

In the case of quaternions we can decide to:

  1. Store the quaternion in an Eigen::Vector4d and apply a similar approach. In this case, I won't have any problems;
  2. Store the quaternion in an Eigen::Quaterniond and its rate of change in Eigen::Vector4d. In this case std::get<I>(x) += std::get<I>(dx) * dT will fail because the operator+() is not defined between Eigen::quaterniond and Eigen::Vector4d.

By the way, in 9870a4f I implemented the Baumgarte stabilization for rotation matrix. Differently from the original paper, I derived the equation in case of angular velocity expressed in the inertial frame. Thanks to this commit I was able to remove the svd computation from the setState() and consequentially I improved the overall performances of the classes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great. This has also the nice property that the simulated system remains an ODE, while with the normalization you are basically coupling an ODE with an discrete non linear instantaneous system, i.e. the block that takes in input the integrated orientation (that is the output of the ODE) and returns the normalized rotation instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's very clear, thank you! In the hindsight, it is also pretty clean to have directly the rotation matrix derivative, nice solution.

src/System/tests/IntegratorTest.cpp Outdated Show resolved Hide resolved
Comment on lines 85 to 90
// project the base orientation matrix in SO3
// here we assume that the velocity is expressed using the mixed representation
const Eigen::Matrix3d& baseOrientation = std::get<3>(state);
Eigen::JacobiSVD<Eigen::Matrix3d> svd(baseOrientation,
Eigen::ComputeFullU | Eigen::ComputeFullV);
std::get<3>(m_state) = svd.matrixU() * svd.matrixV().transpose();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, why you are performing this kind of computations in setState? Is it to make sure that the user is actually passing a rotation matrix?

Have you considered implementing the computations for SVD manually, since the matrix is small?

Comment on lines 80 to 83
std::get<0>(m_state) = std::get<0>(state);
std::get<1>(m_state) = std::get<1>(state);
std::get<2>(m_state) = std::get<2>(state);
std::get<4>(m_state) = std::get<4>(state);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to say that it is a little hard to remember what is what. I suspect there could be issues where we take one element for another. After a quick googling, there are some possible implementations of "tagged tuples", where the get uses a string instead of an index. Some references:

Clearly, this is not related to this PR and probably there is no clean solution, and I don't know if the initializer lists would work anyway, but it may be worth discussing.

Copy link
Collaborator

@traversaro traversaro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall it seems fine!

@GiulioRomualdi
Copy link
Member Author

Merging

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants