24#include <kdl/chainidsolver_recursive_newton_euler.hpp>
37static constexpr double kMaxPlaceErr = 0.005;
38static constexpr double kMinElbowHeight = 0.45;
39static constexpr double kMaxDisturb = 0.001;
42static constexpr double kKp[7] = { 100, 200, 100, 200, 100, 200, 100 };
43static constexpr double kKd[7] = { 20, 28, 20, 28, 20, 28, 20 };
46static constexpr double kBase[2][3] = { { -0.70, -0.12, 0.0 }, { 0.70, 0.12, M_PI } };
47static constexpr const char *kPrefix[2] = {
"",
"r2_" };
57static KDL::Vector cube_spot(
int arm,
const double xy[2])
92 const double friction[3] = { 1.0, 0.005, 0.0001 };
93 std::copy(friction, friction + 3, o.
friction);
98 camera(
const char *name,
const double pos[3],
const double quat[4],
double fovy)
102 std::copy(pos, pos + 3, cam.
pos);
103 std::copy(quat, quat + 4, cam.
quat);
112 :
robot(r),
n(r.chain.getNrOfJoints()),
rnea(r.chain,
KDL::Vector(0.0, 0.0, gravity_z)),
114 f_ext(r.chain.getNrOfSegments(),
KDL::Wrench::Zero())
120 for (
unsigned i = 0; i <
n; ++i)
qddot(i) = kKp[i] * (q_des(i) -
q(i)) - kKd[i] *
qdot(i);
122 for (
unsigned i = 0; i <
n; ++i)
robot.jnt_trq_cmd[i] =
tau(i);
134int main(
int argc,
char *argv[])
139 for (
int a = 0; a < 2; ++a) {
143 spec.
pos[0] = kBase[a][0];
144 spec.
pos[1] = kBase[a][1];
146 spec.
quat[2] = std::sin(kBase[a][2] / 2.0);
147 spec.
quat[3] = std::cos(kBase[a][2] / 2.0);
149 scene.
robots.push_back(spec);
152 for (
int a = 0; a < 2; ++a) {
155 cube.
name = std::string(kPrefix[a]) +
"cube";
158 for (
const FreeObject &f : kFreeObjects) scene.
objects.push_back(scene_object(f));
160 const double overview_pos[3] = { 0.0, -1.3, 1.9 };
161 const double overview_q[4] = { 0.410747, 0.0, 0.0, 0.911749 };
162 const double side_pos[3] = { 0.0, 1.5, 1.15 };
163 const double side_q[4] = { 0.0, 0.633989, 0.773342, 0.0 };
164 scene.
cameras.push_back(camera(
"overview", overview_pos, overview_q, 55.0));
165 scene.
cameras.push_back(camera(
"side", side_pos, side_q, 50.0));
169 std::cout <<
"cameras:";
170 for (
int i = 0; i < env.
model->ncam; ++i)
171 std::cout <<
" " << mj_id2name(env.
model, mjOBJ_CAMERA, i);
175 std::array<mj_kdl::Robot, 2> robots;
176 std::vector<ex::PhaseArm> arms;
177 std::vector<mj_kdl::SceneFreeBodySlot *> cubes, free_bodies;
178 std::deque<RneaArm> ctl;
179 std::array<int, 2> roots{};
180 for (
int a = 0; a < 2; ++a) {
181 const std::string pre = kPrefix[a];
183 &robots[a], &env,
"base_link",
"bracelet_link", pre.c_str(), &tool
192 if (!arms.back().gripper || !cubes.back())
return 1;
193 ctl.emplace_back(robots[a], scene.
gravity_z);
194 const int base = mj_name2id(env.
model, mjOBJ_BODY, (pre +
"base_link").c_str());
195 roots[a] = env.
model->body_rootid[base];
199 if (!free_bodies.back())
return 1;
206 bool restart =
false, solver_ok =
true;
207 std::array<double, 2> elbow_min{};
208 double disturb = 0.0;
209 int arm_contacts = 0;
212 for (
int a = 0; a < 2; ++a) {
215 const double pos[3] = { p.
x(), p.
y(), p.
z() };
217 ctx->data->ctrl[arms[a].gripper->ctrl_id] = 0.0;
219 elbow_min[a] = INFINITY;
228 const auto control = [&](std::size_t a,
const KDL::JntArray &q_des) {
229 if (!ctl[a].control(q_des)) solver_ok =
false;
231 const auto after_step = [&](
const ex::Phase &, double) {
232 for (
int a = 0; a < 2; ++a) {
235 &env, (std::string(kPrefix[a]) +
"forearm_link").c_str(), &elbow
237 elbow_min[a] = std::min(elbow_min[a], elbow.
p.
z());
239 for (std::size_t i = 0; i < free_bodies.size(); ++i) {
242 disturb = std::max(disturb, (free_bodies[i]->pose.p - rest).Norm());
244 for (
int c = 0; c < env.
data->ncon; ++c) {
245 const mjContact &con = env.
data->contact[c];
246 const int r0 = env.
model->body_rootid[env.
model->geom_bodyid[con.geom[0]]];
247 const int r1 = env.
model->body_rootid[env.
model->geom_bodyid[con.geom[1]]];
248 if ((r0 == roots[0] && r1 == roots[1]) || (r0 == roots[1] && r1 == roots[0]))
252 const bool completed =
255 if (!solver_ok) std::cerr <<
"RNEA failed during the run\n";
257 bool ok = completed && solver_ok;
258 std::cout << std::fixed << std::setprecision(4);
259 for (
int a = 0; a < 2; ++a) {
262 const double place_xy = std::hypot(c.
x() - spot.
x(), c.
y() - spot.
y());
263 const bool on_table = std::abs(c.
z() - spot.
z()) < 0.002;
265 std::cout <<
"arm " << a + 1 <<
": cube at [" << c.
x() <<
", " << c.
y() <<
", " << c.
z()
266 <<
"] place error " << place_xy * 1000.0 <<
" mm (limit " << kMaxPlaceErr * 1000.0
267 <<
" mm)" << (on_table ?
"" :
", not on the table")
268 <<
"; lowest elbow above the table " << elbow_h * 1000.0 <<
" mm (limit "
269 << kMinElbowHeight * 1000.0 <<
" mm)\n";
270 ok = ok && on_table && place_xy <= kMaxPlaceErr && elbow_h >= kMinElbowHeight;
272 std::cout <<
"largest free-object displacement: " << disturb * 1000.0 <<
" mm (limit "
273 << kMaxDisturb * 1000.0 <<
" mm)\narm-to-arm contacts: " << arm_contacts
276 ok = ok && disturb <= kMaxDisturb && arm_contacts == 0;
277 return headless ?
ex::verdict(ok,
"both arms placed their cubes and left the rest alone") : 0;
static Rotation RotZ(double angle)
int main(int argc, char *argv[])
ResetInfo reset(Env *env, const ResetOptions *options=nullptr)
void set_body_pose(Env *env, const char *body_name, const double pos[3], const double *quat=nullptr)
Status init_env(Env *env, const SceneSpec *spec)
Status set_control_mode(Robot *r, CtrlMode mode)
Status init_robot_from_mjcf(Robot *r, Env *env, const char *base_body, const char *tip_body, const char *prefix="", const ToolFrameSpec *tool=nullptr)
void set_joint_pos(Robot *r, const KDL::JntArray &q)
bool get_body_frame(Env *env, const char *body_name, KDL::Frame *out)
SceneActuatorSlot * bind_scene_actuator(SceneState *s, const char *name)
SceneFreeBodySlot * bind_scene_free_body(SceneState *s, const char *body_name)
Status open_viewer(Env *env, const char *title="MuJoCo")
std::vector< Wrench > Wrenches
std::string asset(const fs::path &relative)
bool solve_pick_place(const mj_kdl::Robot &robot, PickPlaceWaypoints &w)
int verdict(bool ok, const std::string &goal)
mj_kdl::SceneSpec scene_spec()
mj_kdl::ToolFrameSpec gripper_tool(const std::string &prefix="")
std::vector< Phase > pick_place_phases(const PickPlaceWaypoints &w)
constexpr double kPickXY[2]
mj_kdl::AttachmentSpec gripper_attachment(const std::string &path, const std::string &site="pinch_site")
constexpr double kPlaceXY[2]
void read_q(const mj_kdl::Robot &robot, KDL::JntArray &q)
mj_kdl::SceneObject table_object(const std::string &path, double surface_z)
void prime_gravity(mj_kdl::Robot &robot, KDL::ChainDynParam &dyn, const KDL::JntArray &q)
bool run_phases(mj_kdl::Env &env, const std::vector< PhaseArm > &arms, const std::vector< Phase > &phases, bool &restart, const std::function< void(std::size_t, const KDL::JntArray &)> &control, const std::function< void(const Phase &, double t_rel)> &after_step={})
Args parse_args(int argc, char **argv, const std::string &default_record_path="")
mj_kdl::SceneObject cube_object(double x, double y, double surface_z)
RneaArm(mj_kdl::Robot &r, double gravity_z)
bool control(const KDL::JntArray &q_des)
KDL::ChainIdSolver_RNE rnea
std::vector< AttachmentSpec > attachments
std::vector< RobotSpec > robots
std::vector< SceneObject > objects
std::vector< CameraSpec > cameras