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

SO2 to R1 conversion for postprocessing #496

Merged
merged 32 commits into from
Feb 16, 2019
Merged

SO2 to R1 conversion for postprocessing #496

merged 32 commits into from
Feb 16, 2019

Conversation

aditya-vk
Copy link
Contributor

@aditya-vk aditya-vk commented Jan 27, 2019

The timers assume that the path representation is in R1. When the joints in the skeleton are SO2, we would like them to move along the geodesic and not the other way around. To ensure this happens, we need to appropriately convert the path representation to R1 along the geodesic. THis PR handles this conversion.


Before creating a pull request

  • Document new methods and classes
  • Format code with make format

Before merging a pull request

  • Set version target by selecting a milestone on the right side
  • Summarize this change in CHANGELOG.md
  • Add unit test(s) for this change

@aditya-vk aditya-vk added this to the Aikido 0.3.0 milestone Jan 27, 2019
Copy link
Contributor

@brianhou brianhou left a comment

Choose a reason for hiding this comment

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

Great start! I have a few questions about the implementation, mainly about whether the restriction to 1-DOF joints is necessary.

include/aikido/trajectory/util.hpp Outdated Show resolved Hide resolved
src/trajectory/util.cpp Outdated Show resolved Hide resolved
src/trajectory/util.cpp Show resolved Hide resolved
src/trajectory/util.cpp Show resolved Hide resolved
src/trajectory/util.cpp Outdated Show resolved Hide resolved
src/control/ros/Conversions.cpp Show resolved Hide resolved
src/control/ros/Conversions.cpp Outdated Show resolved Hide resolved
src/control/ros/Conversions.cpp Outdated Show resolved Hide resolved
src/control/ros/Conversions.cpp Outdated Show resolved Hide resolved
include/aikido/trajectory/util.hpp Outdated Show resolved Hide resolved
@codecov
Copy link

codecov bot commented Feb 3, 2019

Codecov Report

Merging #496 into master will decrease coverage by 0.03%.
The diff coverage is 77%.

@@            Coverage Diff             @@
##           master     #496      +/-   ##
==========================================
- Coverage   77.87%   77.83%   -0.04%     
==========================================
  Files         262      262              
  Lines        6712     6768      +56     
==========================================
+ Hits         5227     5268      +41     
- Misses       1485     1500      +15
Impacted Files Coverage Δ
src/planner/vectorfield/VectorFieldPlanner.cpp 87.75% <ø> (ø) ⬆️
include/aikido/statespace/CartesianProduct.hpp 100% <ø> (ø) ⬆️
src/control/ros/Conversions.cpp 78.5% <100%> (+0.38%) ⬆️
src/planner/parabolic/ParabolicUtil.cpp 92.59% <72.72%> (-5.28%) ⬇️
src/trajectory/util.cpp 87.57% <73.8%> (+0.58%) ⬆️
src/planner/kunzretimer/KunzRetimer.cpp 74.4% <73.91%> (-1.97%) ⬇️
src/planner/ompl/CRRT.cpp 75.58% <0%> (-0.59%) ⬇️

brianhou and others added 5 commits February 7, 2019 19:32
Co-Authored-By: aditya-vk <adityavk711@gmail.com>
Co-Authored-By: aditya-vk <adityavk711@gmail.com>
@aditya-vk
Copy link
Contributor Author

@brianhou I do not think dealing with multiple DOF joint spaces needs to be a part of this PR given that the rest of the stack, especially trajectory execution does not support it. Other comments have been addressed.

/// Converts an interpolated trajectory in the cartesian product space of SO(2)
/// and R1 joints
/// to a trajectory in cartesian product space of strictly only R1 joints.
/// \param[in] space Statespace the input trajectory is in.
Copy link
Contributor

Choose a reason for hiding this comment

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

Delete this line.

@@ -89,6 +89,26 @@ double findTimeOfClosestStateOnTrajectory(
UniqueSplinePtr createPartialTrajectory(
const Spline& traj, double partialStartTime);

/// Converts an interpolated trajectory in the cartesian product space of SO(2)
/// and R1 joints
Copy link
Contributor

Choose a reason for hiding this comment

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

This is wrapped weirdly, and things aren't capitalized correctly.

/// \param[in] trajectory Trajectory to be converted.
/// \return Converted trajectory.
aikido::trajectory::ConstInterpolatedPtr toR1JointTrajectory(
// aikido::statespace::ConstStateSpacePtr& space,
Copy link
Contributor

Choose a reason for hiding this comment

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

Delete this line.

/// Converts a spline trajectory in the cartesian product space of SO(2) and R1
/// joints
/// to a trajectory in cartesian product space of strictly only R1 joints.
/// \param[in] space Statespace the input trajectory is in.
Copy link
Contributor

Choose a reason for hiding this comment

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

Delete this line.

/// \param[in] trajectory Trajectory to be converted.
/// \return Converted trajectory.
aikido::trajectory::ConstSplinePtr toR1JointTrajectory(
// aikido::statespace::ConstStateSpacePtr& space,
Copy link
Contributor

Choose a reason for hiding this comment

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

Delete this line.

space->logMap(state, tangentVector);
space->convertStateToPositions(state, tangentVector);

auto geodesicInterpolator = std::dynamic_pointer_cast<aikido::statespace::GeodesicInterpolator>(interpolator);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why don't we take in a GeodesicInterpolator, rather than always dynamic casting it here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  1. Consistency: Seems to be the norm to pass in the base class and then cast it to the appropriate one.
  2. Flexibility: In case we introduce new interpolator classes and want to use the one that's tied to the trajectory.

I don't have a strong preference, but I'd be inclined to go this route.

Copy link
Contributor

Choose a reason for hiding this comment

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

In that case, can we replace this with methods that are defined in the Interpolator class?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See below.

src/trajectory/util.cpp Outdated Show resolved Hide resolved
src/trajectory/util.cpp Show resolved Hide resolved
NonSingularDOF
};

bool checkStateSpace(const statespace::StateSpace* _stateSpace, AllowJointType type)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@brianhou @gilwoolee :

There are a couple of tests written (planner / trajectory namespaces for example) that instantiate an R2 statespace and use this function during the process. I agree that we should not have two functions doing the same here. This seems to be a reasonable solution for now, especially given we currently have support for multi-DOF joints some places and not others. I would like to have this solution in place with an issue raised to

  • Change the tests to be cartesian product spaces with multiple R1 joints.
    or
  • Extend support for multi-DOF joint spaces uniformly and remove this enum usage.

Since this method is internal, I am okay with using an enum at this point but would liek to get rid of it soon. What do you guys think?

Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer to remove the enum and just use a Cartesian product space of multiple R1 joints in these tests, as part of this PR. Then, we can revisit the multi-DOF joint space if/when it becomes useful in the future.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with Brian. Can we have an issue to track this in case it becomes necessary?

"The interpolator of trajectory should be a GeodesicInterpolator");
}

