Skip to content

Commit

Permalink
Move some helper functions from iiwa to manipulation/util (RobotLocom…
Browse files Browse the repository at this point in the history
…otion#13015)

* Move some helper functions from iiwa to manipulation/util

These are items I've used in personal branches exploring planning/sim
with different robots that don't currently have drake examples (or at
least hadn't used this functionality yet).
  • Loading branch information
sammy-tri authored Apr 9, 2020
1 parent 92d8f2d commit 9c69017
Show file tree
Hide file tree
Showing 8 changed files with 312 additions and 67 deletions.
3 changes: 3 additions & 0 deletions examples/kuka_iiwa_arm/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ drake_cc_library(
hdrs = [
"iiwa_common.h",
],
copts = ["-Wno-deprecated-declarations"],
visibility = ["//visibility:public"],
deps = [
"//attic/multibody:rigid_body_tree",
"//attic/multibody/parsers",
"//common:find_resource",
"//common/trajectories:piecewise_polynomial",
"//manipulation/kuka_iiwa:iiwa_constants",
"//manipulation/util:robot_plan_utils",
"@lcmtypes_bot2_core",
"@lcmtypes_robotlocomotion",
],
Expand Down Expand Up @@ -209,6 +211,7 @@ drake_cc_binary(
"//common:add_text_logging_gflags",
"//lcmtypes:iiwa",
"//manipulation/planner:constraint_relaxing_ik",
"//manipulation/util:robot_plan_utils",
"//math:geometric_transform",
"//multibody/parsing",
"//multibody/plant",
Expand Down
42 changes: 7 additions & 35 deletions examples/kuka_iiwa_arm/iiwa_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "drake/common/find_resource.h"
#include "drake/common/text_logging.h"
#include "drake/common/trajectories/piecewise_polynomial.h"
#include "drake/manipulation/util/robot_plan_utils.h"
#include "drake/multibody/parsers/urdf_parser.h"
#include "drake/multibody/rigid_body_tree.h"
#include "drake/util/drakeGeometryUtil.h"
Expand Down Expand Up @@ -197,42 +198,13 @@ robotlocomotion::robot_plan_t EncodeKeyFrames(
const std::vector<double>& time,
const std::vector<int>& info,
const MatrixX<double>& keyframes) {

DRAKE_DEMAND(info.size() == time.size());
DRAKE_DEMAND(keyframes.cols() == static_cast<int>(time.size()));
DRAKE_DEMAND(keyframes.rows() == static_cast<int>(joint_names.size()));

const int num_time_steps = keyframes.cols();

robotlocomotion::robot_plan_t plan{};
plan.utime = 0; // I (sam.creasey) don't think this is used?
plan.robot_name = "iiwa"; // Arbitrary, probably ignored
plan.num_states = num_time_steps;
const bot_core::robot_state_t default_robot_state{};
plan.plan.resize(num_time_steps, default_robot_state);
plan.plan_info.resize(num_time_steps, 0);
/// Encode the q_sol returned for each timestep into the vector of
/// robot states.
for (int i = 0; i < num_time_steps; i++) {
bot_core::robot_state_t& step = plan.plan[i];
step.utime = time[i] * 1e6;
step.num_joints = keyframes.rows();
for (int j = 0; j < step.num_joints; j++) {
step.joint_name.push_back(joint_names[j]);
step.joint_position.push_back(keyframes(j, i));
step.joint_velocity.push_back(0);
step.joint_effort.push_back(0);
}
plan.plan_info[i] = info[i];
std::vector<Eigen::VectorXd> waypoints;
for (int i = 0; i < keyframes.cols(); ++i) {
waypoints.push_back(keyframes.col(i));
}
plan.num_grasp_transitions = 0;
plan.left_arm_control_type = plan.POSITION;
plan.right_arm_control_type = plan.NONE;
plan.left_leg_control_type = plan.NONE;
plan.right_leg_control_type = plan.NONE;
plan.num_bytes = 0;

return plan;

return manipulation::util::EncodeKeyFrames(
joint_names, time, info, waypoints);
}

} // namespace kuka_iiwa_arm
Expand Down
4 changes: 4 additions & 0 deletions examples/kuka_iiwa_arm/iiwa_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ void SetTorqueControlledIiwaGains(Eigen::VectorXd* stiffness,
/// The number of columns in @p keyframes must match the size of @p time. Times
/// must be in strictly increasing order.
/// @see get_iiwa_max_joint_velocities
DRAKE_DEPRECATED("2020-07-01",
"This function is being moved to manipulation::util.")
void ApplyJointVelocityLimits(const MatrixX<double>& keyframes,
std::vector<double>* time);

Expand All @@ -79,6 +81,8 @@ robotlocomotion::robot_plan_t EncodeKeyFrames(
/// keyframes must match the size of @p joint_names. The number of columns in
/// @p keyframes must match the size of @p time. Times must be in strictly
/// increasing order.
DRAKE_DEPRECATED("2020-07-01",
"This function is being moved to manipulation::util.")
robotlocomotion::robot_plan_t EncodeKeyFrames(
const std::vector<std::string>& joint_names,
const std::vector<double>& time, const std::vector<int>& info,
Expand Down
38 changes: 6 additions & 32 deletions examples/kuka_iiwa_arm/move_iiwa_ee.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "drake/examples/kuka_iiwa_arm/iiwa_common.h"
#include "drake/lcmt_iiwa_status.hpp"
#include "drake/manipulation/planner/constraint_relaxing_ik.h"
#include "drake/manipulation/util/robot_plan_utils.h"
#include "drake/math/rigid_transform.h"
#include "drake/math/roll_pitch_yaw.h"
#include "drake/math/rotation_matrix.h"
Expand Down Expand Up @@ -63,26 +64,7 @@ class MoveDemoRunner {
lcm_.subscribe(FLAGS_lcm_status_channel,
&MoveDemoRunner::HandleStatus, this);

// TODO(sammy-tri) Move this code somewhere more generic if we get a
// second example which wants to encode robot plans.
std::map<int, std::string> position_names;
const int num_positions = plant_.num_positions();
for (int i = 0; i < plant_.num_joints(); ++i) {
const multibody::Joint<double>& joint =
plant_.get_joint(multibody::JointIndex(i));
if (joint.num_positions() == 0) {
continue;
}
DRAKE_DEMAND(joint.num_positions() == 1);
DRAKE_DEMAND(joint.position_start() < num_positions);

position_names[joint.position_start()] = joint.name();
}

DRAKE_DEMAND(static_cast<int>(position_names.size()) == num_positions);
for (int i = 0; i < num_positions; ++i) {
joint_names_.push_back(position_names[i]);
}
joint_names_ = manipulation::util::GetJointNames(plant_);
}

void Run() {
Expand Down Expand Up @@ -148,20 +130,12 @@ class MoveDemoRunner {
std::vector<double> times{0, 2};
DRAKE_DEMAND(q_sol.size() == times.size());

// Convert the resulting waypoints into the format expected by
// ApplyJointVelocityLimits and EncodeKeyFrames.
MatrixX<double> q_mat(q_sol.front().size(), q_sol.size());
for (size_t i = 0; i < q_sol.size(); ++i) {
q_mat.col(i) = q_sol[i];
}

// Ensure that the planned motion would not exceed the joint
// velocity limits. This function will scale the supplied
// times if needed.
ApplyJointVelocityLimits(q_mat, &times);
manipulation::util::ApplyJointVelocityLimits(
q_sol, get_iiwa_max_joint_velocities() * 0.9, &times);
std::vector<int> info{1, 1};
robotlocomotion::robot_plan_t plan =
EncodeKeyFrames(joint_names_, times, info, q_mat);
manipulation::util::EncodeKeyFrames(
joint_names_, times, info, q_sol);
lcm_.publish(FLAGS_lcm_plan_channel, &plan);
}
}
Expand Down
26 changes: 26 additions & 0 deletions manipulation/util/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ drake_cc_package_library(
deps = [
":bot_core_lcm_encode_decode",
":moving_average_filter",
":robot_plan_utils",
":trajectory_utils",
],
)
Expand Down Expand Up @@ -51,6 +52,20 @@ drake_py_unittest(
],
)

drake_cc_library(
name = "robot_plan_utils",
srcs = [
"robot_plan_utils.cc",
],
hdrs = [
"robot_plan_utils.h",
],
deps = [
"//multibody/plant",
"@lcmtypes_robotlocomotion",
],
)

drake_cc_library(
name = "trajectory_utils",
srcs = [
Expand Down Expand Up @@ -95,6 +110,17 @@ drake_cc_googletest(
],
)

drake_cc_googletest(
name = "robot_plan_utils_test",
data = [
"//manipulation/models/iiwa_description:models",
],
deps = [
":robot_plan_utils",
"//multibody/parsing",
],
)

drake_cc_googletest(
name = "trajectory_utils_test",
deps = [
Expand Down
132 changes: 132 additions & 0 deletions manipulation/util/robot_plan_utils.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#include "drake/manipulation/util/robot_plan_utils.h"

#include <map>

#include "drake/common/default_scalars.h"
#include "drake/common/drake_assert.h"

namespace drake {
namespace manipulation {
namespace util {

/// @return A vector of joint names corresponding to the positions in @plant
/// in the order of the joint indices.
template <typename T>
std::vector<std::string> GetJointNames(
const multibody::MultibodyPlant<T>& plant) {

std::map<int, std::string> position_names;
const int num_positions = plant.num_positions();
for (int i = 0; i < plant.num_joints(); ++i) {
const multibody::Joint<T>& joint =
plant.get_joint(multibody::JointIndex(i));
if (joint.num_positions() == 0) {
continue;
}
DRAKE_DEMAND(joint.num_positions() == 1);
DRAKE_DEMAND(joint.position_start() < num_positions);

position_names[joint.position_start()] = joint.name();
}

DRAKE_DEMAND(static_cast<int>(position_names.size()) == num_positions);
std::vector<std::string> joint_names;
for (int i = 0; i < num_positions; ++i) {
joint_names.push_back(position_names[i]);
}
return joint_names;
}

void ApplyJointVelocityLimits(
const std::vector<Eigen::VectorXd>& keyframes,
const Eigen::VectorXd& limits,
std::vector<double>* times) {
DRAKE_DEMAND(keyframes.size() == times->size());
DRAKE_DEMAND(times->front() ==0);
const int num_time_steps = keyframes.size();

// Calculate a matrix of velocities between each timestep. We'll
// use this later to determine by how much the plan exceeds the
// joint velocity limits.
Eigen::MatrixXd velocities(limits.size(), num_time_steps - 1);
for (int i = 0; i < velocities.rows(); i++) {
for (int j = 0; j < velocities.cols(); j++) {
DRAKE_ASSERT((*times)[j + 1] > (*times)[j]);
velocities(i, j) =
std::abs((keyframes[j + 1](i) - keyframes[j](i)) /
((*times)[j + 1] - (*times)[j]));
}
}

Eigen::VectorXd velocity_ratios(velocities.rows());

for (int i = 0; i < velocities.rows(); i++) {
const double max_plan_velocity = velocities.row(i).maxCoeff();
velocity_ratios(i) = max_plan_velocity / limits(i);
}

const double max_velocity_ratio = velocity_ratios.maxCoeff();
if (max_velocity_ratio > 1) {
// The code below slows the entire plan such that the fastest step
// meets the limits. If that step is much faster than the others,
// the whole plan becomes very slow.
drake::log()->debug("Slowing plan by {}", max_velocity_ratio);
for (int j = 0; j < num_time_steps; j++) {
(*times)[j] *= max_velocity_ratio;
}
}
}

robotlocomotion::robot_plan_t EncodeKeyFrames(
const std::vector<std::string>& joint_names,
const std::vector<double>& times,
const std::vector<int>& info,
const std::vector<Eigen::VectorXd>& keyframes) {
DRAKE_DEMAND(info.size() == times.size());
DRAKE_DEMAND(keyframes.size() == times.size());

const int num_time_steps = keyframes.size();

robotlocomotion::robot_plan_t plan{};
plan.utime = 0; // I (sam.creasey) don't think this is used?
plan.robot_name = ""; // Arbitrary, probably ignored
plan.num_states = num_time_steps;
const bot_core::robot_state_t default_robot_state{};
plan.plan.resize(num_time_steps, default_robot_state);
plan.plan_info.resize(num_time_steps, 0);
/// Encode the q_sol returned for each timestep into the vector of
/// robot states.
for (int i = 0; i < num_time_steps; i++) {
DRAKE_DEMAND(keyframes[i].size() == static_cast<int>(joint_names.size()));
bot_core::robot_state_t& step = plan.plan[i];
step.utime = times[i] * 1e6;
step.num_joints = keyframes[i].size();
for (int j = 0; j < step.num_joints; j++) {
step.joint_name.push_back(joint_names[j]);
step.joint_position.push_back(keyframes[i](j));
step.joint_velocity.push_back(0);
step.joint_effort.push_back(0);
}
plan.plan_info[i] = info[i];
}
plan.num_grasp_transitions = 0;
plan.left_arm_control_type = plan.POSITION;
plan.right_arm_control_type = plan.NONE;
plan.left_leg_control_type = plan.NONE;
plan.right_leg_control_type = plan.NONE;
plan.num_bytes = 0;

return plan;
}


} // namespace util
} // namespace manipulation
} // namespace drake

template std::vector<std::string> drake::manipulation::util::GetJointNames(
const drake::multibody::MultibodyPlant<double>&);
template std::vector<std::string> drake::manipulation::util::GetJointNames(
const drake::multibody::MultibodyPlant<::drake::AutoDiffXd>&);
template std::vector<std::string> drake::manipulation::util::GetJointNames(
const drake::multibody::MultibodyPlant<::drake::symbolic::Expression>&);
50 changes: 50 additions & 0 deletions manipulation/util/robot_plan_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#pragma once

/// @file Functions to help with the creation of robot_plan_t messages.

#include <string>
#include <vector>

#include "robotlocomotion/robot_plan_t.hpp"

#include "drake/multibody/plant/multibody_plant.h"

namespace drake {
namespace manipulation {
namespace util {

/// @return A vector of joint names corresponding to the positions in @p plant
/// in the order of the joint indices. If joints with duplicate names exist
/// in different model instance in the plant, the names will be duplicated in
/// the output.
template <typename T>
std::vector<std::string> GetJointNames(
const multibody::MultibodyPlant<T>& plant);

/// Scales a plan so that no step exceeds the robot's maximum joint
/// velocities. The size of @p keyframes must match the size of @p times.
/// Times must be in strictly increasing order and start with zero. Per-joint
/// velocity limits are specified by @p limits, which much be the same size ad
/// the number of joints in each element of @p keyframes. Assumes that
/// velocity limits are equal regardless of direction. If any step does
/// exceed the maximum velocities in @p limits, @p times will be modified to
/// reduce the velocity.
void ApplyJointVelocityLimits(
const std::vector<Eigen::VectorXd>& keyframes,
const Eigen::VectorXd& limits,
std::vector<double>* times);

/// Makes a robotlocomotion::robot_plan_t message. The entries in @p
/// joint_names should be unique, though the behavior if names are duplicated
/// depends on how the returned plan is evaluated. The size of each vector in
/// @p keyframes must match the size of @p joint_names. The size of @p
/// keyframes must match the size of @p times. Times must be in strictly
/// increasing order.
robotlocomotion::robot_plan_t EncodeKeyFrames(
const std::vector<std::string>& joint_names,
const std::vector<double>& times, const std::vector<int>& info,
const std::vector<Eigen::VectorXd>& keyframes);

} // namespace util
} // namespace manipulation
} // namespace drake
Loading

0 comments on commit 9c69017

Please sign in to comment.