Skip to content

Commit 53627d3

Browse files
henningkayserpac48AbishaliniEzraBrooks
authored
Enable mdof trajectory execution (#2740)
* Add RobotTrajectory conversion from MDOF to joints * Convert MDOF trajectories to joint trajectories in planning interfaces * Treat mdof joint variables as common joints in TrajectoryExecutionManager * Convert multi-DOF trajectories to joints in TEM * Revert "Convert MDOF trajectories to joint trajectories in planning interfaces" This reverts commit 885ee27. * Handle multi-DOF variables in TEM's bound checking * Add parameter to optionally enable multi-dof conversion * Improve error message about unknown controllers * Fix name ordering in JointTrajectory conversion * Improve DEBUG output in TEM * Comment RobotTrajectory test * add acceleration to avoid out of bounds read Signed-off-by: Paul Gesel <paulgesel@gmail.com> --------- Signed-off-by: Paul Gesel <paulgesel@gmail.com> Co-authored-by: Paul Gesel <paulgesel@gmail.com> Co-authored-by: Abishalini Sivaraman <abi.gpuram@gmail.com> Co-authored-by: Ezra Brooks <ezra@brooks.cx>
1 parent f880329 commit 53627d3

File tree

5 files changed

+265
-18
lines changed

5 files changed

+265
-18
lines changed

moveit_core/robot_trajectory/include/moveit/robot_trajectory/robot_trajectory.h

+9
Original file line numberDiff line numberDiff line change
@@ -410,4 +410,13 @@ std::ostream& operator<<(std::ostream& out, const RobotTrajectory& trajectory);
410410
/// or nullopt if it is not possible to calculate the density
411411
[[nodiscard]] std::optional<double> waypointDensity(const RobotTrajectory& trajectory);
412412

413+
/// \brief Converts a RobotTrajectory to a JointTrajectory message
414+
// \param[in] trajectory Given robot trajectory
415+
// \param[in] include_mdof_joints Treat Multi-DOF variables as joints, e.g. position/x position/y position/theta
416+
// \param[in] joint_filter Exclude joints with the provided names
417+
// \return JointTrajectory message including all waypoints
418+
// or nullopt if the provided RobotTrajectory or RobotModel is empty
419+
[[nodiscard]] std::optional<trajectory_msgs::msg::JointTrajectory>
420+
toJointTrajectory(const RobotTrajectory& trajectory, bool include_mdof_joints = false,
421+
const std::vector<std::string>& joint_filter = {});
413422
} // namespace robot_trajectory

moveit_core/robot_trajectory/src/robot_trajectory.cpp

+132
Original file line numberDiff line numberDiff line change
@@ -373,29 +373,36 @@ void RobotTrajectory::getRobotTrajectoryMsg(moveit_msgs::msg::RobotTrajectory& t
373373
{
374374
const std::vector<std::string> names = mdof[j]->getVariableNames();
375375
const double* velocities = waypoints_[i]->getJointVelocities(mdof[j]);
376+
const double* accelerations = waypoints_[i]->getJointAccelerations(mdof[j]);
376377

377378
geometry_msgs::msg::Twist point_velocity;
379+
geometry_msgs::msg::Twist point_acceleration;
378380

379381
for (std::size_t k = 0; k < names.size(); ++k)
380382
{
381383
if (names[k].find("/x") != std::string::npos)
382384
{
383385
point_velocity.linear.x = velocities[k];
386+
point_acceleration.linear.x = accelerations[k];
384387
}
385388
else if (names[k].find("/y") != std::string::npos)
386389
{
387390
point_velocity.linear.y = velocities[k];
391+
point_acceleration.linear.y = accelerations[k];
388392
}
389393
else if (names[k].find("/z") != std::string::npos)
390394
{
391395
point_velocity.linear.z = velocities[k];
396+
point_acceleration.linear.z = accelerations[k];
392397
}
393398
else if (names[k].find("/theta") != std::string::npos)
394399
{
395400
point_velocity.angular.z = velocities[k];
401+
point_acceleration.angular.z = accelerations[k];
396402
}
397403
}
398404
trajectory.multi_dof_joint_trajectory.points[i].velocities.push_back(point_velocity);
405+
trajectory.multi_dof_joint_trajectory.points[i].accelerations.push_back(point_acceleration);
399406
}
400407
}
401408
if (duration_from_previous_.size() > i)
@@ -707,4 +714,129 @@ std::optional<double> waypointDensity(const RobotTrajectory& trajectory)
707714
return std::nullopt;
708715
}
709716