auto diff = geodesicInterpolator->getTangentVector(prevState, state);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not entirely sure I understand why this change is necessary. So far, my understanding of this PR is:

  • We plan with SO(2) joints
  • We convert SO(2) joints to R1, postprocess (because postprocessors only deal with Rn joints), then convert back to SO(2)

What are we trying to do in this function? Is it what I thought was happening in #496 (comment), i.e. basically converting this into an Rn-joint trajectory to send out via ROS?

My main qualm is that it feels wrong to be requiring a specific type of interpolator here. I'm wondering whether we can rewrite this without using GeodesicInterpolator-specific methods, perhaps by actually converting it to an Rn trajectory and then just using logMap?

Copy link
Contributor Author

@aditya-vk aditya-vk Feb 11, 2019

Choose a reason for hiding this comment

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

To my understanding, we are committing to executing the shortest paths i.e. interpolating via the Geodesic anytime we execute a trajectory.

What are we trying to do in this function?

We want to extract waypoints along the trajectory "ensuring that the interpolation happens along the geodesic so that SO2 joint space is handled appropriately".

What is the PR doing?

  • We plan with SO2 joints
  • We convert SO2 joints to R1 and time it -> this generates a spline that has been timed appropriately.
  • When converting to ROS trajectory, each waypoint from this spline is extracted using the trajectory's statespace which is SO2 and hence requiring us to use the previous point to be able to interpolate appropriately, and then use the converted extracted waypoint to construct the ROS trajectory.

Conversion into Rn-joint trajectory and then using logMap would also involve the geodesic interpolator so I am not sure how that would help.

  1. The issue is that the base Interpolator class does not have a getTangentVector() and I don't see why it shouldn't since every derived class would need to support that. So I can alternatively add a virtual function there and if we take the interpolator by reference or obtain it from the trajectory, it should point to the right implementation?
  2. But the problem is we need to be able to support both Interpolated trajectories and Splines. Spline trajectories do not have an interpolator attached to them so we need to use something.

Copy link
Contributor

@gilwoolee gilwoolee Feb 11, 2019

Choose a reason for hiding this comment

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

@aditya-vk I didn't fully get the 3rd bullet point. trajectory is already in MetaSkeletonStateSpace so it's a Cartesian product of SO2. In SO2 spline trajectory, why do we need the previous point to interpolate appropriately?

Copy link
Contributor Author

@aditya-vk aditya-vk Feb 14, 2019

Choose a reason for hiding this comment

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

Yes exactly. It is in the Cartesian Product of SO2, call it space. So when we evaluate the trajectory, space is used to retrieve the state (=space->createState()). Although the spline polynomial is correct, when we retrieve it using space, the representation for SO2 joints is converted back to between -pi and pi.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This leaves two options: Either have your trajectory be tied with CartesianProduct space of all R1 joints which can again get messy since we check for MetaSkeletonStateSpace during execution or use the interpolation from the previous point.

Copy link
Contributor Author

@aditya-vk aditya-vk Feb 14, 2019

Choose a reason for hiding this comment

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

I was wondering if we could return evaluatePolynomial() instead of evaluate() since that returns the value of the polynomial which is what we desire. However, again, since the spline segments are solved from 0, we need to know the start state of the segment which is stores in mSegments.startState and that is represented as R + SO2. So this route is pretty much useless as well.

@brianhou
Copy link
Contributor

After discussing with @aditya-vk, it seems that somewhere lower down in the stack would be a better place to convert to R1 trajectories (when necessary). I think this would make for a cleaner abstraction, since we'd be able to reason with the SO(2) joints all the way from planning until we send it off to the controller.

I've created personalrobotics/rewd_controllers#30 to track this, let's clean up this PR to remove the conversion here.

@gilwoolee gilwoolee merged commit fe6ba16 into master Feb 16, 2019
@gilwoolee gilwoolee deleted the so2_to_r1 branch February 16, 2019 06:07
@gilwoolee gilwoolee mentioned this pull request Feb 17, 2019
5 tasks
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.

3 participants