Skip to content

Commit 5a3f21b

Browse files
committed
Reduce code overlap.
1 parent 4ae72df commit 5a3f21b

File tree

1 file changed

+68
-83
lines changed

1 file changed

+68
-83
lines changed

multibody/inverse_kinematics/global_inverse_kinematics.cc

Lines changed: 68 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,56 @@ void ApproximateBoundedNormByLinearConstraints(
611611
prog->AddLinearConstraint(x(0) - x(1) + x(2), -sqrt3_c, sqrt3_c);
612612
prog->AddLinearConstraint(x(0) - x(1) - x(2), -sqrt3_c, sqrt3_c);
613613
}
614+
} // namespace
615+
namespace {
616+
617+
// Helper struct containing precomputed quantities shared by
618+
// AddJointLimitConstraint() and AddJointCenteringCost().
619+
struct RevoluteJointGeometry {
620+
std::array<Eigen::Vector3d, 4> v_samples;
621+
Eigen::Matrix3d rotmat_joint_offset;
622+
};
623+
624+
// Helper to compute v_samples and rotmat_joint_offset for a revolute joint.
625+
RevoluteJointGeometry ComputeRevoluteJointGeometry(
626+
const Eigen::Vector3d& axis_F, double angle_offset) {
627+
// First generate a vector v_C that is perpendicular to rotation
628+
// axis, in child frame.
629+
Vector3d v_C = axis_F.cross(Vector3d(1, 0, 0));
630+
double v_C_norm = v_C.norm();
631+
if (v_C_norm < sqrt(2) / 2) {
632+
// axis_F is almost parallel to [1; 0; 0]. Try another axis [0, 1, 0]
633+
v_C = axis_F.cross(Vector3d(0, 1, 0));
634+
v_C_norm = v_C.norm();
635+
}
636+
// Normalizes the revolute vector.
637+
v_C /= v_C_norm;
638+
639+
// The constraint would be tighter, if we choose many unit
640+
// length vector `v`, perpendicular to the joint axis, in the
641+
// joint frame. Here to balance between the size of the
642+
// optimization problem, and the tightness of the convex
643+
// relaxation, we just use four vectors in `v`. Notice that
644+
// v_basis contains the orthonormal basis of the null space
645+
// null(axis_F).
646+
std::array<Eigen::Vector3d, 2> v_basis = {{v_C, axis_F.cross(v_C)}};
647+
v_basis[1] /= v_basis[1].norm();
648+
649+
std::array<Eigen::Vector3d, 4> v_samples;
650+
v_samples[0] = v_basis[0];
651+
v_samples[1] = v_basis[1];
652+
v_samples[2] = v_basis[0] + v_basis[1];
653+
v_samples[2] /= v_samples[2].norm();
654+
v_samples[3] = v_basis[0] - v_basis[1];
655+
v_samples[3] /= v_samples[3].norm();
656+
657+
// rotmat_joint_offset is R(k, angle_offset) explained above.
658+
const Matrix3d rotmat_joint_offset =
659+
Eigen::AngleAxisd(angle_offset, axis_F).toRotationMatrix();
660+
661+
return {v_samples, rotmat_joint_offset};
662+
}
663+
614664
} // namespace
615665

