Skip to content

Commit

Permalink
Add PointToPointDistanceConstraint. (RobotLocomotion#12896)
Browse files Browse the repository at this point in the history
Add PointToPointDistanceConstraint.
  • Loading branch information
hongkai-dai authored Mar 19, 2020
1 parent 2753ecd commit 6ee9963
Show file tree
Hide file tree
Showing 10 changed files with 496 additions and 4 deletions.
48 changes: 48 additions & 0 deletions bindings/pydrake/multibody/inverse_kinematics_py.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "drake/multibody/inverse_kinematics/inverse_kinematics.h"
#include "drake/multibody/inverse_kinematics/minimum_distance_constraint.h"
#include "drake/multibody/inverse_kinematics/orientation_constraint.h"
#include "drake/multibody/inverse_kinematics/point_to_point_distance_constraint.h"
#include "drake/multibody/inverse_kinematics/position_constraint.h"

namespace drake {
Expand Down Expand Up @@ -75,6 +76,11 @@ PYBIND11_MODULE(inverse_kinematics, m) {
.def("AddDistanceConstraint", &Class::AddDistanceConstraint,
py::arg("geometry_pair"), py::arg("distance_lower"),
py::arg("distance_upper"), cls_doc.AddDistanceConstraint.doc)
.def("AddPointToPointDistanceConstraint",
&Class::AddPointToPointDistanceConstraint, py::arg("frame1"),
py::arg("p_B1P1"), py::arg("frame2"), py::arg("p_B2P2"),
py::arg("distance_lower"), py::arg("distance_upper"),
cls_doc.AddPointToPointDistanceConstraint.doc)
.def("q", &Class::q, cls_doc.q.doc)
.def("prog", &Class::prog, py_reference_internal, cls_doc.prog.doc)
.def("get_mutable_prog", &Class::get_mutable_prog,
Expand Down Expand Up @@ -125,6 +131,48 @@ PYBIND11_MODULE(inverse_kinematics, m) {
// Keep alive, reference: `self` keeps `plant_context` alive.
py::keep_alive<1, 9>(), ctor_doc_ad);
}
{
using Class = PointToPointDistanceConstraint;
constexpr auto& cls_doc = doc.PointToPointDistanceConstraint;
using Ptr = std::shared_ptr<Class>;
py::class_<Class, Constraint, Ptr>(
m, "PointToPointDistanceConstraint", cls_doc.doc)
.def(py::init([](const multibody::MultibodyPlant<double>* const plant,
const multibody::Frame<double>& frame1,
const Eigen::Ref<const Eigen::Vector3d>& p_B1P1,
const multibody::Frame<double>& frame2,
const Eigen::Ref<const Eigen::Vector3d>& p_B2P2,
double distance_lower, double distance_upper,
systems::Context<double>* plant_context) {
return std::make_shared<Class>(plant, frame1, p_B1P1, frame2, p_B2P2,
distance_lower, distance_upper, plant_context);
}),
py::arg("plant"), py::arg("frame1"), py::arg("p_B1P1"),
py::arg("frame2"), py::arg("p_B2P2"), py::arg("distance_lower"),
py::arg("distance_upper"), py::arg("plant_context"),
// Keep alive, reference: `self` keeps `plant` alive.
py::keep_alive<1, 2>(),
// Keep alive, reference: `self` keeps `plant_context` alive.
py::keep_alive<1, 9>(), cls_doc.ctor.doc)
.def(py::init(
[](const multibody::MultibodyPlant<AutoDiffXd>* const plant,
const multibody::Frame<AutoDiffXd>& frame1,
const Eigen::Ref<const Eigen::Vector3d>& p_B1P1,
const multibody::Frame<AutoDiffXd>& frame2,
const Eigen::Ref<const Eigen::Vector3d>& p_B2P2,
double distance_lower, double distance_upper,
systems::Context<AutoDiffXd>* plant_context) {
return std::make_shared<Class>(plant, frame1, p_B1P1, frame2,
p_B2P2, distance_lower, distance_upper, plant_context);
}),
py::arg("plant"), py::arg("frame1"), py::arg("p_B1P1"),
py::arg("frame2"), py::arg("p_B2P2"), py::arg("distance_lower"),
py::arg("distance_upper"), py::arg("plant_context"),
// Keep alive, reference: `self` keeps `plant` alive.
py::keep_alive<1, 2>(),
// Keep alive, reference: `self` keeps `plant_context` alive.
py::keep_alive<1, 9>(), ctor_doc_ad);
}
{
using Class = DistanceConstraint;
constexpr auto& cls_doc = doc.DistanceConstraint;
Expand Down
41 changes: 41 additions & 0 deletions bindings/pydrake/multibody/test/inverse_kinematics_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,37 @@ def test_AddAngleBetweenVectorsConstraint(self):
self.assertTrue(result.is_success())
self.assertTrue(np.allclose(result.GetSolution(self.q), q_val))

def test_AddPointToPointDistanceConstraint(self):
p_B1P1 = np.array([0.2, -0.4, 0.9])
p_B2P2 = np.array([1.4, -0.1, 1.8])

distance_lower = 0.1
distance_upper = 0.2

self.ik_two_bodies.AddPointToPointDistanceConstraint(
frame1=self.body1_frame, p_B1P1=p_B1P1,
frame2=self.body2_frame, p_B2P2=p_B2P2,
distance_lower=distance_lower, distance_upper=distance_upper)
result = mp.Solve(self.prog)
self.assertTrue(result.is_success())

q_val = result.GetSolution(self.q)
body1_quat = self._body1_quat(q_val)
body2_quat = self._body2_quat(q_val)
body1_rotmat = Quaternion(body1_quat).rotation()
body2_rotmat = Quaternion(body2_quat).rotation()

p_WP1 = self._body1_xyz(q_val) + body1_rotmat.dot(p_B1P1)
p_WP2 = self._body2_xyz(q_val) + body2_rotmat.dot(p_B2P2)
distance = np.linalg.norm(p_WP1 - p_WP2)

self.assertLess(distance, distance_upper + 3e-6)
self.assertGreater(distance, distance_lower - 3e-6)

result = mp.Solve(self.prog)
self.assertTrue(result.is_success())
self.assertTrue(np.allclose(result.GetSolution(self.q), q_val))

def test_AddMinimumDistanceConstraint(self):
ik = self.ik_two_bodies
W = self.plant.world_frame()
Expand Down Expand Up @@ -426,6 +457,16 @@ def test_orientation_constraint(self, variables):
theta_bound=0.2 * math.pi, plant_context=variables.plant_context)
self.assertIsInstance(constraint, mp.Constraint)

@check_type_variables
def test_point_to_point_distance_constraint(self, variables):
constraint = ik.PointToPointDistanceConstraint(
plant=variables.plant,
frame1=variables.body1_frame, p_B1P1=[0.1, 0.2, 0.3],
frame2=variables.body2_frame, p_B2P2=[0.3, 0.4, 0.5],
distance_lower=0.1, distance_upper=0.2,
plant_context=variables.plant_context)
self.assertIsInstance(constraint, mp.Constraint)

@check_type_variables
def test_distance_constraint(self, variables):
inspector = self.scene_graph_f.model_inspector()
Expand Down
11 changes: 11 additions & 0 deletions multibody/inverse_kinematics/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ drake_cc_library(
"kinematic_constraint_utilities.cc",
"minimum_distance_constraint.cc",
"orientation_constraint.cc",
"point_to_point_distance_constraint.cc",
"position_constraint.cc",
],
hdrs = [
Expand All @@ -41,6 +42,7 @@ drake_cc_library(
"kinematic_constraint_utilities.h",
"minimum_distance_constraint.h",
"orientation_constraint.h",
"point_to_point_distance_constraint.h",
"position_constraint.h",
],
deps = [
Expand Down Expand Up @@ -143,6 +145,15 @@ drake_cc_googletest(
],
)

drake_cc_googletest(
name = "point_to_point_distance_constraint_test",
deps = [
":inverse_kinematics_test_utilities",
":kinematic_constraint",
"//solvers/test_utilities",
],
)

drake_cc_googletest(
name = "inverse_kinematics_test",
# TODO(betsymcphail) Ubsan fails for this test. See #9475.
Expand Down
14 changes: 14 additions & 0 deletions multibody/inverse_kinematics/inverse_kinematics.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "drake/multibody/inverse_kinematics/gaze_target_constraint.h"
#include "drake/multibody/inverse_kinematics/minimum_distance_constraint.h"
#include "drake/multibody/inverse_kinematics/orientation_constraint.h"
#include "drake/multibody/inverse_kinematics/point_to_point_distance_constraint.h"
#include "drake/multibody/inverse_kinematics/position_constraint.h"

namespace drake {
Expand Down Expand Up @@ -105,5 +106,18 @@ solvers::Binding<solvers::Constraint> InverseKinematics::AddDistanceConstraint(
distance_upper);
return prog_->AddConstraint(constraint, q_);
}

solvers::Binding<solvers::Constraint>
InverseKinematics::AddPointToPointDistanceConstraint(
const Frame<double>& frame1,
const Eigen::Ref<const Eigen::Vector3d>& p_B1P1,
const Frame<double>& frame2,
const Eigen::Ref<const Eigen::Vector3d>& p_B2P2, double distance_lower,
double distance_upper) {
auto constraint = std::make_shared<PointToPointDistanceConstraint>(
&plant_, frame1, p_B1P1, frame2, p_B2P2, distance_lower, distance_upper,
get_mutable_context());
return prog_->AddConstraint(constraint, q_);
}
} // namespace multibody
} // namespace drake
18 changes: 18 additions & 0 deletions multibody/inverse_kinematics/inverse_kinematics.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,24 @@ class InverseKinematics {
const SortedPair<geometry::GeometryId>& geometry_pair,
double distance_lower, double distance_upper);

/**
* Add a constraint that the distance between point P1 attached to frame 1 and
* point P2 attached to frame 2 is within the range [distance_lower,
* distance_upper].
* @param frame1 The frame to which P1 is attached.
* @param p_B1P1 The position of P1 measured and expressed in frame 1.
* @param frame2 The frame to which P2 is attached.
* @param p_B2P2 The position of P2 measured and expressed in frame 2.
* @param distance_lower The lower bound on the distance.
* @param distance_upper The upper bound on the distance.
*/
solvers::Binding<solvers::Constraint> AddPointToPointDistanceConstraint(
const Frame<double>& frame1,
const Eigen::Ref<const Eigen::Vector3d>& p_B1P1,
const Frame<double>& frame2,
const Eigen::Ref<const Eigen::Vector3d>& p_B2P2, double distance_lower,
double distance_upper);

/** Getter for q. q is the decision variable for the generalized positions of
* the robot. */
const solvers::VectorXDecisionVariable& q() const { return q_; }
Expand Down
124 changes: 124 additions & 0 deletions multibody/inverse_kinematics/point_to_point_distance_constraint.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include "drake/multibody/inverse_kinematics/point_to_point_distance_constraint.h"

#include "drake/math/autodiff_gradient.h"
#include "drake/multibody/inverse_kinematics/kinematic_constraint_utilities.h"

using drake::multibody::internal::RefFromPtrOrThrow;
using drake::multibody::internal::UpdateContextConfiguration;

namespace drake {
namespace multibody {
PointToPointDistanceConstraint::PointToPointDistanceConstraint(
const MultibodyPlant<double>* const plant, const Frame<double>& frame1,
const Eigen::Ref<const Eigen::Vector3d>& p_B1P1,
const Frame<double>& frame2,
const Eigen::Ref<const Eigen::Vector3d>& p_B2P2, double distance_lower,
double distance_upper, systems::Context<double>* plant_context)
: solvers::Constraint(1, RefFromPtrOrThrow(plant).num_positions(),
Vector1d(distance_lower * distance_lower),
Vector1d(distance_upper * distance_upper)),
plant_double_(plant),
frame1_index_(frame1.index()),
frame2_index_(frame2.index()),
p_B1P1_(p_B1P1),
p_B2P2_(p_B2P2),
context_double_(plant_context),
plant_autodiff_(nullptr),
context_autodiff_(nullptr) {
if (plant_context == nullptr) {
throw std::invalid_argument("plant_context is nullptr");
}
DRAKE_DEMAND(distance_lower >= 0);
DRAKE_DEMAND(distance_upper >= distance_lower);
}

PointToPointDistanceConstraint::PointToPointDistanceConstraint(
const MultibodyPlant<AutoDiffXd>* const plant,
const Frame<AutoDiffXd>& frame1,
const Eigen::Ref<const Eigen::Vector3d>& p_B1P1,
const Frame<AutoDiffXd>& frame2,
const Eigen::Ref<const Eigen::Vector3d>& p_B2P2, double distance_lower,
double distance_upper, systems::Context<AutoDiffXd>* plant_context)
: solvers::Constraint(1, RefFromPtrOrThrow(plant).num_positions(),
Vector1d(distance_lower * distance_lower),
Vector1d(distance_upper * distance_upper)),
plant_double_(nullptr),
frame1_index_(frame1.index()),
frame2_index_(frame2.index()),
p_B1P1_(p_B1P1),
p_B2P2_(p_B2P2),
context_double_{nullptr},
plant_autodiff_(plant),
context_autodiff_(plant_context) {
if (plant_context == nullptr) {
throw std::invalid_argument("plant_context is nullptr");
}
DRAKE_DEMAND(distance_lower >= 0);
DRAKE_DEMAND(distance_upper >= distance_lower);
}

template <typename T>
void EvalPointToPointDistanceConstraintGradient(
const systems::Context<T>&, const MultibodyPlant<T>&, const Frame<T>&,
const Frame<T>&, const Eigen::Vector3d&, const Vector3<T>& p_P1P2_B1,
const Eigen::Ref<const VectorX<T>>&, VectorX<T>* y) {
(*y)(0) = p_P1P2_B1.squaredNorm();
}

void EvalPointToPointDistanceConstraintGradient(
const systems::Context<double>& context,
const MultibodyPlant<double>& plant, const Frame<double>& frame1,
const Frame<double>& frame2, const Eigen::Vector3d& p_B2P2,
const Eigen::Vector3d& p_P1P2_B1, const Eigen::Ref<const AutoDiffVecXd>& x,
AutoDiffVecXd* y) {
Eigen::MatrixXd Jq_V_B1P2(6, plant.num_positions());
plant.CalcJacobianSpatialVelocity(context, JacobianWrtVariable::kQDot, frame2,
p_B2P2, frame1, frame1, &Jq_V_B1P2);
*y = math::initializeAutoDiffGivenGradientMatrix(
Vector1d(p_P1P2_B1.squaredNorm()), 2 * p_P1P2_B1.transpose() *
Jq_V_B1P2.bottomRows<3>() *
math::autoDiffToGradientMatrix(x));
}

template <typename T, typename S>
void DoEvalGeneric(const MultibodyPlant<T>& plant, systems::Context<T>* context,
const FrameIndex frame1_index, const Eigen::Vector3d& p_B1P1,
const FrameIndex frame2_index, const Eigen::Vector3d& p_B2P2,
const Eigen::Ref<const VectorX<S>>& x, VectorX<S>* y) {
y->resize(1);
UpdateContextConfiguration(context, plant, x);
const Frame<T>& frame1 = plant.get_frame(frame1_index);
const Frame<T>& frame2 = plant.get_frame(frame2_index);
// Compute p_B1P2
Vector3<T> p_B1P2;
plant.CalcPointsPositions(*context, frame2, p_B2P2.cast<T>(), frame1,
&p_B1P2);
const Vector3<T> p_P1P2_B1 = p_B1P2 - p_B1P1;
EvalPointToPointDistanceConstraintGradient(*context, plant, frame1, frame2,
p_B2P2, p_P1P2_B1, x, y);
}

void PointToPointDistanceConstraint::DoEval(
const Eigen::Ref<const Eigen::VectorXd>& x, Eigen::VectorXd* y) const {
if (use_autodiff()) {
AutoDiffVecXd y_t;
Eval(math::initializeAutoDiff(x), &y_t);
*y = math::autoDiffToValueMatrix(y_t);
} else {
DoEvalGeneric(*plant_double_, context_double_, frame1_index_, p_B1P1_,
frame2_index_, p_B2P2_, x, y);
}
}

void PointToPointDistanceConstraint::DoEval(
const Eigen::Ref<const AutoDiffVecXd>& x, AutoDiffVecXd* y) const {
if (use_autodiff()) {
DoEvalGeneric(*plant_autodiff_, context_autodiff_, frame1_index_, p_B1P1_,
frame2_index_, p_B2P2_, x, y);
} else {
DoEvalGeneric(*plant_double_, context_double_, frame1_index_, p_B1P1_,
frame2_index_, p_B2P2_, x, y);
}
}
} // namespace multibody
} // namespace drake
Loading

0 comments on commit 6ee9963

Please sign in to comment.