-
Notifications
You must be signed in to change notification settings - Fork 1
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
feat!: split trajectory classes and implement Cartesian/JointState specializations #218
base: feat/trajectory-msgs-dev
Are you sure you want to change the base?
feat!: split trajectory classes and implement Cartesian/JointState specializations #218
Conversation
One side effect of the new structure is that since we are storing a flat queue but expose That is, we can no longer do: trajectory[n] = CartesianState(...) Instead, I replaced the functionality by adding |
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.
Review of the headers
source/state_representation/include/state_representation/StateType.hpp
Outdated
Show resolved
Hide resolved
source/state_representation/include/state_representation/trajectory/TrajectoryBase.hpp
Outdated
Show resolved
Hide resolved
source/state_representation/include/state_representation/trajectory/TrajectoryBase.hpp
Outdated
Show resolved
Hide resolved
source/state_representation/include/state_representation/trajectory/TrajectoryBase.hpp
Outdated
Show resolved
Hide resolved
source/state_representation/include/state_representation/trajectory/TrajectoryBase.hpp
Outdated
Show resolved
Hide resolved
source/state_representation/include/state_representation/trajectory/TrajectoryBase.hpp
Outdated
Show resolved
Hide resolved
source/state_representation/include/state_representation/trajectory/TrajectoryBase.hpp
Outdated
Show resolved
Hide resolved
source/state_representation/include/state_representation/trajectory/CartesianTrajectory.hpp
Outdated
Show resolved
Hide resolved
source/state_representation/include/state_representation/trajectory/CartesianTrajectory.hpp
Outdated
Show resolved
Hide resolved
source/state_representation/include/state_representation/trajectory/TrajectoryBase.hpp
Outdated
Show resolved
Hide resolved
source/state_representation/include/state_representation/trajectory/TrajectoryBase.hpp
Outdated
Show resolved
Hide resolved
if (new_point.is_empty()) { | ||
throw exceptions::EmptyStateException("Point is empty"); | ||
} | ||
if (this->get_size() > 0) { |
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.
Ok so I think I would like to go like that:
Cartesian
- Emtpy constructor: no name, reference frame default to world and will stay there (consistent with the rest of the stack)
- Constructor with name and reference frame
- Constructor with name, point, duration (in that order): reference frame taken from point
- Constructor with name, vector of points, vector of durations (in that order): verify all reference frames are the same, set reference frame from that
- get_point, add_point, add_points, get_points, insert_points do the obvious checks
- reset is here to reset a state to empty and have no data, it doesn't mean we reset the reference frames or joint frames. hence, for me, reset should just do that, not mess with reference frame
- set_points is a bit more tricky, I would say that the size of vectors passed as arguments need to correspond to the existing size of the trajectory. If one wants to change all points and size on a trajectory object, one can just call
reset
and thenadd_points
. - If we really want to go all the way, we could introduce a
set_reference_frame(CartesianPose pose)
that multiplies all points with the pose in order to actually perform a change of reference frame and not just a renaming.
Joint
all the same, except that we can probably allow a set_joint_names(std::vector<std::string>)
directly.
What do you think?
@domire8 A few things I want to go over in case you review before that
Let's take the trajectory.set_reference_frame(some_pose)
// if new points different in size
trajectory.reset()
trajectory.add_points(points)
// if new points same in size
trajectory.set_points(points) // here we could set the points and then the reference frame For Joint trajectories, things are a bit more direct and the setters will directly change the joint names or robot name directly without checking something
|
Regarding the constructors, I'd also like to see for consistency
I see that adding new points with different reference frame requires multiple function calls, but in that case the user should just create a new object honestly. I feel that it's quite clear if the reference frame is set on construction, and if you want to change it you need to call Also, I'm not sure that the |
Noted, thanks for checking. Regarding the robot name, my understanding was this should be common for all |
Yes I think so, same as for the Cartesian |
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.
Still a few places where I see room for improvement and reduction of code duplication. Happy to talk about it if you disagree with my points
source/state_representation/include/state_representation/trajectory/TrajectoryBase.hpp
Outdated
Show resolved
Hide resolved
source/state_representation/include/state_representation/trajectory/TrajectoryBase.hpp
Outdated
Show resolved
Hide resolved
source/state_representation/include/state_representation/trajectory/TrajectoryBase.hpp
Show resolved
Hide resolved
if (index > this->points_.size()) { | ||
throw std::out_of_range("Index out of range"); | ||
} |
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.
if (index > this->points_.size()) { | |
throw std::out_of_range("Index out of range"); | |
} | |
this->assert_index_in_range(index); |
What we have done in other places is to wrap that check that we have several times in a helper.
control-libraries/source/state_representation/include/state_representation/IOState.hpp
Line 142 in f72569e
static void assert_index_in_range(unsigned int io_index, unsigned int size); |
* @class CartesianTrajectoryPoint | ||
* @brief Struct to represent a Cartesian trajectory point | ||
*/ | ||
struct CartesianTrajectoryPoint : public TrajectoryPoint {}; |
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.
Is this necessary or just nice? I'm just wondering. And while we are here, you might as well define a constructor from a duration and CartesianState, that will help reduce code in the source file
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.
Yes, I think this is just nicer to have a dedicates struct for this class which could also allow us to more easily specify fields if ever needed.
Also, my original understanding (because of the attribute name) was that JointStates have a robot name, not necessarily a state name, that is common to all. In that case, we would only need to store a name for Cartesian states. But still, I find it a bit nicer to keep it there for future use
if (new_point.is_empty()) { | ||
throw exceptions::EmptyStateException("Point is empty"); | ||
} else if (new_point.get_reference_frame() != this->reference_frame_) { | ||
throw exceptions::IncompatibleReferenceFramesException( | ||
"Incompatible reference frames: " + new_point.get_reference_frame() + " and " + this->reference_frame_ | ||
); |
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.
These lines are also duplicated, which speaks for a static helper up top that does the same
assert_valid(const CartesianState& point, const std::string& reference_frame) {
throw if point empty
throw if reference frame not the same
}
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 introduced quite a few assertion functions. We might see that some of them can be further merged, but for starters everything should be a bit more readable now
void JointTrajectory::add_point(const JointState& new_point, const std::chrono::nanoseconds& duration) { | ||
if (new_point.is_empty()) { | ||
throw exceptions::EmptyStateException("Point is empty"); | ||
} else if (this->joint_names_ != new_point.get_names()) { |
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.
Does that operator work to compare all strings from a vector?
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.
yes, side note is that in c++20 these operators are automatically generated because of the introduction of the spaceship <=>
(https://en.cppreference.com/w/cpp/container/vector). TL;DR we are both backwards and forwards compatible and this works
Description
Also resolves #217.
This PR solves the issue by splitting the existing Trajectory class to a base and 2 derived classes, namely, CartesianTrajectory and JointTrajectory.
A lot of the functionality of the base class is now marked protected and acts more as a utility for derived classes. The two new classes share similarities but require different handling when it comes to member variables and cleaning up.
This is missing python bindings for the new types. I believe it will be cleaner to add them separately.
Review guidelines
Estimated Time of Review: 20 minutes
Checklist before merging: