Mujoco KDL Wrapper  0.1.0
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
ex_vel_ctrl.cpp
Go to the documentation of this file.
1/* ex_vel_ctrl.cpp
2 * Joint velocity control: drive the Kinova GEN3 from the home pose to a target
3 * pose using a proportional velocity controller, then hold.
4 *
5 * gen3.xml has POSITION actuators, so velocity is implemented by integrating
6 * a clamped velocity command into the position setpoint each step:
7 *
8 * vel[i] = clamp(Kv * (target[i] - q[i]), -maxVel, maxVel)
9 * jnt_pos_cmd[i] += vel[i] * dt
10 *
11 * Requires third_party/menagerie submodule.
12 *
13 * Usage:
14 * ex_vel_ctrl [--headless]
15 *
16 * With --headless runs until convergence (or 5 s timeout) and prints
17 * final max joint error. */
18
20
21#include <algorithm>
22#include <cmath>
23#include <filesystem>
24#include <iomanip>
25#include <iostream>
26#include <string>
27
28static constexpr double kHomePose[7] = { 0.0, 0.2618, 3.1416, -2.2689, 0.0, 0.9599, 1.5708 };
29static constexpr double kTargetPose[7] = { 0.3, 0.5, 2.9, -2.0, 0.3, 1.2, 1.3 };
30
31static constexpr double kKv = 2.0; // proportional gain [rad/s per rad error]
32static constexpr double kMaxVel = 0.6; // max joint velocity [rad/s]
33static constexpr double kTol = 0.01; // convergence tolerance [rad]
34
35namespace fs = std::filesystem;
36static fs::path repo_root() { return fs::path(__FILE__).parent_path().parent_path().parent_path(); }
37
38int main(int argc, char *argv[])
39{
40 bool headless = false;
41 for (int i = 1; i < argc; ++i)
42 if (std::string(argv[i]) == "--headless") headless = true;
43
44 const fs::path root = repo_root();
45 if (!fs::exists(root / "third_party/menagerie")) {
46 std::cerr << "third_party/menagerie/ not found - run:\n"
47 " cmake -B build -DFETCH_MENAGERIE=ON\n";
48 return 1;
49 }
50
51 const std::string mjcf = (root / "third_party/menagerie/kinova_gen3/gen3.xml").string();
52
55 r.path = mjcf.c_str();
56 sc.robots.push_back(r);
57
58 mjModel *model = nullptr;
59 mjData *data = nullptr;
60 if (!mj_kdl::build_scene(&model, &data, &sc)) {
61 std::cerr << "build_scene() failed\n";
62 return 1;
63 }
64
65 mj_kdl::Robot robot;
66 if (!mj_kdl::init_robot_from_mjcf(&robot, model, data, "base_link", "bracelet_link")) {
67 std::cerr << "init_robot_from_mjcf() failed\n";
68 mj_kdl::destroy_scene(model, data);
69 return 1;
70 }
71
72 unsigned n = static_cast<unsigned>(robot.n_joints);
73
74 KDL::JntArray q_home(n);
75 for (unsigned i = 0; i < n; ++i) q_home(i) = kHomePose[i];
76
78
79 mj_kdl::Env env;
80 env.spec = sc;
81 env.model = model;
82 env.data = data;
83 mj_kdl::env_add_robot(&env, &robot);
84
85 const double dt = model->opt.timestep;
86 bool arrived = false;
87
88 env.on_reset = [&](mj_kdl::ResetContext *) {
89 mj_kdl::set_joint_pos(&robot, q_home, false);
90 arrived = false;
91 };
92
93 mj_kdl::reset(&env);
94
95 auto ctrl_step = [&]() {
96 mj_kdl::update(&robot);
97
98 if (arrived) return;
99
100 double max_err = 0.0;
101 for (unsigned i = 0; i < n; ++i) {
102 double err = kTargetPose[i] - robot.jnt_pos_msr[i];
103 max_err = std::max(max_err, std::abs(err));
104 double vel = std::clamp(kKv * err, -kMaxVel, kMaxVel);
105 robot.jnt_pos_cmd[i] += vel * dt;
106 }
107
108 if (max_err < kTol) {
109 arrived = true;
110 for (unsigned i = 0; i < n; ++i) robot.jnt_pos_cmd[i] = robot.jnt_pos_msr[i];
111 }
112 };
113
114 if (headless) {
115 const double timeout = 5.0;
116 while (data->time < timeout && !arrived) {
117 ctrl_step();
118 mj_kdl::step(&robot);
119 }
120
121 double max_err = 0.0;
122 for (unsigned i = 0; i < n; ++i)
123 max_err = std::max(max_err, std::abs(kTargetPose[i] - robot.jnt_pos_msr[i]));
124 std::cout << "max joint error: " << std::fixed << std::setprecision(4) << max_err
125 << " rad (" << (arrived ? "converged" : "timeout") << ")\n";
126 } else {
127 mj_kdl::Viewer viewer;
128 if (!mj_kdl::init_window_sim(&viewer, &robot)) {
129 std::cerr << "init_window_sim() failed\n";
130 mj_kdl::cleanup(&robot);
131 mj_kdl::destroy_scene(model, data);
132 return 1;
133 }
134
135 double prev_sim_time = data->time;
136 while (true) {
137 if (data->time < prev_sim_time - 1e-6) mj_kdl::reset(&env);
138 prev_sim_time = data->time;
139 ctrl_step();
140 if (!mj_kdl::step(&robot)) break;
141 }
142
143 mj_kdl::cleanup(&viewer);
144 }
145
146 mj_kdl::cleanup(&robot);
147 mj_kdl::destroy_scene(model, data);
148 return 0;
149}
int main(int argc, char *argv[])
bool step(Robot *s)
void cleanup(Robot *r)
bool init_robot_from_mjcf(Robot *r, mjModel *model, mjData *data, 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 call_forward=true)
void update(Robot *r)
bool build_scene(mjModel **out_model, mjData **out_data, const SceneSpec *spec)
void destroy_scene(mjModel *model, mjData *data)
bool init_window_sim(Viewer *v, Robot *r, const char *title="MuJoCo")
void env_add_robot(Env *env, Robot *robot)
ResetInfo reset(Env *env, const ResetOptions *options=nullptr)
ResetHook on_reset
std::vector< double > jnt_pos_msr
std::vector< double > jnt_pos_cmd
std::vector< RobotSpec > robots