717+
std::optional<trajectory_msgs::msg::JointTrajectory> toJointTrajectory(const RobotTrajectory& trajectory,
718+
bool include_mdof_joints,
719+
const std::vector<std::string>& joint_filter)
720+
{
721+
const auto group = trajectory.getGroup();
722+
const auto robot_model = trajectory.getRobotModel();
723+
const std::vector<const moveit::core::JointModel*>& jnts =
724+
group ? group->getActiveJointModels() : robot_model->getActiveJointModels();
725+
726+
if (trajectory.empty() || jnts.empty())
727+
return std::nullopt;
728+
729+
trajectory_msgs::msg::JointTrajectory joint_trajectory;
730+
std::vector<const moveit::core::JointModel*> onedof;
731+
std::vector<const moveit::core::JointModel*> mdof;
732+
733+
for (const moveit::core::JointModel* active_joint : jnts)
734+
{
735+
// only consider joints listed in joint_filter
736+
if (!joint_filter.empty() &&
737+
std::find(joint_filter.begin(), joint_filter.end(), active_joint->getName()) == joint_filter.end())
738+
continue;
739+
740+
if (active_joint->getVariableCount() == 1)
741+
{
742+
onedof.push_back(active_joint);
743+
}
744+
else if (include_mdof_joints)
745+
{
746+
mdof.push_back(active_joint);
747+
}
748+
}
749+
750+
for (const auto& joint : onedof)
751+
{
752+
joint_trajectory.joint_names.push_back(joint->getName());
753+
}
754+
for (const auto& joint : mdof)
755+
{
756+
for (const auto& name : joint->getVariableNames())
757+
{
758+
joint_trajectory.joint_names.push_back(name);
759+
}
760+
}
761+
762+
if (!onedof.empty() || !mdof.empty())
763+
{
764+
joint_trajectory.header.frame_id = robot_model->getModelFrame();
765+
joint_trajectory.header.stamp = rclcpp::Time(0, 0, RCL_ROS_TIME);
766+
joint_trajectory.points.resize(trajectory.getWayPointCount());
767+
}
768+
769+
static const auto ZERO_DURATION = rclcpp::Duration::from_seconds(0);
770+
double total_time = 0.0;
771+
for (std::size_t i = 0; i < trajectory.getWayPointCount(); ++i)
772+
{
773+
total_time += trajectory.getWayPointDurationFromPrevious(i);
774+
joint_trajectory.points[i].time_from_start = rclcpp::Duration::from_seconds(total_time);
775+
const auto& waypoint = trajectory.getWayPoint(i);
776+
777+
if (!onedof.empty())
778+
{
779+
joint_trajectory.points[i].positions.resize(onedof.size());
780+
joint_trajectory.points[i].velocities.reserve(onedof.size());
781+
782+
for (std::size_t j = 0; j < onedof.size(); ++j)
783+
{
784+
joint_trajectory.points[i].positions[j] = waypoint.getVariablePosition(onedof[j]->getFirstVariableIndex());
785+
// if we have velocities/accelerations/effort, copy those too
786+
if (waypoint.hasVelocities())
787+
{
788+
joint_trajectory.points[i].velocities.push_back(
789+
waypoint.getVariableVelocity(onedof[j]->getFirstVariableIndex()));
790+
}
791+
if (waypoint.hasAccelerations())
792+
{
793+
joint_trajectory.points[i].accelerations.push_back(
794+
waypoint.getVariableAcceleration(onedof[j]->getFirstVariableIndex()));
795+
}
796+
if (waypoint.hasEffort())
797+
{
798+
joint_trajectory.points[i].effort.push_back(waypoint.getVariableEffort(onedof[j]->getFirstVariableIndex()));
799+
}
800+
}
801+
// clear velocities if we have an incomplete specification
802+
if (joint_trajectory.points[i].velocities.size() != onedof.size())
803+
joint_trajectory.points[i].velocities.clear();
804+
// clear accelerations if we have an incomplete specification
805+
if (joint_trajectory.points[i].accelerations.size() != onedof.size())
806+
joint_trajectory.points[i].accelerations.clear();
807+
// clear effort if we have an incomplete specification
808+
if (joint_trajectory.points[i].effort.size() != onedof.size())
809+
joint_trajectory.points[i].effort.clear();
810+
}
811+
812+
if (!mdof.empty())
813+
{
814+
for (const auto joint : mdof)
815+
{
816+
// Add variable placeholders
817+
const std::vector<std::string> names = joint->getVariableNames();
818+
joint_trajectory.points[i].positions.reserve(joint_trajectory.points[i].positions.size() + names.size());
819+
820+
joint_trajectory.points[i].velocities.reserve(joint_trajectory.points[i].velocities.size() + names.size());
821+
822+
for (const auto& name : names)
823+
{
824+
joint_trajectory.points[i].positions.push_back(waypoint.getVariablePosition(name));
825+
826+
if (waypoint.hasVelocities())
827+
{
828+
joint_trajectory.points[i].velocities.push_back(waypoint.getVariableVelocity(name));
829+
}
830+
if (waypoint.hasAccelerations())
831+
{
832+
joint_trajectory.points[i].accelerations.push_back(waypoint.getVariableAcceleration(name));
833+
}
834+
}
835+
}
836+
}
837+
}
838+
839+
return joint_trajectory;
840+
}
841+
710842
} // end of namespace robot_trajectory

