-
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
Add methods for converting trajectories between different (but equivalent) state spaces #249
Conversation
Codecov Report
@@ Coverage Diff @@
## master #249 +/- ##
=======================================
Coverage 70.43% 70.43%
=======================================
Files 181 181
Lines 5391 5391
Branches 847 847
=======================================
Hits 3797 3797
Misses 1074 1074
Partials 520 520
|
Hmm, I need to think a bit about how we are ending up a situation where we have multiple Statespaces that are effectively identical. We would eventually need conversions for situations where there are two statespaces from different places that happen to be identical, but it seems weird that these multiple Statespaces are being created through cloning. Is there something going on where would prefer these Statespaces to be immutable objects that are passed by reference instead of copied when cloning occurs? @mkoval? |
This is a fundamental issue beyond I see two possible solutions:
The key difference between is whether we (1) relax this requirement or (2) work around it through conversion functions. I prefer (1) because it keeps all of the nasty conversion logic, instead of scattered throughout the entire public API. The conversion and compatibility check could look something like this: bool Rn::convert(StateSpace sourceSpace, const StateSpace::State* sourceState,
StateSpace::State* destinationState)
{
auto sourceSpaceRn = dynamic_cast<const Rn*>(&sourceSpace);
if (!(sourceSpaceRn && mN == sourceSpaceRn->mN)) return false;
auto sourceStateRn = static_cast<const Rn*>(sourceState);
auto destinationStateRn = static_cast<Rn*>(destinationState);
destinationStateRn->mData = sourceStateRn->mData;
return true;
} In principle, this solves the problem. All we would need to do is implement a conversion function on each However, @psigen raises a great point. It’s odd that we a It may be worth refactoring For example, the API could look like this: class MetaSkeletonStateSpace {
StateSpacePtr getStateSpace() const;
void convertStateToPositions(const StateSpace::State* state, Eigen::VectorXd* positions) const;
void convertPositionsToState(const Eigen::VectorXd& positions, StateSpace::State* state) const;
static MetaSkeletonStateSpace fromMetaSkeleton(const dart::dynamics::MetaSkeleton& metaSkeleton);
}; In hindsight, I wish I implemented it this way originally. 😓 |
@mkoval: Do you remember why we originally went with this? I'm trying to recall if it was an implementation constraint, or if there was a particular set of semantics we were trying to create. It certainly seems intuitive at the moment that two Separately, I'm still not entirely clear why we need to create new instances of |
It's a semantic I wanted to enforce. It's true that it's harmless to pass states between two It gets more complicated if you think about state spaces that represent the same topology, but have different internal representations. For example, SO(3) may be parameterized by unit quaternions, spatial twists, rotation matrices, or Euler angles. It's clear what the intended result is - that all of these parameterzations are interchangeably - but it's tricky to implement that cleanly. @jslee02 tacked some of these issues in the The simplest solution is to only allow a I am generally in favor of keeping the "only allow a
I agree! Even without a conversion function, a lot of issues go away if we do this. We could also consider using a singleton for state spaces that take no runtime parameters (e.g. |
Ok, FWIW, I think we're on the same page here. I think it would make sense to do the following:
If we do these two things, then I think we largely obviate the need for this PR. We might need to convert state spaces in cases where two dynamic StateSpaces are loaded and we need to do a more rigorous equivalence check, but we can do this via a "conversion" function ahead of time and use a normal comparator to check instance equality in the internal functions. |
@mkoval -- @gilwoolee and I have been trying to understand how this works, but we have a few questions.
Rather than having a separate We'd still use the input Since
My understanding is that this solution means we won't implement the |
@brianhou @gilwoolee: maybe it would help to think of it this way:
So, you can think of We can take a given So the intended API for a Does that make sense? With that architecture the presented API was intended to be relatively straightforward to implement without requiring extra params or conversion functions. |
I entirely agree with @psigen here. I regret not implementing |
Sounds good! I started to work on the refactor today, and I'm hoping to finish it in the next couple days. I initially removed the |
Closing in favor of #278. |
We need to be able to plan trajectories for one
MetaSkeleton
inMetaSkeletonStateSpace
, while being able to execute them in different but equivalent state spaces. This is important for pipelining planning and execution: since trajectories are tied to a specific state space (aMetaSkeletonStateSpace
for aMetaSkeleton
in a specificWorld
), trajectories from the planning environment cannot be executed in the execution environment.This should probably have better error-checking to prevent converting between incompatible
StateSpace
s. Without knowing the exact type of theStateSpace
, is the best thing I can do here just compare the dimensions? Is it fair to assume that the two spaces will always be aMetaSkeletonStateSpace
? (When else would one need to convert the state space into an equivalent one?)I'm open to better suggestions, but this is the best way that I've thought of so far.
Before creating a pull request
make format
Before merging a pull request
CHANGELOG.md