-
Notifications
You must be signed in to change notification settings - Fork 33
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
Conversation
- 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
// 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(); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
Do you think that a Baumgarte stabilization may solve the problem?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 integralThis 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:
- Store the quaternion in an
Eigen::Vector4d
and apply a similar approach. In this case, I won't have any problems; - Store the quaternion in an
Eigen::Quaterniond
and its rate of change inEigen::Vector4d
. In this casestd::get<I>(x) += std::get<I>(dx) * dT
will fail because theoperator+()
is not defined betweenEigen::quaterniond
andEigen::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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
// 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(); |
There was a problem hiding this comment.
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?
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); |
There was a problem hiding this comment.
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:
- https://stackoverflow.com/a/46862694
- https://gist.github.com/ilsken/dd91285d50197d6345f9
- https://github.com/duckie/named_types
- https://bitbucket.org/tlutz/named-variadic-template-proposal/src/master/
- https://stackoverflow.com/questions/13065166/c11-tagged-tuple
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.
There was a problem hiding this 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!
…temDynamics and FloatingBaseSystemKinematics
ff98d9a
to
339cf76
Compare
Merging |
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#716A test for the
FloatingBaseSystemDynamics
class is still missing.