00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "tutsim.hpp"
00023
00024 #include <jspace/test/sai_brep_parser.hpp>
00025 #include <jspace/test/sai_brep.hpp>
00026
00027 #include <tao/dynamics/taoNode.h>
00028 #include <tao/dynamics/taoJoint.h>
00029 #include <tao/dynamics/taoDynamics.h>
00030 #include <tao/dynamics/taoVar.h>
00031 #include <tao/matrix/TaoDeMath.h>
00032 #include <tao/matrix/TaoDeQuaternion.h>
00033 #include <tao/matrix/TaoDeFrame.h>
00034
00035 #include <boost/shared_ptr.hpp>
00036
00037 #include <FL/Fl.H>
00038 #include <FL/Fl_Double_Window.H>
00039 #include <FL/Fl_Button.H>
00040 #include <FL/fl_draw.H>
00041
00042 #include <err.h>
00043 #include <sys/time.h>
00044
00045
00046 static char const * robot_filename(TUTROB_XML_PATH_STR);
00047 static double gfx_rate_hz(20.0);
00048 static double servo_rate_hz(400.0);
00049 static double sim_rate_hz(1600.0);
00050 static int win_width(300);
00051 static int win_height(200);
00052 static char const * win_title("Stanford WBC Tutorial");
00053 static boost::shared_ptr<jspace::tao_tree_info_s> sim_tree;
00054 static boost::shared_ptr<jspace::tao_tree_info_s> scratch_tree;
00055 static int id1(-1);
00056 static int id2(-1);
00057 static int id3(-1);
00058 static int id4(-1);
00059 static jspace::State state;
00060 static size_t ndof;
00061 static size_t toggle_count(0);
00062 static double servo_dt_ms;
00063 static bool paused(false);
00064
00065
00066 static bool (*servo_cb)(size_t toggle_count,
00067 double wall_time_ms,
00068 double sim_time_ms,
00069 jspace::State & state,
00070 jspace::Vector & command);
00071
00072 static void (*draw_cb)(double x0, double y0, double scale) = 0;
00073
00074
00075 namespace {
00076
00077
00078 class Simulator : public Fl_Widget {
00079 public:
00080 Simulator(int xx, int yy, int width, int height, const char * label = 0);
00081 virtual ~Simulator();
00082
00083 virtual void draw();
00084
00085 void tick();
00086 static void timer_cb(void * param);
00087 };
00088
00089
00090 class Window : public Fl_Double_Window {
00091 public:
00092 Window(int width, int height, const char * title);
00093
00094 virtual void resize(int x, int y, int w, int h);
00095
00096 Simulator * simulator;
00097 Fl_Button * toggle;
00098 Fl_Button * pause;
00099 Fl_Button * quit;
00100
00101 static void cb_toggle(Fl_Widget * widget, void * param);
00102 static void cb_pause(Fl_Widget * widget, void * param);
00103 static void cb_quit(Fl_Widget * widget, void * param);
00104 };
00105
00106
00107 static void write_state_to_tree(jspace::Vector const * jpos,
00108 jspace::Vector const * jvel,
00109 jspace::tao_tree_info_s & tree)
00110 {
00111 for (size_t ii(0); ii < ndof; ++ii) {
00112 taoJoint * joint(tree.info[ii].joint);
00113 int const kk(tree.info[ii].id);
00114 if (jpos) {
00115 joint->setQ(&(jpos->coeff(kk)));
00116 }
00117 if (jvel) {
00118 joint->setDQ(&(jvel->coeff(kk)));
00119 }
00120 joint->zeroDDQ();
00121 joint->zeroTau();
00122 }
00123 taoDynamics::updateTransformation(tree.root);
00124 }
00125
00126
00127 static void write_state_to_tree(jspace::tao_tree_info_s & tree)
00128 {
00129 write_state_to_tree(&(state.position_), &(state.velocity_), tree);
00130 }
00131
00132
00133 static void read_state_from_tree(jspace::tao_tree_info_s const & tree,
00134 jspace::Vector * jpos,
00135 jspace::Vector * jvel,
00136 jspace::Vector * jfrc)
00137 {
00138 for (size_t ii(0); ii < ndof; ++ii) {
00139 taoJoint const * joint(tree.info[ii].joint);
00140 int const kk(tree.info[ii].id);
00141 if (jpos) {
00142 joint->getQ(&(jpos->coeffRef(kk)));
00143 }
00144 if (jvel) {
00145 joint->getDQ(&(jvel->coeffRef(kk)));
00146 }
00147 if (jfrc) {
00148 joint->getTau(&(jfrc->coeffRef(kk)));
00149 }
00150 }
00151 }
00152
00153
00154 static void read_state_from_tree(jspace::tao_tree_info_s const & tree)
00155 {
00156 read_state_from_tree(tree, &(state.position_), &(state.velocity_), &(state.force_));
00157 }
00158
00159
00160 Simulator::
00161 Simulator(int xx, int yy, int width, int height, const char * label)
00162 : Fl_Widget(xx, yy, width, height, label)
00163 {
00164 Fl::add_timeout(0.1, timer_cb, this);
00165 }
00166
00167
00168 Simulator::
00169 ~Simulator()
00170 {
00171 Fl::remove_timeout(timer_cb, this);
00172 }
00173
00174
00175 static taoDNode * find_node(jspace::tao_tree_info_s & tree, std::string const & name)
00176 {
00177 for (size_t ii(0); ii < ndof; ++ii) {
00178 if (name == tree.info[ii].link_name) {
00179 return tree.info[ii].node;
00180 }
00181 }
00182 errx(EXIT_FAILURE, "node `%s' not found", name.c_str());
00183 }
00184
00185
00186 static void raw_draw_tree(jspace::tao_tree_info_s & tree, double x0, double y0, double scale)
00187 {
00188 if (0 > id1) {
00189 id1 = find_node(*sim_tree, "link1")->getID();
00190 id2 = find_node(*sim_tree, "link2")->getID();
00191 id3 = find_node(*sim_tree, "link3")->getID();
00192 id4 = find_node(*sim_tree, "link4")->getID();
00193 }
00194
00195 fl_line(x0 + (tree.info[id1].node->frameGlobal()->translation()[1] * scale),
00196 y0 - (tree.info[id1].node->frameGlobal()->translation()[2] * scale),
00197 x0 + (tree.info[id2].node->frameGlobal()->translation()[1] * scale),
00198 y0 - (tree.info[id2].node->frameGlobal()->translation()[2] * scale));
00199 fl_line(x0 + (tree.info[id2].node->frameGlobal()->translation()[1] * scale),
00200 y0 - (tree.info[id2].node->frameGlobal()->translation()[2] * scale),
00201 x0 + (tree.info[id3].node->frameGlobal()->translation()[1] * scale),
00202 y0 - (tree.info[id3].node->frameGlobal()->translation()[2] * scale));
00203 fl_line(x0 + (tree.info[id3].node->frameGlobal()->translation()[1] * scale),
00204 y0 - (tree.info[id3].node->frameGlobal()->translation()[2] * scale),
00205 x0 + (tree.info[id4].node->frameGlobal()->translation()[1] * scale),
00206 y0 - (tree.info[id4].node->frameGlobal()->translation()[2] * scale));
00207 deFrame locframe;
00208 locframe.translation()[2] = -1.0;
00209 deFrame globframe;
00210 globframe.multiply(*(tree.info[id4].node->frameGlobal()), locframe);
00211 fl_line(x0 + (tree.info[id4].node->frameGlobal()->translation()[1] * scale),
00212 y0 - (tree.info[id4].node->frameGlobal()->translation()[2] * scale),
00213 x0 + (globframe.translation()[1] * scale),
00214 y0 - (globframe.translation()[2] * scale));
00215 }
00216
00217
00218 static void raw_draw_com(jspace::tao_tree_info_s & tree, double x0, double y0, double scale)
00219 {
00220 if ( ! id1) {
00221 id1 = find_node(*sim_tree, "link1")->getID();
00222 id2 = find_node(*sim_tree, "link2")->getID();
00223 id3 = find_node(*sim_tree, "link3")->getID();
00224 id4 = find_node(*sim_tree, "link4")->getID();
00225 }
00226
00227 deVector3 globpoint;
00228 globpoint.multiply(*(tree.info[id1].node->frameGlobal()), *(tree.info[id1].node->center()));
00229 fl_point(x0 + (globpoint[1] * scale), y0 - (globpoint[2] * scale));
00230 globpoint.multiply(*(tree.info[id2].node->frameGlobal()), *(tree.info[id2].node->center()));
00231 fl_point(x0 + (globpoint[1] * scale), y0 - (globpoint[2] * scale));
00232 globpoint.multiply(*(tree.info[id3].node->frameGlobal()), *(tree.info[id3].node->center()));
00233 fl_point(x0 + (globpoint[1] * scale), y0 - (globpoint[2] * scale));
00234 globpoint.multiply(*(tree.info[id4].node->frameGlobal()), *(tree.info[id4].node->center()));
00235 fl_point(x0 + (globpoint[1] * scale), y0 - (globpoint[2] * scale));
00236 }
00237
00238
00239 void Simulator::
00240 draw()
00241 {
00242 double scale;
00243 if (w() > h()) {
00244 scale = h() / 9.0;
00245 }
00246 else {
00247 scale = w() / 9.0;
00248 }
00249 double const x0(w() / 2.0);
00250 double const y0(h() / 2.0);
00251
00252 fl_color(FL_BLACK);
00253 fl_rectf(x(), y(), w(), h());
00254
00255 fl_color(FL_WHITE);
00256 fl_line_style(FL_SOLID, 3, 0);
00257 raw_draw_tree(*sim_tree, x0, y0, scale);
00258
00259 fl_color(FL_GREEN);
00260 raw_draw_com(*sim_tree, x0, y0, scale);
00261
00262 if (draw_cb) {
00263 draw_cb(x0, y0, scale);
00264 }
00265
00266 fl_line_style(0);
00267 }
00268
00269
00270 void Simulator::
00271 tick()
00272 {
00273 static struct timeval wall_time_start;
00274 static size_t sim_nsteps(0);
00275 static double wall_time_ms, sim_time_ms, sim_dt;
00276 static double gfx_dt_ms, gfx_next_ms;
00277
00278 if (sim_nsteps == 0) {
00279 if (0 != gettimeofday(&wall_time_start, 0)) {
00280 errx(EXIT_FAILURE, "gettimofday failed");
00281 }
00282 wall_time_ms = 0;
00283 sim_time_ms = 0;
00284 servo_dt_ms = 1e3 / servo_rate_hz;
00285 if (sim_rate_hz <= servo_rate_hz) {
00286 sim_rate_hz = servo_rate_hz;
00287 sim_nsteps = 1;
00288 }
00289 else {
00290 sim_nsteps = llrint(ceil(sim_rate_hz / servo_rate_hz));
00291 sim_rate_hz = sim_nsteps * servo_rate_hz;
00292 }
00293 sim_dt = 1.0 / sim_rate_hz;
00294 gfx_dt_ms = 1e3 / gfx_rate_hz;
00295 gfx_next_ms = -1;
00296 }
00297 else {
00298 struct timeval now;
00299 if (0 != gettimeofday(&now, 0)) {
00300 errx(EXIT_FAILURE, "gettimofday failed");
00301 }
00302 wall_time_ms = 1e3 * (now.tv_sec - wall_time_start.tv_sec)
00303 + 1e-3 * (now.tv_usec - wall_time_start.tv_usec);
00304 sim_time_ms += servo_dt_ms;
00305 }
00306
00307 jspace::Vector command;
00308 read_state_from_tree(*sim_tree);
00309 if ( ! servo_cb(toggle_count, wall_time_ms, sim_time_ms, state, command)) {
00310 write_state_to_tree(*sim_tree);
00311 }
00312 else {
00313 if (command.rows() != ndof) {
00314 errx(EXIT_FAILURE, "invalid command dimension %d (should be %zu)", command.rows(), ndof);
00315 }
00316 jspace::Vector ddq(ndof);
00317 for (size_t ii(0); ii < sim_nsteps; ++ii) {
00318 static deVector3 earth_gravity(0.0, 0.0, -9.81);
00319 for (size_t kk(0); kk < ndof; ++kk) {
00320
00321 sim_tree->info[kk].joint->setTau(&(command.coeffRef(sim_tree->info[kk].id)));
00322 }
00323 taoDynamics::fwdDynamics(sim_tree->root, &earth_gravity);
00324 for (size_t kk(0); kk < ndof; ++kk) {
00325 sim_tree->info[kk].joint->getDDQ(&(ddq.coeffRef(sim_tree->info[kk].id)));
00326 }
00330 state.velocity_ += sim_dt * ddq;
00331 state.position_ += sim_dt * state.velocity_;
00332 write_state_to_tree(*sim_tree);
00333 }
00334 }
00335
00336 if (wall_time_ms >= gfx_next_ms) {
00337 redraw();
00338 gfx_next_ms += gfx_dt_ms;
00339 }
00340 }
00341
00342
00343 void Simulator::
00344 timer_cb(void * param)
00345 {
00346 if ( ! paused) {
00347 reinterpret_cast<Simulator*>(param)->tick();
00348 }
00349 Fl::repeat_timeout(1e-3 * servo_dt_ms,
00350 timer_cb,
00351 param);
00352 }
00353
00354
00355 Window::
00356 Window(int width, int height, const char * title)
00357 : Fl_Double_Window(width, height, title)
00358 {
00359 Fl::visual(FL_DOUBLE|FL_INDEX);
00360 begin();
00361 simulator = new Simulator(0, 0, width, height - 40);
00362 toggle = new Fl_Button(5, height - 35, 100, 30, "&Toggle");
00363 toggle->callback(cb_toggle);
00364 pause = new Fl_Button(width / 2 - 50, height - 35, 100, 30, "&Pause");
00365 pause->callback(cb_pause);
00366 quit = new Fl_Button(width - 105, height - 35, 100, 30, "&Quit");
00367 quit->callback(cb_quit, this);
00368 end();
00369 resizable(this);
00370 show();
00371 }
00372
00373
00374 void Window::
00375 resize(int x, int y, int w, int h)
00376 {
00377 Fl_Double_Window::resize(x, y, w, h);
00378 simulator->resize(0, 0, w, h - 40);
00379 toggle->resize(5, h-35, 100, 30);
00380 pause->resize(w/2-50, h-35, 100, 30);
00381 quit->resize(w-105, h-35, 100, 30);
00382 }
00383
00384
00385 void Window::
00386 cb_toggle(Fl_Widget * widget, void * param)
00387 {
00388 ++toggle_count;
00389 }
00390
00391
00392 void Window::
00393 cb_pause(Fl_Widget * widget, void * param)
00394 {
00395 if (paused) {
00396 paused = false;
00397 }
00398 else {
00399 paused = true;
00400 }
00401 }
00402
00403
00404 void Window::
00405 cb_quit(Fl_Widget * widget, void * param)
00406 {
00407 reinterpret_cast<Window*>(param)->hide();
00408 }
00409
00410 }
00411
00412
00413 void tutsim::
00414 set_robot_filename(char const * _robot_filename)
00415 {
00416 robot_filename = _robot_filename;
00417 }
00418
00419
00420 void tutsim::
00421 set_rates(double _gfx_rate_hz,
00422 double _servo_rate_hz,
00423 double _sim_rate_hz)
00424 {
00425 gfx_rate_hz = _gfx_rate_hz;
00426 servo_rate_hz = _servo_rate_hz;
00427 sim_rate_hz = _sim_rate_hz;
00428 }
00429
00430
00431 void tutsim::
00432 set_window_params(int width, int height, char const * title)
00433 {
00434 win_width = width;
00435 win_height = height;
00436 win_title = title;
00437 }
00438
00439
00440 void tutsim::
00441 draw_robot(jspace::Vector const & jpos, int width,
00442 unsigned char red, unsigned char green, unsigned char blue,
00443 double x0, double y0, double scale)
00444 {
00445 write_state_to_tree(&jpos, 0, *scratch_tree);
00446 fl_color(red, green, blue);
00447 fl_line_style(FL_SOLID, width, 0);
00448 raw_draw_tree(*scratch_tree, x0, y0, scale);
00449 fl_line_style(0);
00450 }
00451
00452
00453 void tutsim::
00454 draw_delta_jpos(jspace::Vector const & jpos, int width,
00455 unsigned char red, unsigned char green, unsigned char blue,
00456 double x0, double y0, double scale)
00457 {
00458 if (0 > id1) {
00459 id1 = find_node(*sim_tree, "link1")->getID();
00460 id2 = find_node(*sim_tree, "link2")->getID();
00461 id3 = find_node(*sim_tree, "link3")->getID();
00462 id4 = find_node(*sim_tree, "link4")->getID();
00463 }
00464
00465 jspace::Vector scratchpos(sim_tree->info.size());
00466 read_state_from_tree(*sim_tree, &scratchpos, 0, 0);
00467
00468 fl_color(red, green, blue);
00469 fl_line_style(FL_SOLID, width, 0);
00470
00471 scratchpos[id4] = jpos[id4];
00472 write_state_to_tree(&scratchpos, 0, *scratch_tree);
00473 deFrame locframe;
00474 locframe.translation()[2] = -1.0;
00475 deFrame globframe;
00476 globframe.multiply(*(scratch_tree->info[id4].node->frameGlobal()), locframe);
00477 fl_line(x0 + (scratch_tree->info[id4].node->frameGlobal()->translation()[1] * scale),
00478 y0 - (scratch_tree->info[id4].node->frameGlobal()->translation()[2] * scale),
00479 x0 + (globframe.translation()[1] * scale),
00480 y0 - (globframe.translation()[2] * scale));
00481
00482 scratchpos[id3] = jpos[id3];
00483 write_state_to_tree(&scratchpos, 0, *scratch_tree);
00484 fl_line(x0 + (scratch_tree->info[id3].node->frameGlobal()->translation()[1] * scale),
00485 y0 - (scratch_tree->info[id3].node->frameGlobal()->translation()[2] * scale),
00486 x0 + (scratch_tree->info[id4].node->frameGlobal()->translation()[1] * scale),
00487 y0 - (scratch_tree->info[id4].node->frameGlobal()->translation()[2] * scale));
00488
00489 scratchpos[id2] = jpos[id2];
00490 write_state_to_tree(&scratchpos, 0, *scratch_tree);
00491 fl_line(x0 + (scratch_tree->info[id2].node->frameGlobal()->translation()[1] * scale),
00492 y0 - (scratch_tree->info[id2].node->frameGlobal()->translation()[2] * scale),
00493 x0 + (scratch_tree->info[id3].node->frameGlobal()->translation()[1] * scale),
00494 y0 - (scratch_tree->info[id3].node->frameGlobal()->translation()[2] * scale));
00495
00496 scratchpos[id1] = jpos[id1];
00497 write_state_to_tree(&scratchpos, 0, *scratch_tree);
00498 fl_line(x0 + (scratch_tree->info[id1].node->frameGlobal()->translation()[1] * scale),
00499 y0 - (scratch_tree->info[id1].node->frameGlobal()->translation()[2] * scale),
00500 x0 + (scratch_tree->info[id2].node->frameGlobal()->translation()[1] * scale),
00501 y0 - (scratch_tree->info[id2].node->frameGlobal()->translation()[2] * scale));
00502
00503 fl_line_style(0);
00504 }
00505
00506
00507 void tutsim::
00508 set_draw_cb(void (*_draw_cb)(double x0, double y0, double scale))
00509 {
00510 draw_cb = _draw_cb;
00511 }
00512
00513
00514 int tutsim::
00515 run(bool (*_servo_cb)(size_t toggle_count,
00516 double wall_time_ms,
00517 double sim_time_ms,
00518 jspace::State & state,
00519 jspace::Vector & command))
00520 {
00521 if (gfx_rate_hz <= 0.0) {
00522 errx(EXIT_FAILURE, "invalid gfx_rate_hz %g (must be > 0)", gfx_rate_hz);
00523 }
00524 if (servo_rate_hz <= 0.0) {
00525 errx(EXIT_FAILURE, "invalid servo_rate_hz %g (must be > 0)", servo_rate_hz);
00526 }
00527
00528 try {
00529 jspace::test::BRParser brp;
00530 boost::shared_ptr<jspace::test::BranchingRepresentation> brep(brp.parse(robot_filename));
00531 sim_tree.reset(brep->createTreeInfo());
00532 brep.reset(brp.parse(robot_filename));
00533 scratch_tree.reset(brep->createTreeInfo());
00534 }
00535 catch (std::runtime_error const & ee) {
00536 errx(EXIT_FAILURE, "%s", ee.what());
00537 }
00538
00539 servo_cb = _servo_cb;
00540 ndof = sim_tree->info.size();
00541 state.init(ndof, ndof, ndof);
00542 write_state_to_tree(*sim_tree);
00543
00544 Window win(win_width, win_height, win_title);
00545 return Fl::run();
00546 }