-
Notifications
You must be signed in to change notification settings - Fork 30
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
Yaml extension for Eigen::Matrix and std::unordered_map #175
Conversation
…rsonalrobotics/aikido into SimBarrettHandCommandExecutor
Duplicate changes in BarrettHandKinematicSimulationPositionCommandExecutor.
…cutor in HandPositionCommandExecutor. Adapted from https://github.com/personalrobotics/libherb/pull/20.
Make test_BarrettFinger* compile (although actual execution seems to fail). Make part of test_BarrettHand compile, although introducing `robot` as a parameter seems like it will make testing this difficult.
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 generally looks good. I guess I shouldn't be surprised, since I wrote it!
A few minor comments, namely:
- Add docstrings for the helper functions, e.g.
aikido::util::deserialize
and, if you're feeling energetic, theyaml-cpp
template class specializations. - Add unit tests for converting to/from YAML.
Also a few points for discussion:
- This file is a bit of a mess (as it was in
muul
). Does it make sense to split some of this into thedetail
directory? @gilwoolee @jslee02 does this make sense to you, and if so, which parts? - It would be nice to eliminate the dependency on
boost::format
. Is it worth replacing those calls withstd::stringstream
to do so?
include/aikido/util/yaml_utils.hpp
Outdated
#include <vector> | ||
#include <iostream> | ||
#include <boost/format.hpp> | ||
#include <boost/make_shared.hpp> |
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.
Of these, I think only Eigen/Dense
, yaml-cpp/yaml.h
, and boost/format.hpp
are actually used. We should remove the unused #include
directives.
Actually, I'd also like to replace boost::format
with std:;stringstream
to eliminate that dependency as well.
include/aikido/util/yaml_utils.hpp
Outdated
{ | ||
typedef Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> MatrixType; | ||
typedef typename MatrixType::Index Index; | ||
typedef typename MatrixType::Scalar Scalar; |
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.
Nit: Prefer using
aliases to typedef
s.
include/aikido/util/yaml_utils.hpp
Outdated
struct encode_impl<MatrixType, true> { | ||
static Node encode(MatrixType const &matrix) | ||
{ | ||
typedef typename MatrixType::Index Index; |
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.
Nit: Prefer using
aliases to typedef
s.
include/aikido/util/yaml_utils.hpp
Outdated
inline void operator>>(Node const &node, T &value) | ||
{ | ||
value = node.as<T>(); | ||
} |
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'd like to remove this. If I remember correctly, I added this as a workaround to support both yaml-cpp
versions 0.3 (which used the bitshift operators) and 0.5 (which used the as
function).
include/aikido/util/yaml_utils.hpp
Outdated
|
||
template <class _Scalar, | ||
int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> | ||
inline void deserialize( |
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.
Missing docstring. I'm willing to let it slide for the convert
that we specialize for yaml-cpp
- but this is purely a helper function, so it should be properly documented. 😁
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 believe we don't use this function out of template <...> struct convert
. Could I just move this into it as you said?
include/aikido/util/yaml_utils.hpp
Outdated
inline bool has_child(YAML::Node const &node, T const &key) | ||
{ | ||
return node[key].IsDefined(); | ||
} |
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 function is not used, please delete it.
include/aikido/util/yaml_utils.hpp
Outdated
|
||
template <class _Scalar, int Dim, int Mode, int _Options> | ||
inline void deserialize(YAML::Node const &node, | ||
Eigen::Transform<_Scalar, Dim, Mode, _Options> &pose) |
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.
Missing docstring. See above. 😉
include/aikido/util/yaml_utils.hpp
Outdated
namespace detail { | ||
|
||
template <typename MatrixType, bool IsVectorAtCompileTime> | ||
struct encode_impl { |
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 is an internal helper class, so it deserves a docstring. 😁
The reason this exists is so we can use template specialization to switch between serializing vectors and serializing matrices based on the IsVectorAtCompileTime
flag. This is a "nice to have" that lets us generate !Vector [1, 2, 3]
instead of the syntactic travesty like !Matrix [[1], [2] ,[3]]
when serializing an Eigen vector.
include/aikido/util/yaml_utils.hpp
Outdated
} | ||
}; | ||
|
||
} //detail |
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.
Nit: We may want to move this helper code into the detail
directory. @gilwoolee @jslee02 Thoughts?
I believe none of these functions needs to be exposed as public API. So all the code should be in
More importantly, we already have this file in |
include/aikido/util/yaml_utils.hpp
Outdated
int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> | ||
inline void deserialize( | ||
YAML::Node const &node, | ||
Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> &matrix) |
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.
We might want to follow the suggestion of Eigen's "Writing Functions Taking Eigen Types as Parameters" here.
and add a test
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.
Question: It seems the
yaml-cpp
's convention is to return false when failed to decode rather than throwing an exception, but the decoder forEigen::Matrix
in this PR throws exceptions for the case. So I would like to change it to follow the convention.
I am conflicted. On one hand, I'd like to honor yaml-cpp
's API convention as much as possible. On the other hand, it is hard to debug parsing errors when as
returns false
without a useful error message.
Additionally, the code currently only raises an exception if a node is tagged as a !Vector
or !Matrix
that cannot be interpreted as such. I would be more open to this change if we modified the conversion functions to not require those tags.
I believe that is possible with a small refactor. Instead of checking the tag, we'll simply check whether the node contains two layers of nested lists (a Matrix
), one level of lists (a Vector
), or another value (an error). I like this idea quite a bit because many people are not familiar with tags in YAML.
} | ||
}; | ||
|
||
} // namespace (anonymous) |
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.
Don't use anonymous namespaces in headers. I suggest putting this in a detail
namespace 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.
I thought we use anonymous namespace for objects that are only used in the implementation, and use detail
namespace for objects that are used somewhere out of the implementation but don't expect that is used by the user.
In most cases, the implementation is placed in a source file, but we have this decode
in a header file because it's a template. I think we could consider putting this into a separate file with the name yaml_extension-impl.hpp
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.
I don't think we should ever put an anonymous namespace
in a header file. A header file can be #include
ed into more than one translation unit, so this will result in unnecessary duplication of the objects (in the best case) or difficult-to-diagnose bugs (in the worst case, e.g. possibly if the namespace contains static
member or global variables).
I see the detail
namespace as the logical equivalent of an anonymous namespace in the header file, i.e. it is an implementation detail of other functions in that file.
{ | ||
if (node.Tag() == "Vector" || node.Tag() == "!Vector" | ||
|| node.Tag() == "Matrix" | ||
|| node.Tag() == "!Matrix") |
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 pretty sure that we only need the set with the !
or the one without. If I remember correctly, I have both here because the behavior changed between yaml-cpp
0.3 and 0.5. Aikido only supports yaml-cpp
0.5, so that is a moot point.
Unfortunately, I don't remember which is used in 0.5. I'd like to do a quick test and eliminate the superfluous check.
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'm not sure either, but it seems the spec says !
should be used. So let's stick with that. 😅
|
||
map.clear(); | ||
for (const auto& it : node) | ||
map[it.first.as<_Key>()] = it.second.as<_Tp>(); |
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.
What happens if as<>()
fails? If we want to return false
on failure, then we need to handle that case explicitly.
if (!node.IsMap()) | ||
return false; | ||
|
||
map.clear(); |
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.
Nit: Call map.reserve(node.size())
.
I do not think it is a good idea to introduce exceptions into the And doesn't the https://github.com/jbeder/yaml-cpp/blob/master/include/yaml-cpp/node/impl.h#L125 |
Hm, looking into more As a consensus, I would like to use exceptions but |
I agree with @jslee02's conclusion - let's keep exceptions, but switch from Also, I would like to eliminate the need for the |
I believe this PR is ready to merge. One remained an issue but not a blocker is removing the requirement of tag for vector and matrix. I'll create an issue for this to implement this in the future. |
#else | ||
DART_UNUSED(node); | ||
return YAML::Mark::null_mark(); | ||
#endif |
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.
Why is this #ifdef
necessary? Can you add a comment explaining why?
int _Cols, | ||
int _Options, | ||
int _MaxRows, | ||
int _MaxCols> |
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.
@jslee02 Did you determine whether if or not we can replace this with template <class _Derived> struct<Eigen::MatrixBase<_Derived>>
? I really liked that 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.
Yeah, I haven't figured it out yet, but it doesn't work for struct. 😞 Let's put this over for now.
Adds YAML parser for Eigen datatype, originally implemented by @mkoval . Copied from kenv. Allows operations such as
node.as<Eigen::VectorXd>, node.as<Eigen::Isometry3d>
, etc..