moveit_core/robot_trajectory/test/test_robot_trajectory.cpp

+32
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,38 @@ TEST_F(OneRobot, UnwindFromState)
634634
}
635635
}
636636

637+
TEST_F(OneRobot, MultiDofTrajectoryToJointStates)
638+
{
639+
// GIVEN a RobotTrajectory with two waypoints of a robot model that has a multi-dof base joint
640+
robot_trajectory::RobotTrajectory trajectory(robot_model_);
641+
trajectory.addSuffixWayPoint(robot_state_, 0.01 /* dt */);
642+
trajectory.addSuffixWayPoint(robot_state_, 0.01 /* dt */);
643+
644+
// WHEN converting the RobotTrajectory to a JointTrajectory message, including mdof variables
645+
auto maybe_trajectory_msg = toJointTrajectory(trajectory, true /* include_mdof_joints */);
646+
647+
// WHEN the optional trajectory result is valid (always assumed)
648+
ASSERT_TRUE(maybe_trajectory_msg.has_value());
649+
650+
const auto traj = maybe_trajectory_msg.value();
651+
const auto& joint_names = traj.joint_names;
652+
653+
size_t joint_variable_count = 0u;
654+
for (const auto& active_joint : robot_model_->getActiveJointModels())
655+
{
656+
joint_variable_count += active_joint->getVariableCount();
657+
}
658+
659+
// THEN all joints names should include the base joint variables
660+
EXPECT_EQ(joint_names.size(), joint_variable_count);
661+
EXPECT_TRUE(std::find(joint_names.begin(), joint_names.end(), "base_joint/x") != joint_names.end());
662+
// THEN the size of the trajectory should equal the input size
663+
ASSERT_EQ(traj.points.size(), 2u);
664+
// THEN all positions size should equal the variable size
665+
EXPECT_EQ(traj.points.at(0).positions.size(), joint_variable_count);
666+
EXPECT_EQ(traj.points.at(1).positions.size(), joint_variable_count);
667+
}
668+
637669
int main(int argc, char** argv)
638670
{
639671
testing::InitGoogleTest(&argc, argv);

moveit_ros/planning/trajectory_execution_manager/include/moveit/trajectory_execution_manager/trajectory_execution_manager.h

+1
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ class MOVEIT_TRAJECTORY_EXECUTION_MANAGER_EXPORT TrajectoryExecutionManager
333333
// 'global' values
334334
double allowed_execution_duration_scaling_;
335335
double allowed_goal_duration_margin_;
336+
bool control_multi_dof_joint_variables_;
336337
// controller-specific values
337338
// override the 'global' values
338339
std::map<std::string, double> controller_allowed_execution_duration_scaling_;

0 commit comments

Comments
 (0)