Normalization methods SpecialEuclidean and SpecialOrthogonal rely on Eigen's normalize() method. According to the method's doc, if the vector's norm is too small, then it is left unchanged. Because of this, if the input vector contains all zeroes at the place of quaternions/continuous joints, the output of Pinocchio's normalize is _not_ normalized.
This is unexpected behaviour. A possible solution would be normalizing to the neutral configuration for the specific joint where Eigen's normalization fails. Would you agree on this? I can handle it as soon as I have time.
Additionally, it would be helpful to add a method called isNormalized for joint configurations (and, more in general, isValid, checking normalization _and_ bounds) (for things like checking user's input and such).
This is unexpected behaviour. A possible solution would be normalizing to the neutral configuration for the specific joint where Eigen's normalization fails. Would you agree on this? I can handle it as soon as I have time.
It might be dangerous I would say, leading to some discontinuities, everything leading to the neutral element. I would prefer to avoid that, and I do think @jmirabel may provide his feedback on it too.
Additionally, it would be helpful to add a method called
isNormalizedfor joint configurations (and, more in general,isValid, checking normalization _and_ bounds) (for things like checking user's input and such).
These new helper functions are more than welcome. Please go ahead.
FYI,
IsNormalized exists in hpp-pinocchio. Copy-paste may (or may not) be simpler than starting from scratch.normalize was introduced to overcome rounding errors and the zero quaternion example is more likely to be a programming mistake than a rounding error. I think it is better to have one tool for each goal. That being said, I agree the method name may be misleading (as it is in Eigen).We may decide to do as it is done in Eigen: state the small norm issue in the doc.
By the way, for the helpers, please add the prec arguments, similarly to isApprox for Pinocchio and Eigen.
Ok, so, based on your answers, I will
normalize as it is, but clarify its limitations in the docisNormalized (with precision)isValidConfiguration (with precision for normalized, but hard check for the limits)Fine with me. Thanks @gabrielebndn for handling this.
- Add
isValidConfiguration(with precision for normalized, but hard check for the limits)
You'll have to pass the limits as arguments.
- Add
isValidConfiguration(with precision for normalized, but hard check for the limits)You'll have to pass the limits as arguments.
The idea is that there is no need to implement isValidConfiguration joint per joint. It can be done at the model level, and checked element-by-element. So I was thinking of using the internal lowerPositionLimit and upperPositionLimit. I can do it with custom limits too, of course.
Providing the two signatures would be nice indeed.
In which class do you want to implement the isValidConfiguration function ? Lie group classes do not have limits as member attributes.
The guess is to perform that at the model configuration level for the whole using the difference function.
In which class do you want to implement the
isValidConfigurationfunction ? Lie group classes do not have limits as member attributes.
No class. I am not planning to implement it for Lie groups. Just a free function for the model. Basically (just a sketch):
bool isValidConfiguration(Model model,
VectorXd q,
VectorXd lowerBound,
VectorXd upperBound,
double prec=Eigen::NumTraits<double>::dummy_precision())
{
if(!isNormalized(model, q, prec))
return false;
for(int i=0; i<model.nq; i++)
{
if(q[i] < lowerBound[i] || q[i] > upperBound[i])
return false;
}
return true;
}
bool isValidConfiguration(Model model,
VectorXd q,
double prec=Eigen::NumTraits<double>::dummy_precision())
{
return isValidConfiguration(model, q, model.lowerPositionLimit, model.upperPositionLimit, prec);
}
Please do it for Lie groups. It is just a matter of using Pinocchio::difference.
We may decide to do as it is done in Eigen: state the small norm issue in the doc.
What about an assert, or an exception for those who never compile in debug mode ?
Please do it for Lie groups. It is just a matter of using Pinocchio::difference.
Not sure about it. First of all, the concept of bound for Lie Groups is dubious at best as far as I understand, second of all I am really not sure on how to implement it with quaternions (and it can be confusing for SO(2) too, what about \pi - \delta <= \theta <= \pi + \delta)? The truth is the bounds on the quaternion part of the configuration have never had a lot of sense and we only have them for ease of implementation. @jmirabel what do you think?
I think that for the bounds I'll stick to the element-by-element comparison performed at the model level. If you think it is an inadequate implementation, then I'll leave it to you, it was just a suggestion :slightly_smiling_face:
@gabrielebndn, you just need to use the correct operations, and pinocchio::difference will be transparent in your implementation. This is the only correct operator. In the case of quaternion parts, the limit is a bit above the one value for each component, and it won't affect the check. Test it. This is my only but main concern.
Not sure about it. First of all, the concept of bound for Lie Groups is dubious at best as far as I understand, second of all I am really not sure on how to implement it with quaternions (and it can be confusing for
SO(2)too, what about\pi - \delta <= \theta <= \pi + \delta)? The truth is the bounds on the quaternion part of the configuration have never had a lot of sense and we only have them for ease of implementation. @jmirabel what do you think?
As you said, checking the bound of a unit quaternion or a unit complex is meaningless. Two useful things:
Please do it for Lie groups. It is just a matter of using Pinocchio::difference.
@jcarpent I think I don't understand what you mean. What do you want to do with difference ?
@jcarpent I think I don't understand what you mean. What do you want to do with
difference?
As simple as: pinocchio::difference(model,q,lb).array() >= 0.
This is exactly how it works in TSID and other related frameworks.
This won't work for quaternions.
This won't work for quaternions.
That's exactly my point.
Two useful things:
* a light check that ensure the coefficient are within [-1, 1], with a tiny epsilon. * check the normalization.
@jmirabel, I'll just note that the second point implies the first one. If the quaternion is normalized, then its coefficients are certainly within [-1, 1], so no need to check it separately.
Yes, but this means you can safely do lb <= q <= ub, assuming lb and ub are properly set.
Yes, but this means you can safely do lb <= q <= ub, assuming lb and ub are properly set.
Of course, that was the idea
About the function name, I think isValidConfiguration is not precise enough. One may think you do collision checking too.
Not sure exactly of what function would suit it more:
isConfigurationWithinBounds,isWithinBoundsisConfigInConfigSpaceThe unit-norm check being somehow equivalent to the standard joint bounds, I like the second one.
I would prefer to keep the original suggestion. The doc should be precise enough.
Your suggestions seem to be too long and not providing additional meaning to me.
isValidConfiguration isn't very short (and makes the code stutter).
From the developer perspective, I judge
pinocchio::isWithinBounds(model, config);
more readable than
pinocchio::isValidConfiguration(model, config);
There a two thing:
isValidConfiguration is calling isWithinBounds + isNormalized.
isValidConfigurationisn't very short (and makes the code stutter).From the developer perspective, I judge
pinocchio::isWithinBounds(model, config);more readable than
pinocchio::isValidConfiguration(model, config);
The idea was that isValidConfiguration would check that it is normalized _and_ within bounds. BTW, I am starting to reconsider the need of checking the bounds, after all it is a one-liner in C++ :
((q-lowerBound).array() >= 0).all() && ((upperBound-q).array() >= 0).all()
Yes, but you need to know Eigen.
There a two thing:
isValidConfigurationis callingisWithinBounds+isNormalized.
Exactly. Should I implement _both_ isWithinBounds and isValidConfiguration? Feels redundant, but costs me nothing
Otherwise, I can just implement isWithinBounds and let users do
isNormalized(q) && isWithinBounds(model, q);
Simple, clear, and doesn't need any advanced Eigen knowledge ;)
I'm not against it, and should be easy.
Perfect
Closed by #1324