616666
void GlobalInverseKinematics::AddJointLimitConstraint(
@@ -711,46 +761,16 @@ void GlobalInverseKinematics::AddJointLimitConstraint(
711761
// |R_WC*R_CJc*v - R_WP * R_PJp * R(k,(a+b)/2)*v | <= 2*sin (α / 2)
712762
// as we explained above.
713763

714-
// First generate a vector v_C that is perpendicular to rotation
715-
// axis, in child frame.
716-
Vector3d v_C = axis_F.cross(Vector3d(1, 0, 0));
717-
double v_C_norm = v_C.norm();
718-
if (v_C_norm < sqrt(2) / 2) {
719-
// axis_F is almost parallel to [1; 0; 0]. Try another axis
720-
// [0, 1, 0]
721-
v_C = axis_F.cross(Vector3d(0, 1, 0));
722-
v_C_norm = v_C.norm();
723-
}
724-
// Normalizes the revolute vector.
725-
v_C /= v_C_norm;
726-
727-
// The constraint would be tighter, if we choose many unit
728-
// length vector `v`, perpendicular to the joint axis, in the
729-
// joint frame. Here to balance between the size of the
730-
// optimization problem, and the tightness of the convex
731-
// relaxation, we just use four vectors in `v`. Notice that
732-
// v_basis contains the orthonormal basis of the null space
733-
// null(axis_F).
734-
std::array<Eigen::Vector3d, 2> v_basis = {{v_C, axis_F.cross(v_C)}};
735-
v_basis[1] /= v_basis[1].norm();
736-
737-
std::array<Eigen::Vector3d, 4> v_samples;
738-
v_samples[0] = v_basis[0];
739-
v_samples[1] = v_basis[1];
740-
v_samples[2] = v_basis[0] + v_basis[1];
741-
v_samples[2] /= v_samples[2].norm();
742-
v_samples[3] = v_basis[0] - v_basis[1];
743-
v_samples[3] /= v_samples[3].norm();
744-
745-
// rotmat_joint_offset is R(k, (a+b)/2) explained above.
746-
const Matrix3d rotmat_joint_offset =
747-
Eigen::AngleAxisd((joint_lower_bounds_[position_idx] +
748-
joint_upper_bounds_[position_idx]) /
749-
2,
750-
axis_F)
751-
.toRotationMatrix();
752-
753-
// joint_limit_expr is going to be within the Lorentz cone.
764+
const double offset_angle = (joint_lower_bounds_[position_idx] +
765+
joint_upper_bounds_[position_idx]) /
766+
2;
767+
const auto joint_geometry =
768+
ComputeRevoluteJointGeometry(axis_F, offset_angle);
769+
770+
const auto& v_samples = joint_geometry.v_samples;
771+
const auto& rotmat_joint_offset =
772+
joint_geometry.rotmat_joint_offset;
773+
754774
Eigen::Matrix<Expression, 4, 1> joint_limit_expr;
755775
const double joint_limit_lorentz_rhs = 2 * sin(joint_bound / 2);
756776
joint_limit_expr(0) = joint_limit_lorentz_rhs;
@@ -766,7 +786,6 @@ void GlobalInverseKinematics::AddJointLimitConstraint(
766786
ApproximateBoundedNormByLinearConstraints(
767787
joint_limit_expr.tail<3>(), joint_limit_lorentz_rhs,
768788
&prog_);
769-
770789
} else {
771790
prog_.AddLorentzConeConstraint(joint_limit_expr);
772791
}
@@ -815,6 +834,8 @@ void GlobalInverseKinematics::AddJointLimitConstraint(
815834
R_WB_[body_index] * X_CJc.rotation().matrix();
816835
const double joint_bound_cos{std::cos(joint_bound)};
817836
if (!linear_constraint_approximation) {
837+
std::array<Eigen::Vector3d, 2> v_basis = {
838+
{v_samples[0], (v_samples[0].cross(axis_F)).normalized()}};
818839
Eigen::Matrix<double, 3, 2> V;
819840
V << v_basis[0], v_basis[1];
820841
const Eigen::Matrix<symbolic::Expression, 2, 2> M =
@@ -824,7 +845,6 @@ void GlobalInverseKinematics::AddJointLimitConstraint(
824845
prog_.AddRotatedLorentzConeConstraint(
825846
Vector3<symbolic::Expression>(M(0, 0), M(1, 1), M(1, 0)));
826847
}
827-
828848
// From Rodriguez formula, we know that -α <= β <= α implies
829849
// trace(R(k, β)) = 1 + 2 * cos(β) >= 1 + 2*cos(α)
830850
// So we can impose the constraint
@@ -880,53 +900,19 @@ void GlobalInverseKinematics::AddJointCenteringCost(BodyIndex body_index,
880900

881901
switch (joint->num_velocities()) {
882902
case 0: {
883-
// Fixed to the parent body.
884903
throw std::runtime_error("Cannot impose joint limits for a fixed joint.");
885904
}
886905
case 1: {
887-
// Should NOT do this evil dynamic cast here, but currently we do
888-
// not have a method to tell if a joint is revolute or not.
889906
if (dynamic_cast<const RevoluteJoint<double>*>(joint) != nullptr) {
890907
const auto* revolute_joint =
891908
dynamic_cast<const RevoluteJoint<double>*>(joint);
892-
// axis_F is the vector of the rotation axis in the joint
893-
// inboard/outboard frame.
894909
const Vector3d axis_F = revolute_joint->revolute_axis();
895910

896-
// First generate a vector v_C that is perpendicular to rotation
897-
// axis, in child frame.
898-
Vector3d v_C = axis_F.cross(Vector3d(1, 0, 0));
899-
double v_C_norm = v_C.norm();
900-
if (v_C_norm < sqrt(2) / 2) {
901-
// axis_F is almost parallel to [1; 0; 0]. Try another axis
902-
// [0, 1, 0]
903-
v_C = axis_F.cross(Vector3d(0, 1, 0));
904-
v_C_norm = v_C.norm();
905-
}
906-
// Normalizes the revolute vector.
907-
v_C /= v_C_norm;
908-
909-
// The constraint would be tighter, if we choose many unit
910-
// length vector `v`, perpendicular to the joint axis, in the
911-
// joint frame. Here to balance between the size of the
912-
// optimization problem, and the tightness of the convex
913-
// relaxation, we just use four vectors in `v`. Notice that
914-
// v_basis contains the orthonormal basis of the null space
915-
// null(axis_F).
916-
std::array<Eigen::Vector3d, 2> v_basis = {{v_C, axis_F.cross(v_C)}};
917-
v_basis[1] /= v_basis[1].norm();
918-
919-
std::array<Eigen::Vector3d, 4> v_samples;
920-
v_samples[0] = v_basis[0];
921-
v_samples[1] = v_basis[1];
922-
v_samples[2] = v_basis[0] + v_basis[1];
923-
v_samples[2] /= v_samples[2].norm();
924-
v_samples[3] = v_basis[0] - v_basis[1];
925-
v_samples[3] /= v_samples[3].norm();
926-
927-
// rotmat_joint_offset is R(k, nominal) explained above.
928-
const Matrix3d rotmat_joint_offset =
929-
Eigen::AngleAxisd(nominal_value, axis_F).toRotationMatrix();
911+
// Compute shared geometric quantities.
912+
const auto joint_geometry =
913+
ComputeRevoluteJointGeometry(axis_F, nominal_value);
914+
const auto& v_samples = joint_geometry.v_samples;
915+
const auto& rotmat_joint_offset = joint_geometry.rotmat_joint_offset;
930916

931917
for (const auto& v : v_samples) {
932918
Eigen::Matrix<Expression, 3, 1> unit_vector_difference =
@@ -942,7 +928,7 @@ void GlobalInverseKinematics::AddJointCenteringCost(BodyIndex body_index,
942928
prog_.AddLinearCost(weight * s);
943929
}
944930
} else if (norm == 2) {
945-
// Get the union of all variables in all expressions
931+
// Get the union of all variables in all expressions.
946932
symbolic::Variables var_set;
947933
for (int i = 0; i < unit_vector_difference.size(); ++i) {
948934
var_set += unit_vector_difference[i].GetVariables();
@@ -963,7 +949,6 @@ void GlobalInverseKinematics::AddJointCenteringCost(BodyIndex body_index,
963949
// Sign flip is necessary since this method adds a cost on Ax-b.
964950
prog_.Add2NormSquaredCost(A, -b, vars);
965951
} else {
966-
// prog_.AddL2NormCost(A, b, vars);
967952
prog_.AddL2NormCostUsingConicConstraint(A, b, vars);
968953
}
969954
} else {

0 commit comments

Comments
 (0)