- DOF(Degree Of Freedom)。自由度。
Ros用moveit_msgs::RobotTrajectory封装发向机器人的路径控制,对当中每个路点,表示它的有关节状态(positions)、相对于起始路点的时间戳(time_from_start)等。
C:\Windows\System32>rosmsg show moveit_msgs/RobotTrajectory trajectory_msgs/JointTrajectory joint_trajectory std_msgs/Header header uint32 seq time stamp string frame_id string[] joint_names trajectory_msgs/JointTrajectoryPoint[] points float64[] positions float64[] velocities float64[] accelerations float64[] effort duration time_from_start trajectory_msgs/MultiDOFJointTrajectory multi_dof_joint_trajectory std_msgs/Header header uint32 seq time stamp string frame_id string[] joint_names trajectory_msgs/MultiDOFJointTrajectoryPoint[] points geometry_msgs/Transform[] transforms geometry_msgs/Vector3 translation float64 x float64 y float64 z geometry_msgs/Quaternion rotation float64 x float64 y float64 z float64 w geometry_msgs/Twist[] velocities geometry_msgs/Vector3 linear float64 x float64 y float64 z geometry_msgs/Vector3 angular float64 x float64 y float64 z geometry_msgs/Twist[] accelerations geometry_msgs/Vector3 linear float64 x float64 y float64 z geometry_msgs/Vector3 angular float64 x float64 y float64 z duration time_from_start
moveit_msgs::RobotTrajectory就是trajectory_msgs::JointTrajectory和trajectory_msgs::MultiDOFJointTrajector的struct,JointTrajectory发向机器人中那些单自由度关节,MultiDOFJointTrajector则是多自由度关节。
一、robot_trajectory::RobotTrajectory转成moveit_msgs::RobotTrajectory
- @trajectory[OUT]。要转换出的moveit_msgs::RobotTrajectory类型的路径轨迹。
- @joint_filter[IN]。额外的关节值筛选器。当不是空时,存储时认为有效的关节名称,像a、b、c。路径中要关节需要是它们中的一种,它会认为有效,才会加入要生成的路径。默认空。
void RobotTrajectory::getRobotTrajectoryMsg(moveit_msgs::RobotTrajectory& trajectory,
const std::vector<std::string>& joint_filter) const
{
trajectory = moveit_msgs::RobotTrajectory();
if (waypoints_.empty())
return;
const std::vector<const moveit::core::JointModel*>& jnts =
group_ ? group_->getActiveJointModels() : robot_model_->getActiveJointModels();
std::vector<const moveit::core::JointModel*> onedof;
std::vector<const moveit::core::JointModel*> mdof;
trajectory.joint_trajectory.joint_names.clear();
trajectory.multi_dof_joint_trajectory.joint_names.clear();
for (const moveit::core::JointModel* active_joint : jnts)
{
// only consider joints listed in joint_filter
if (!joint_filter.empty() &&
std::find(joint_filter.begin(), joint_filter.end(), active_joint->getName()) == joint_filter.end())
continue;
if (active_joint->getVariableCount() == 1)
{
trajectory.joint_trajectory.joint_names.push_back(active_joint->getName());
onedof.push_back(active_joint);
}
else
{
trajectory.multi_dof_joint_trajectory.joint_names.push_back(active_joint->getName());
mdof.push_back(active_joint);
}
}
if (!onedof.empty())
{
trajectory.joint_trajectory.header.frame_id = robot_model_->getModelFrame();
trajectory.joint_trajectory.header.stamp = ros::Time(0);
trajectory.joint_trajectory.points.resize(waypoints_.size());
}
if (!mdof.empty())
{
trajectory.multi_dof_joint_trajectory.header.frame_id = robot_model_->getModelFrame();
trajectory.multi_dof_joint_trajectory.header.stamp = ros::Time(0);
trajectory.multi_dof_joint_trajectory.points.resize(waypoints_.size());
}
onedof:存储着此个规划组中单自由度关节信息。示例中有3个。
mdof:存储着此此个规划组多自由度关节信息。示例中有0个。
static const ros::Duration ZERO_DURATION(0.0);
double total_time = 0.0;
for (std::size_t i = 0; i < waypoints_.size(); ++i)
{
if (duration_from_previous_.size() > i)
total_time += duration_from_previous_[i];
if (!onedof.empty())
{
trajectory.joint_trajectory.points[i].positions.resize(onedof.size());
trajectory.joint_trajectory.points[i].velocities.reserve(onedof.size());
for (std::size_t j = 0; j < onedof.size(); ++j)
{
trajectory.joint_trajectory.points[i].positions[j] =
waypoints_[i]->getVariablePosition(onedof[j]->getFirstVariableIndex());
// if we have velocities/accelerations/effort, copy those too
if (waypoints_[i]->hasVelocities())
trajectory.joint_trajectory.points[i].velocities.push_back(
waypoints_[i]->getVariableVelocity(onedof[j]->getFirstVariableIndex()));
if (waypoints_[i]->hasAccelerations())
trajectory.joint_trajectory.points[i].accelerations.push_back(
waypoints_[i]->getVariableAcceleration(onedof[j]->getFirstVariableIndex()));
if (waypoints_[i]->hasEffort())
trajectory.joint_trajectory.points[i].effort.push_back(
waypoints_[i]->getVariableEffort(onedof[j]->getFirstVariableIndex()));
}
// clear velocities if we have an incomplete specification
if (trajectory.joint_trajectory.points[i].velocities.size() != onedof.size())
trajectory.joint_trajectory.points[i].velocities.clear();
// clear accelerations if we have an incomplete specification
if (trajectory.joint_trajectory.points[i].accelerations.size() != onedof.size())
trajectory.joint_trajectory.points[i].accelerations.clear();
// clear effort if we have an incomplete specification
if (trajectory.joint_trajectory.points[i].effort.size() != onedof.size())
trajectory.joint_trajectory.points[i].effort.clear();
if (duration_from_previous_.size() > i)
trajectory.joint_trajectory.points[i].time_from_start = ros::Duration(total_time);
else
trajectory.joint_trajectory.points[i].time_from_start = ZERO_DURATION;
SDL_Log("[%i/%i]{onedof}, duration_from_previous_[%i/%i]: %.5f, total_time: %.5f, time_from_start: %.5f, positions: %s",
(int)i, (int)waypoints_.size() - 1, (int)i, (int)duration_from_previous_.size() - 1, duration_from_previous_[i], total_time,
trajectory.joint_trajectory.points[i].time_from_start.toSec(), vector_double_DebugString(trajectory.joint_trajectory.points[i].positions).c_str());为理解,增加了SDL_log,底下有对图1的一次运行时输出。
}
if (!mdof.empty())
{
trajectory.multi_dof_joint_trajectory.points[i].transforms.resize(mdof.size());
for (std::size_t j = 0; j < mdof.size(); ++j)
{
geometry_msgs::TransformStamped ts = tf2::eigenToTransform(waypoints_[i]->getJointTransform(mdof[j]));
trajectory.multi_dof_joint_trajectory.points[i].transforms[j] = ts.transform;
// TODO: currently only checking for planar multi DOF joints / need to add check for floating
if (waypoints_[i]->hasVelocities() && (mdof[j]->getType() == moveit::core::JointModel::JointType::PLANAR))
{
const std::vector<std::string> names = mdof[j]->getVariableNames();
const double* velocities = waypoints_[i]->getJointVelocities(mdof[j]);
geometry_msgs::Twist point_velocity;
for (std::size_t k = 0; k < names.size(); ++k)
{
if (names[k].find("/x") != std::string::npos)
{
point_velocity.linear.x = velocities[k];
}
else if (names[k].find("/y") != std::string::npos)
{
point_velocity.linear.y = velocities[k];
}
else if (names[k].find("/z") != std::string::npos)
{
point_velocity.linear.z = velocities[k];
}
else if (names[k].find("/theta") != std::string::npos)
{
point_velocity.angular.z = velocities[k];
}
}
trajectory.multi_dof_joint_trajectory.points[i].velocities.push_back(point_velocity);
}
}
if (duration_from_previous_.size() > i)
trajectory.multi_dof_joint_trajectory.points[i].time_from_start = ros::Duration(total_time);
else
trajectory.multi_dof_joint_trajectory.points[i].time_from_start = ZERO_DURATION;
}
}
}a、b、c三个单自由度关节,图1显示了此时相关变量值。
[0/41]{onedof}, duration_from_previous_[0/41]: 0, total_time: 0, time_from_start: 0, positions: (0, 0, 0)
[1/41]{onedof}, duration_from_previous_[1/41]: 0.02009, total_time: 0.02009, time_from_start: 0.02009, positions: (4.71548e-05, 5.65878e-05, -6.60183e-05)
[2/41]{onedof}, duration_from_previous_[2/41]: 0.18117, total_time: 0.20127, time_from_start: 0.20127, positions: (0.0128202, 0.0153849, -0.0179488)
[3/41]{onedof}, duration_from_previous_[3/41]: 0.07927, total_time: 0.28054, time_from_start: 0.28054, positions: (0.0256405, 0.0307697, -0.0358975)
[4/41]{onedof}, duration_from_previous_[4/41]: 0.06426, total_time: 0.34481, time_from_start: 0.34481, positions: (0.0384607, 0.0461546, -0.0538463)
[5/41]{onedof}, duration_from_previous_[5/41]: 0.07131, total_time: 0.41612, time_from_start: 0.41612, positions: (0.051281, 0.0615394, -0.0717951)
...
[37/41]{onedof}, duration_from_previous_[37/41]: 0.07131, total_time: 2.98264, time_from_start: 2.98264, positions: (0.461529, 0.553855, -0.646156)
[38/41]{onedof}, duration_from_previous_[38/41]: 0.06426, total_time: 3.04690, time_from_start: 3.04690, positions: (0.474349, 0.56924, -0.664105)
[39/41]{onedof}, duration_from_previous_[39/41]: 0.07927, total_time: 3.12618, time_from_start: 3.12618, positions: (0.487169, 0.584625, -0.682053)
[40/41]{onedof}, duration_from_previous_[40/41]: 0.18117, total_time: 3.30735, time_from_start: 3.30735, positions: (0.499942, 0.599953, -0.699936)
[41/41]{onedof}, duration_from_previous_[41/41]: 0.02009, total_time: 3.32745, time_from_start: 3.32745, positions: (0.499989, 0.600009, -0.700002)对生成的time_from_start,值等于total_time,
二、moveit_msgs::RobotTrajectory发向机器人
moveit四大插件之一的控制器(MoveItControllerManager)负责把moveit_msgs::RobotTrajectory发向机器人。这里以默认的控制器插件MoveItFakeControllerManager为例,它支持三种方式,先择当中的ViaPointController。
- ViaPointController。每个点有个time_from_start字段,指示该point要发送到机器人的时刻。选择这种时,时间必须已到这个时刻,才把原值为作为js发送向关节。
void ViaPointController::execTrajectory(const moveit_msgs::RobotTrajectory& t)
{
ROS_INFO("Fake execution of trajectory");
sensor_msgs::JointState js;
js.header = t.joint_trajectory.header;
js.name = t.joint_trajectory.joint_names;
// publish joint states for all intermediate via points of the trajectory
// no further interpolation
int at = 0;
ros::Time start_time = ros::Time::now();start_time是发送零路点的真实时刻,“ros::Time::now() - start_time”指现在离零路点真实过了多少时间,“理论应该相隔的时间-实际时间相隔了时间”wait_time就是希望再等待时间。
for (std::vector<trajectory_msgs::JointTrajectoryPoint>::const_iterator via = t.joint_trajectory.points.begin(),
end = t.joint_trajectory.points.end();
!cancelled() && via != end; ++via)
{
js.position = via->positions;
js.velocity = via->velocities;
js.effort = via->effort;
ros::Duration wait_time = via->time_from_start - (ros::Time::now() - start_time);
if (wait_time.toSec() > std::numeric_limits<float>::epsilon())
{
ROS_INFO("Fake execution: waiting %0.5fs for next via point, %ld remaining", wait_time.toSec(), end - via);
wait_time.sleep();
}
js.header.stamp = ros::Time::now();
ROS_INFO("[%i/%i]via->time_from_start: %.5f, pre pub_.publish(%s)",
at ++, (int)t.joint_trajectory.points.size() - 1, via->time_from_start.toSec(), vector_double_DebugString(js.position).c_str());
pub_.publish(js);
}
ROS_DEBUG("Fake execution of trajectory: done");
}执行示例后的输出日志。
[1653911981.900536500]: Fake execution of trajectory [1653911981.900794100]: [0/41]via->time_from_start: 0.00000, pre pub_.publish((0, 0, 0)) [1653911981.900984700]: Fake execution: waiting 0.01990s for next via point, 41 remaining [1653911981.921961500]: [1/41]via->time_from_start: 0.02010, pre pub_.publish((4.71548e-05, 5.65878e-05, -6.60183e-05)) [1653911981.922242000]: Fake execution: waiting 0.17981s for next via point, 40 remaining [1653911982.095091900]: scene update 2.09506 robot stamp: 2.09449 [1653911982.102515300]: [2/41]via->time_from_start: 0.20127, pre pub_.publish((0.0128202, 0.0153849, -0.0179488)) [1653911982.102970500]: Fake execution: waiting 0.07838s for next via point, 39 remaining [1653911982.183268800]: [3/41]via->time_from_start: 0.28055, pre pub_.publish((0.0256405, 0.0307697, -0.0358975)) [1653911982.183629300]: Fake execution: waiting 0.06197s for next via point, 38 remaining [1653911982.246102600]: [4/41]via->time_from_start: 0.34481, pre pub_.publish((0.0384607, 0.0461546, -0.0538463)) [1653911982.246325800]: Fake execution: waiting 0.07059s for next via point, 37 remaining [1653911982.296290700]: scene update 2.29626 robot stamp: 2.29493 [1653911982.317920100]: [5/41]via->time_from_start: 0.41613, pre pub_.publish((0.051281, 0.0615394, -0.0717951)) [1653911982.318311000]: Fake execution: waiting 0.08092s for next via point, 36 remaining ... [1653911984.813651100]: Fake execution: waiting 0.06977s for next via point, 5 remaining [1653911984.884144800]: [37/41]via->time_from_start: 2.98264, pre pub_.publish((0.461529, 0.553855, -0.646156)) [1653911984.884487100]: Fake execution: waiting 0.06320s for next via point, 4 remaining [1653911984.949513200]: [38/41]via->time_from_start: 3.04691, pre pub_.publish((0.474349, 0.56924, -0.664105)) [1653911984.949882000]: Fake execution: waiting 0.07708s for next via point, 3 remaining [1653911985.028742400]: [39/41]via->time_from_start: 3.12618, pre pub_.publish((0.487169, 0.584625, -0.682053)) [1653911985.029164100]: Fake execution: waiting 0.17897s for next via point, 2 remaining [1653911985.051992700]: scene update 5.05192 robot stamp: 4.99531 [1653911985.209224100]: [40/41]via->time_from_start: 3.30736, pre pub_.publish((0.499942, 0.599953, -0.699936)) [1653911985.209493500]: Fake execution: waiting 0.01874s for next via point, 1 remaining [1653911985.229174200]: [41/41]via->time_from_start: 3.32745, pre pub_.publish((0.499989, 0.600009, -0.700002))
按规则,最后一个路点[41]发布时刻应该约等于发布[0]路点时刻(1653911981.900794100)加上路点[41]的time_from_start(3.32745)。