DRAKERBSYSTEM_EXPORT RigidBodySystem::StateVector<double>
Drake::getInitialState(const RigidBodySystem& sys) {
  VectorXd x0(sys.tree->num_positions + sys.tree->num_velocities);
  default_random_engine generator;
  x0 << sys.tree->getRandomConfiguration(generator),
      VectorXd::Random(sys.tree->num_velocities);

  // todo: implement joint limits, etc.

  if (sys.tree->getNumPositionConstraints()) {
    // todo: move this up to the system level?

    OptimizationProblem prog;
    std::vector<RigidBodyLoop, Eigen::aligned_allocator<RigidBodyLoop> > const&
        loops = sys.tree->loops;

    int nq = sys.tree->num_positions;
    auto qvar = prog.AddContinuousVariables(nq);

    Matrix<double, 7, 1> bTbp = Matrix<double, 7, 1>::Zero();
    bTbp(3) = 1.0;
    Vector2d tspan;
    tspan << -std::numeric_limits<double>::infinity(),
        std::numeric_limits<double>::infinity();
    Vector3d zero = Vector3d::Zero();
    for (int i = 0; i < loops.size(); i++) {
      auto con1 = make_shared<RelativePositionConstraint>(
          sys.tree.get(), zero, zero, zero, loops[i].frameA->frame_index,
          loops[i].frameB->frame_index, bTbp, tspan);
      std::shared_ptr<SingleTimeKinematicConstraintWrapper> con1wrapper(
          new SingleTimeKinematicConstraintWrapper(con1));
      prog.AddGenericConstraint(con1wrapper, {qvar});
      auto con2 = make_shared<RelativePositionConstraint>(
          sys.tree.get(), loops[i].axis, loops[i].axis, loops[i].axis,
          loops[i].frameA->frame_index, loops[i].frameB->frame_index, bTbp,
          tspan);
      std::shared_ptr<SingleTimeKinematicConstraintWrapper> con2wrapper(
          new SingleTimeKinematicConstraintWrapper(con2));
      prog.AddGenericConstraint(con2wrapper, {qvar});
    }

    VectorXd q_guess = x0.topRows(nq);
    prog.AddQuadraticCost(MatrixXd::Identity(nq, nq), q_guess);
    prog.Solve();

    x0 << qvar.value(), VectorXd::Zero(sys.tree->num_velocities);
  }
  return x0;
}
RigidBodySystem::StateVector<double> RigidBodySystem::dynamics(
    const double& t, const RigidBodySystem::StateVector<double>& x,
    const RigidBodySystem::InputVector<double>& u) const {
  using namespace std;
  using namespace Eigen;
  eigen_aligned_unordered_map<const RigidBody*, Matrix<double, 6, 1> > f_ext;

  // todo: make kinematics cache once and re-use it (but have to make one per
  // type)
  auto nq = tree->num_positions;
  auto nv = tree->num_velocities;
  auto num_actuators = tree->actuators.size();
  auto q = x.topRows(nq);
  auto v = x.bottomRows(nv);
  auto kinsol = tree->doKinematics(q, v);

  // todo: preallocate the optimization problem and constraints, and simply
  // update them then solve on each function eval.
  // happily, this clunkier version seems fast enough for now
  // the optimization framework should support this (though it has not been
  // tested thoroughly yet)
  OptimizationProblem prog;
  auto const& vdot = prog.AddContinuousVariables(nv, "vdot");

  auto H = tree->massMatrix(kinsol);
  Eigen::MatrixXd H_and_neg_JT = H;

  VectorXd C = tree->dynamicsBiasTerm(kinsol, f_ext);
  if (num_actuators > 0) C -= tree->B * u.topRows(num_actuators);

  // loop through rigid body force elements
  {
    // todo: distinguish between AppliedForce and ConstraintForce

    size_t u_index = 0;
    for (auto const& f : force_elements) {
      size_t num_inputs = f->getNumInputs();
      VectorXd force_input(u.middleRows(u_index, num_inputs));
      C -= f->output(t, force_input, kinsol);
      u_index += num_inputs;
    }
  }

  // apply joint limit forces
  {
    for (auto const& b : tree->bodies) {
      if (!b->hasParent()) continue;
      auto const& joint = b->getJoint();
      if (joint.getNumPositions() == 1 &&
          joint.getNumVelocities() ==
              1) {  // taking advantage of only single-axis joints having joint
                    // limits makes things easier/faster here
        double qmin = joint.getJointLimitMin()(0),
               qmax = joint.getJointLimitMax()(0);
        // tau = k*(qlimit-q) - b(qdot)
        if (q(b->position_num_start) < qmin)
          C(b->velocity_num_start) -=
              penetration_stiffness * (qmin - q(b->position_num_start)) -
              penetration_damping * v(b->velocity_num_start);
        else if (q(b->position_num_start) > qmax)
          C(b->velocity_num_start) -=
              penetration_stiffness * (qmax - q(b->position_num_start)) -
              penetration_damping * v(b->velocity_num_start);
      }
    }
  }

  // apply contact forces
  {
    VectorXd phi;
    Matrix3Xd normal, xA, xB;
    vector<int> bodyA_idx, bodyB_idx;
    if (use_multi_contact)
      tree->potentialCollisions(kinsol, phi, normal, xA, xB, bodyA_idx,
                                bodyB_idx);
    else
      tree->collisionDetect(kinsol, phi, normal, xA, xB, bodyA_idx, bodyB_idx);

    for (int i = 0; i < phi.rows(); i++) {
      if (phi(i) < 0.0) {  // then i have contact
        // todo: move this entire block to a shared an updated "contactJacobian"
        // method in RigidBodyTree
        auto JA = tree->transformPointsJacobian(kinsol, xA.col(i), bodyA_idx[i],
                                                0, false);
        auto JB = tree->transformPointsJacobian(kinsol, xB.col(i), bodyB_idx[i],
                                                0, false);
        Vector3d this_normal = normal.col(i);

        // compute the surface tangent basis
        Vector3d tangent1;
        if (1.0 - this_normal(2) < EPSILON) {  // handle the unit-normal case
                                               // (since it's unit length, just
                                               // check z)
          tangent1 << 1.0, 0.0, 0.0;
        } else if (1 + this_normal(2) < EPSILON) {
          tangent1 << -1.0, 0.0, 0.0;  // same for the reflected case
        } else {                       // now the general case
          tangent1 << this_normal(1), -this_normal(0), 0.0;
          tangent1 /= sqrt(this_normal(1) * this_normal(1) +
                           this_normal(0) * this_normal(0));
        }
        Vector3d tangent2 = this_normal.cross(tangent1);
        Matrix3d R;  // rotation into normal coordinates
        R.row(0) = tangent1;
        R.row(1) = tangent2;
        R.row(2) = this_normal;
        auto J = R * (JA - JB);          // J = [ D1; D2; n ]
        auto relative_velocity = J * v;  // [ tangent1dot; tangent2dot; phidot ]

        {
          // spring law for normal force:  fA_normal = -k*phi - b*phidot
          // and damping for tangential force:  fA_tangent = -b*tangentdot
          // (bounded by the friction cone)
          Vector3d fA;
          fA(2) =
              std::max<double>(-penetration_stiffness * phi(i) -
                                   penetration_damping * relative_velocity(2),
                               0.0);
          fA.head(2) =
              -std::min<double>(
                  penetration_damping,
                  friction_coefficient * fA(2) /
                      (relative_velocity.head(2).norm() + EPSILON)) *
              relative_velocity.head(2);  // epsilon to avoid divide by zero

          // equal and opposite: fB = -fA.
          // tau = (R*JA)^T fA + (R*JB)^T fB = J^T fA
          C -= J.transpose() * fA;
        }
      }
    }
  }

  if (tree->getNumPositionConstraints()) {
    int nc = tree->getNumPositionConstraints();
    const double alpha = 5.0;  // 1/time constant of position constraint
                               // satisfaction (see my latex rigid body notes)

    prog.AddContinuousVariables(
        nc, "position constraint force");  // don't actually need to use the
                                           // decision variable reference that
                                           // would be returned...

    // then compute the constraint force
    auto phi = tree->positionConstraints(kinsol);
    auto J = tree->positionConstraintsJacobian(kinsol, false);
    auto Jdotv = tree->positionConstraintsJacDotTimesV(kinsol);

    // phiddot = -2 alpha phidot - alpha^2 phi  (0 + critically damped
    // stabilization term)
    prog.AddLinearEqualityConstraint(
        J, -(Jdotv + 2 * alpha * J * v + alpha * alpha * phi), {vdot});
    H_and_neg_JT.conservativeResize(NoChange, H_and_neg_JT.cols() + J.rows());
    H_and_neg_JT.rightCols(J.rows()) = -J.transpose();
  }

  // add [H,-J^T]*[vdot;f] = -C
  prog.AddLinearEqualityConstraint(H_and_neg_JT, -C);

  prog.Solve();
  //      prog.PrintSolution();

  StateVector<double> dot(nq + nv);
  dot << kinsol.transformPositionDotMappingToVelocityMapping(
             Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>::Identity(
                 nq, nq)) *
             v,
      vdot.value();
  return dot;
}
Example #3
0
void inverseKinBackend(
    RigidBodyTree* model, const int nT,
    const double* t, const MatrixBase<DerivedA>& q_seed,
    const MatrixBase<DerivedB>& q_nom, const int num_constraints,
    RigidBodyConstraint** const constraint_array,
    const IKoptions& ikoptions, MatrixBase<DerivedC>* q_sol,
    int* info, std::vector<std::string>* infeasible_constraint) {

  // Validate some basic parameters of the input.
  if (q_seed.rows() != model->number_of_positions() || q_seed.cols() != nT ||
      q_nom.rows() != model->number_of_positions() || q_nom.cols() != nT) {
    throw std::runtime_error(
        "Drake::inverseKinBackend: q_seed and q_nom must be of size "
        "nq x nT");
  }

  KinematicsCacheHelper<double> kin_helper(model->bodies);

  // TODO(sam.creasey) I really don't like rebuilding the
  // OptimizationProblem for every timestep, but it's not possible to
  // enable/disable (or even remove) a constraint from an
  // OptimizationProblem between calls to Solve() currently, so
  // there's not actually another way.
  for (int t_index = 0; t_index < nT; t_index++) {
    OptimizationProblem prog;
    SetIKSolverOptions(ikoptions, &prog);

    DecisionVariableView vars =
        prog.AddContinuousVariables(model->number_of_positions());

    MatrixXd Q;
    ikoptions.getQ(Q);
    auto objective = std::make_shared<InverseKinObjective>(model, Q);
    prog.AddCost(objective, {vars});

    for (int i = 0; i < num_constraints; i++) {
      RigidBodyConstraint* constraint = constraint_array[i];
      const int constraint_category = constraint->getCategory();
      if (constraint_category ==
          RigidBodyConstraint::SingleTimeKinematicConstraintCategory) {
        SingleTimeKinematicConstraint* stc =
            static_cast<SingleTimeKinematicConstraint*>(constraint);
        if (!stc->isTimeValid(&t[t_index])) { continue; }
        auto wrapper = std::make_shared<SingleTimeKinematicConstraintWrapper>(
            stc, &kin_helper);
        prog.AddConstraint(wrapper, {vars});
      } else if (constraint_category ==
                 RigidBodyConstraint::PostureConstraintCategory) {
        PostureConstraint* pc = static_cast<PostureConstraint*>(constraint);
        if (!pc->isTimeValid(&t[t_index])) { continue; }
        VectorXd lb;
        VectorXd ub;
        pc->bounds(&t[t_index], lb, ub);
        prog.AddBoundingBoxConstraint(lb, ub, {vars});
      } else if (
          constraint_category ==
          RigidBodyConstraint::SingleTimeLinearPostureConstraintCategory) {
        AddSingleTimeLinearPostureConstraint(
            &t[t_index], constraint, model->number_of_positions(),
            vars, &prog);
      } else if (constraint_category ==
                 RigidBodyConstraint::QuasiStaticConstraintCategory) {
        AddQuasiStaticConstraint(&t[t_index], constraint, &kin_helper,
                                 vars, &prog);
      } else if (constraint_category ==
                 RigidBodyConstraint::MultipleTimeKinematicConstraintCategory) {
        throw std::runtime_error(
            "MultipleTimeKinematicConstraint is not supported"
            " in pointwise mode.");
      } else if (
          constraint_category ==
          RigidBodyConstraint::MultipleTimeLinearPostureConstraintCategory) {
        throw std::runtime_error(
            "MultipleTimeLinearPostureConstraint is not supported"
            " in pointwise mode.");
      }
    }

    // Add a bounding box constraint from the model.
    prog.AddBoundingBoxConstraint(
        model->joint_limit_min, model->joint_limit_max,
        {vars});

    // TODO(sam.creasey) would this be faster if we stored the view
    // instead of copying into a VectorXd?
    objective->set_q_nom(q_nom.col(t_index));
    if (!ikoptions.getSequentialSeedFlag() || (t_index == 0)) {
      prog.SetInitialGuess(vars, q_seed.col(t_index));
    } else {
      prog.SetInitialGuess(vars, q_sol->col(t_index - 1));
    }

    SolutionResult result = prog.Solve();
    prog.PrintSolution();
    q_sol->col(t_index) = vars.value();
    info[t_index] = GetIKSolverInfo(prog, result);
  }
}