00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include <stdio.h>
00037 #include <getopt.h>
00038 #include <execinfo.h>
00039 #include <signal.h>
00040 #include <sys/mman.h>
00041 #include <sys/types.h>
00042 #include <sys/stat.h>
00043 #include <unistd.h>
00044 #include <fcntl.h>
00045 #include <pthread.h>
00046 #include <sys/mman.h>
00047 #include <boost/cast.hpp>
00048
00049 #include <open_controllers_interface/open_controllers_interface.h>
00050
00051 using namespace pr2_hardware_interface;
00052 using namespace std;
00053 using namespace boost::accumulators;
00054
00055 static const int NSEC_PER_SECOND = 1e+9;
00056 static const int USEC_PER_SECOND = 1e6;
00057
00058 namespace OpenControllersInterface {
00059 OpenController::OpenController():
00060 not_sleep_clock_(false) {
00061 }
00062
00063 OpenController::~OpenController() {
00064
00065 }
00066 bool OpenController::resetMotorsService(std_srvs::Empty::Request &req,
00067 std_srvs::Empty::Response &resp) {
00068 g_reset_motors_ = true;
00069 return true;
00070 }
00071
00072 bool OpenController::haltMotorsService(std_srvs::Empty::Request &req,
00073 std_srvs::Empty::Response &resp) {
00074 quitRequest();
00075 return true;
00076 }
00077
00078 bool OpenController::publishTraceService(std_srvs::Empty::Request &req,
00079 std_srvs::Empty::Response &resp) {
00080 g_publish_trace_requested_ = true;
00081 return true;
00082 }
00083
00084 double timespecDiff(struct timespec* a, struct timespec* b) {
00085 return (a->tv_sec - b->tv_sec) + double(a->tv_nsec - b->tv_nsec) / NSEC_PER_SECOND;
00086 }
00087
00088 void OpenController::start() {
00089 while(!g_quit_) {
00090 startMain();
00091 if (g_reset_motors_)
00092 {
00093 ControllerStatusPtr st = recoverController();
00094 if (!st || !st->isHealthy())
00095 {
00096 ROS_FATAL("oh no, no way to recover! quit!");
00097 g_quit_ = true;
00098 }
00099 g_reset_motors_ = false;
00100 }
00101 }
00102 ROS_INFO("good bye start()");
00103 }
00104
00105 void OpenController::publishDiagnostics() {
00106 if (!!publisher_ && publisher_->trylock())
00107 {
00108 accumulator_set<double, stats<tag::max, tag::mean> > zero;
00109 vector<diagnostic_msgs::DiagnosticStatus> statuses;
00110 diagnostic_updater::DiagnosticStatusWrapper status;
00111
00112 static double max_ec = 0, max_cm = 0, max_loop = 0, max_jitter = 0;
00113 double avg_ec, avg_cm, avg_loop, avg_jitter;
00114
00115 avg_ec = extract_result<tag::mean>(g_stats_.ec_acc);
00116 avg_cm = extract_result<tag::mean>(g_stats_.cm_acc);
00117 avg_loop = extract_result<tag::mean>(g_stats_.loop_acc);
00118 max_ec = std::max(max_ec, extract_result<tag::max>(g_stats_.ec_acc));
00119 max_cm = std::max(max_cm, extract_result<tag::max>(g_stats_.cm_acc));
00120 max_loop = std::max(max_loop, extract_result<tag::max>(g_stats_.loop_acc));
00121 g_stats_.ec_acc = zero;
00122 g_stats_.cm_acc = zero;
00123 g_stats_.loop_acc = zero;
00124
00125
00126 avg_jitter = extract_result<tag::mean>(g_stats_.jitter_acc);
00127 max_jitter = std::max(max_jitter, extract_result<tag::max>(g_stats_.jitter_acc));
00128 g_stats_.jitter_acc = zero;
00129
00130 static bool first = true;
00131 if (first)
00132 {
00133 first = false;
00134 status.add("Robot Description", g_robot_desc_);
00135 }
00136
00137 status.addf("Max EtherCAT roundtrip (us)", "%.2f", max_ec*USEC_PER_SECOND);
00138 status.addf("Avg EtherCAT roundtrip (us)", "%.2f", avg_ec*USEC_PER_SECOND);
00139 status.addf("Max Controller Manager roundtrip (us)", "%.2f", max_cm*USEC_PER_SECOND);
00140 status.addf("Avg Controller Manager roundtrip (us)", "%.2f", avg_cm*USEC_PER_SECOND);
00141 status.addf("Max Total Loop roundtrip (us)", "%.2f", max_loop*USEC_PER_SECOND);
00142 status.addf("Avg Total Loop roundtrip (us)", "%.2f", avg_loop*USEC_PER_SECOND);
00143 status.addf("Max Loop Jitter (us)", "%.2f", max_jitter * USEC_PER_SECOND);
00144 status.addf("Avg Loop Jitter (us)", "%.2f", avg_jitter * USEC_PER_SECOND);
00145 status.addf("Control Loop Overruns", "%d", g_stats_.overruns);
00146 status.addf("Total Loop Count", "%ul", g_stats_.loop_count);
00147 status.addf("Recent Control Loop Overruns", "%d", g_stats_.recent_overruns);
00148 status.addf("Last Control Loop Overrun Cause", "ec: %.2fus, cm: %.2fus",
00149 g_stats_.overrun_ec*USEC_PER_SECOND, g_stats_.overrun_cm*USEC_PER_SECOND);
00150 status.addf("Last Overrun Loop Time (us)", "%.2f", g_stats_.overrun_loop_sec * USEC_PER_SECOND);
00151 status.addf("Realtime Loop Frequency", "%.4f", g_stats_.rt_loop_frequency);
00152
00153 status.name = "Realtime Control Loop";
00154 if (g_stats_.overruns > 0 && g_stats_.last_overrun < 30)
00155 {
00156 if (g_stats_.last_severe_overrun < 30)
00157 status.level = 1;
00158 else
00159 status.level = 0;
00160 status.message = "Realtime loop used too much time in the last 30 seconds.";
00161 }
00162 else
00163 {
00164 status.level = 0;
00165 status.message = "OK";
00166 }
00167 g_stats_.recent_overruns = 0;
00168 g_stats_.last_overrun++;
00169 g_stats_.last_severe_overrun++;
00170
00171 if (g_stats_.rt_loop_not_making_timing)
00172 {
00173 status.mergeSummaryf(status.ERROR, "Halting, realtime loop only ran at %.4f Hz", g_stats_.halt_rt_loop_frequency);
00174 }
00175
00176 statuses.push_back(status);
00177 publisher_->msg_.status = statuses;
00178 publisher_->msg_.header.stamp = ros::Time::now();
00179 publisher_->unlockAndPublish();
00180 }
00181 }
00182
00183 void OpenController::parseArguments(int argc, char** argv) {
00184 while (1) {
00185 static struct option long_options[] = {
00186 {"help", no_argument, 0, 'h'},
00187 {"stats", no_argument, 0, 's'},
00188 {"allow_unprogrammed", no_argument, 0, 'u'},
00189 {"interface", required_argument, 0, 'i'},
00190 {"xml", required_argument, 0, 'x'},
00191 {"dryrun", no_argument, 0, 'd'}
00192 };
00193 int option_index = 0;
00194 int c = getopt_long(argc, argv, "hi:usdx:", long_options, &option_index);
00195 if (c == -1) break;
00196 switch (c) {
00197 case 'h':
00198 Usage();
00199 break;
00200 case 'u':
00201 setAllowUnprogrammedP(true);
00202 break;
00203 case 'x':
00204 setRobotXMLFile(std::string(optarg));
00205 break;
00206 case 's':
00207 setStatsPublishP(true);
00208 break;
00209 case 'd':
00210 setDryRun(true);
00211 break;
00212 }
00213 }
00214 }
00215
00216
00217 void OpenController::initialize() {
00218 ros::NodeHandle node("~");
00219 initializeROS(node);
00220 reset_service_
00221 = node.advertiseService("reset_motors",
00222 &OpenController::resetMotorsService,
00223 this);
00224 halt_service_
00225 = node.advertiseService("halt_motors",
00226 &OpenController::haltMotorsService,
00227 this);
00228 publishTrace_service_
00229 = node.advertiseService("publish_trace",
00230 &OpenController::publishTraceService,
00231 this);
00232
00233 publisher_.reset(new realtime_tools::RealtimePublisher<diagnostic_msgs::DiagnosticArray>(node, "/diagnostics", 2));
00234
00235 if (!node.getParam("not_sleep_clock", not_sleep_clock_)) {
00236 not_sleep_clock_ = false;
00237 }
00238
00239 if (!node.getParam("min_acceptable_rt_loop_frequency", min_acceptable_rt_loop_frequency_))
00240 {
00241 min_acceptable_rt_loop_frequency_ = 750.0;
00242 }
00243 else
00244 {
00245 ROS_WARN("min_acceptable_rt_loop_frequency changed to %f", min_acceptable_rt_loop_frequency_);
00246 }
00247
00248
00249 if (!node.getParam("rt_period", period_))
00250 {
00251 ROS_WARN("failed to read rt_period parameter");
00252 period_ = 1e+6;
00253 }
00254 ROS_INFO("realtime loop period is %f", period_);
00255
00256 if (!node.getParam("pidfile", pidfile_)) {
00257 pidfile_ = "open_controller.pid";
00258 }
00259 ROS_INFO("pidfile is: %s", pidfile_.c_str());
00260
00261 if (!node.getParam("piddir", piddir_)) {
00262 piddir_ = "/var/tmp/run/";
00263 }
00264 ROS_INFO("piddir is: %s", piddir_.c_str());
00265
00266 if (stats_publish_p_) {
00267 rtpublisher_.reset(new realtime_tools::RealtimePublisher<std_msgs::Float64>(node, "realtime", 2));
00268 }
00269
00270
00271 if (setupPidFile() < 0) {
00272 ROS_FATAL("pid file (%s/%s) already exists", piddir_.c_str(), pidfile_.c_str());
00273 exit(1);
00274 }
00275
00276
00277
00278
00279 initializeCM();
00280 loadRobotDescription();
00281 initializeHW();
00282
00283 for (size_t i = 0; i < cm_->state_->joint_states_.size(); i++) {
00284 cm_->state_->joint_states_[i].calibrated_ = true;
00285 }
00286
00287
00288 publishDiagnostics();
00289
00290
00291 boost::thread t(&OpenController::diagnosticLoop, this);
00292
00293
00294 #if 0 // disable
00295 struct sched_param thread_param;
00296 policy = SCHED_FIFO;
00297 thread_param.sched_priority = sched_get_priority_max(policy);
00298 if ( pthread_setschedparam(pthread_self(), policy, &thread_param) < -1 ) {
00299 perror("sched_setscheduler");
00300 }
00301 #endif
00302 }
00303
00304 void OpenController::loadRobotDescription() {
00305
00306 TiXmlElement *root;
00307 TiXmlElement *root_element;
00308
00309 TiXmlDocument xml;
00310 struct stat st;
00311 if (0 == stat(robot_xml_file_.c_str(), &st))
00312 {
00313 xml.LoadFile(robot_xml_file_.c_str());
00314 }
00315 else
00316 {
00317 ROS_INFO("Xml file not found, reading from parameter server");
00318 ros::NodeHandle top_level_node;
00319 if (top_level_node.getParam(robot_xml_file_.c_str(), g_robot_desc_))
00320 xml.Parse(g_robot_desc_.c_str());
00321 else
00322 {
00323 ROS_FATAL("Could not load the xml from parameter server: %s", robot_xml_file_.c_str());
00324 throw "end";
00325 }
00326 }
00327 root_element = xml.RootElement();
00328 root = xml.FirstChildElement("robot");
00329 if (!root || !root_element)
00330 {
00331 ROS_FATAL("Could not parse the xml from %s", robot_xml_file_.c_str());
00332 throw "end";
00333 }
00334
00335
00336 if (!cm_->initXml(root))
00337 {
00338 ROS_FATAL("Could not initialize the controller manager");
00339 throw "end";
00340 }
00341 else {
00342 ROS_INFO("success to initialize the controller manager");
00343 }
00344 }
00345
00346 double OpenController::now() {
00347 struct timespec n;
00348 clock_gettime(CLOCK_MONOTONIC, &n);
00349 return double(n.tv_nsec) / NSEC_PER_SECOND + n.tv_sec;
00350 }
00351
00352 void OpenController::timespecInc(struct timespec &tick, int nsec)
00353 {
00354 tick.tv_nsec += nsec;
00355 while (tick.tv_nsec >= NSEC_PER_SECOND)
00356 {
00357 tick.tv_nsec -= NSEC_PER_SECOND;
00358 tick.tv_sec++;
00359 }
00360 }
00361 double OpenController::publishJitter(double start)
00362 {
00363 double jitter = now() - start;
00364 g_stats_.jitter_acc(jitter);
00365
00366 if (!!rtpublisher_)
00367 {
00368 if (rtpublisher_->trylock())
00369 {
00370 rtpublisher_->msg_.data = jitter;
00371 rtpublisher_->unlockAndPublish();
00372 }
00373 }
00374 return jitter;
00375 }
00376
00377
00378 void OpenController::startMain() {
00379
00380
00381 struct sched_param thread_param;
00382 int policy = SCHED_FIFO;
00383 thread_param.sched_priority = sched_get_priority_max(policy);
00384 if ( pthread_setschedparam(pthread_self(), policy, &thread_param) < -1 ) {
00385 perror("sched_setscheduler");
00386 ROS_ERROR("failed to sched_setscheduler");
00387 }
00388
00389
00390
00391 RTLoopHistory rt_loop_history(3, 1.0 / (period_ / NSEC_PER_SECOND));
00392 double rt_loop_monitor_period_ = 0.6 / 3;
00393 unsigned long rt_cycle_count = 0;
00394 struct timespec tick;
00395 clock_gettime(CLOCK_REALTIME, &tick);
00396
00397 timespecInc(tick, period_);
00398 clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tick, NULL);
00399 double last_published = now();
00400 double last_loop_start = now();
00401 double last_rt_monitor_time = now();
00402 struct timespec last_exec_time;
00403 clock_gettime(CLOCK_REALTIME, &last_exec_time);
00404 g_stats_.loop_count = 0;
00405 while (!g_quit_ && !g_reset_motors_) {
00406 g_stats_.loop_count++;
00407
00408 double this_loop_start = now();
00409 g_stats_.loop_acc(this_loop_start - last_loop_start);
00410 last_loop_start = this_loop_start;
00411 bool success_update_joint = false;
00412 double start = now();
00413 if (g_reset_motors_) {
00414
00415 g_stats_.rt_loop_not_making_timing = false;
00416 break;
00417 }
00418 else {
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428 struct timespec exec_time;
00429
00430 ControllerStatusPtr status = updateJoints(&exec_time);
00431 success_update_joint = status->isHealthy();
00432 g_reset_motors_ = !success_update_joint;
00433
00434
00435 last_exec_time.tv_sec = exec_time.tv_sec;
00436 last_exec_time.tv_nsec = exec_time.tv_nsec;
00437 }
00438
00439 if (g_publish_trace_requested_)
00440 {
00441 g_publish_trace_requested_ = false;
00442 #if 0
00443 ec.publishTrace(-1,"",0,0);
00444 #endif
00445 }
00446 double after_ec = now();
00447 if (success_update_joint) {
00448 cm_->update();
00449 }
00450 double end = now();
00451
00452 g_stats_.ec_acc(after_ec - start);
00453 g_stats_.cm_acc(end - after_ec);
00454
00455 if ((end - last_published) > 1.0)
00456 {
00457 publishDiagnostics();
00458 last_published = end;
00459 }
00460
00461
00462
00463
00464
00465 ++rt_cycle_count;
00466 if ((start - last_rt_monitor_time) > rt_loop_monitor_period_)
00467 {
00468
00469 double rt_loop_frequency = double(rt_cycle_count) / rt_loop_monitor_period_;
00470
00471
00472 rt_loop_history.sample(rt_loop_frequency);
00473 double avg_rt_loop_frequency = rt_loop_history.average();
00474 if (avg_rt_loop_frequency < min_acceptable_rt_loop_frequency_)
00475 {
00476 if (!g_stats_.rt_loop_not_making_timing)
00477 {
00478
00479 g_stats_.halt_rt_loop_frequency = avg_rt_loop_frequency;
00480 }
00481 g_stats_.rt_loop_not_making_timing = true;
00482 }
00483 g_stats_.rt_loop_frequency = avg_rt_loop_frequency;
00484 rt_cycle_count = 0;
00485 last_rt_monitor_time = start;
00486 }
00487
00488 timespecInc(tick, period_);
00489
00490 struct timespec before;
00491 clock_gettime(CLOCK_REALTIME, &before);
00492 double overrun_time = (before.tv_sec + double(before.tv_nsec)/NSEC_PER_SECOND) -
00493 (tick.tv_sec + double(tick.tv_nsec)/NSEC_PER_SECOND);
00494 if (overrun_time > 0.0)
00495 {
00496 ROS_WARN(" overrun: %f", overrun_time);
00497 double jitter = publishJitter(start);
00498 ROS_WARN(" jitter: %f", jitter);
00499 ROS_WARN(" loop: %d", g_stats_.loop_count);
00500
00501 g_stats_.overrun_loop_sec = overrun_time;
00502
00503
00504
00505
00506 tick.tv_sec = before.tv_sec;
00507
00508 tick.tv_nsec = before.tv_nsec;
00509 timespecInc(tick, period_);
00510
00511
00512 if (g_stats_.overruns == 0){
00513 g_stats_.last_overrun = 1000;
00514 g_stats_.last_severe_overrun = 1000;
00515 }
00516
00517 if (g_stats_.recent_overruns > 10)
00518 g_stats_.last_severe_overrun = 0;
00519 g_stats_.last_overrun = 0;
00520
00521 g_stats_.overruns++;
00522 g_stats_.recent_overruns++;
00523 g_stats_.overrun_ec = after_ec - start;
00524 g_stats_.overrun_cm = end - after_ec;
00525 }
00526
00527 struct timespec sleep_before;
00528 clock_gettime(CLOCK_REALTIME, &sleep_before);
00529
00530 if (!not_sleep_clock_)
00531 clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tick, NULL);
00532 if (overrun_time <= 0.0)
00533 publishJitter(start);
00534
00535 struct timespec sleep_after;
00536 clock_gettime(CLOCK_REALTIME, &sleep_after);
00537 double sleep_time = (sleep_after.tv_sec - sleep_before.tv_sec
00538 + double(sleep_after.tv_nsec-sleep_before.tv_nsec)/NSEC_PER_SECOND);
00539
00540 if (overrun_time > 0.0) {
00541 ROS_WARN(" sleep_time: %f", sleep_time);
00542 }
00543
00544
00545 if (g_halt_requested_)
00546 {
00547 fprintf(stderr, "detect halt request\n");
00548 g_quit_ = true;
00549 g_halt_requested_ = false;
00550 }
00551 }
00552 ROS_INFO("good bye startMain()");
00553 }
00554
00555 void OpenController::finalize() {
00556 ROS_WARN("finalizing");
00557 finalizeHW();
00558
00559 for (pr2_hardware_interface::ActuatorMap::const_iterator it = hw_->actuators_.begin(); it != hw_->actuators_.end(); ++it)
00560 {
00561 it->second->command_.enable_ = false;
00562 it->second->command_.effort_ = 0;
00563 }
00564 #if 0
00565 ec.update(false, true);
00566 #endif
00567
00568 if (!!publisher_) {
00569 publisher_->stop();
00570 publisher_.reset();
00571 }
00572 rtpublisher_.reset();
00573
00574
00575 fprintf(stderr, "exiting from finalize\n");
00576 }
00577
00578 void OpenController::diagnosticLoop() {
00579
00580 struct timespec tick;
00581 clock_gettime(CLOCK_MONOTONIC, &tick);
00582 while (!g_quit_) {
00583
00584 tick.tv_sec += 1;
00585 clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &tick, NULL);
00586 }
00587 }
00588
00589 int OpenController::lock_fd(int fd) {
00590 struct flock lock;
00591 int rv;
00592
00593 lock.l_type = F_WRLCK;
00594 lock.l_whence = SEEK_SET;
00595 lock.l_start = 0;
00596 lock.l_len = 0;
00597
00598 rv = fcntl(fd, F_SETLK, &lock);
00599 return rv;
00600 }
00601
00602 int OpenController::setupPidFile() {
00603 int rv = -1;
00604 pid_t pid;
00605 int fd;
00606 FILE *fp = NULL;
00607 boost::filesystem::path fullpath = boost::filesystem::path(piddir_) / boost::filesystem::path(pidfile_);
00608 umask(0);
00609 mkdir(piddir_.c_str(), 0777);
00610 fd = open(fullpath.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP | S_IWOTH | S_IROTH);
00611 if (fd == -1)
00612 {
00613 if (errno != EEXIST)
00614 {
00615 ROS_FATAL("Unable to create pid file '%s': %s", fullpath.c_str(), strerror(errno));
00616 goto end;
00617 }
00618
00619 if ((fd = open(fullpath.c_str(), O_RDWR)) < 0)
00620 {
00621 ROS_FATAL("Unable to open pid file '%s': %s", fullpath.c_str(), strerror(errno));
00622 goto end;
00623 }
00624
00625 if ((fp = fdopen(fd, "rw")) == NULL)
00626 {
00627 ROS_FATAL("Can't read from '%s': %s", fullpath.c_str(), strerror(errno));
00628 goto end;
00629 }
00630 pid = -1;
00631 if ((fscanf(fp, "%d", &pid) != 1) || (pid == getpid()) || (lock_fd(fileno(fp)) == 0))
00632 {
00633 int rc;
00634
00635 if ((rc = unlink(fullpath.c_str())) == -1)
00636 {
00637 ROS_FATAL("Can't remove stale pid file '%s': %s", fullpath.c_str(), strerror(errno));
00638 goto end;
00639 }
00640 } else {
00641 ROS_FATAL("Another instance of pr2_etherCAT is already running with pid: %d", pid);
00642 goto end;
00643 }
00644 }
00645
00646 unlink(fullpath.c_str());
00647 fd = open(fullpath.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP | S_IWOTH | S_IROTH);
00648
00649 if (fd == -1)
00650 {
00651 ROS_FATAL("Unable to open pid file '%s': %s", fullpath.c_str(), strerror(errno));
00652 goto end;
00653 }
00654
00655 if (lock_fd(fd) == -1)
00656 {
00657 ROS_FATAL("Unable to lock pid file '%s': %s", fullpath.c_str(), strerror(errno));
00658 goto end;
00659 }
00660
00661 if ((fp = fdopen(fd, "w")) == NULL)
00662 {
00663 ROS_FATAL("fdopen failed: %s", strerror(errno));
00664 goto end;
00665 }
00666
00667 fprintf(fp, "%d\n", getpid());
00668
00669
00670 fflush(fp);
00671 fcntl(fd, F_SETFD, (long) 1);
00672 rv = 0;
00673 end:
00674 return rv;
00675 }
00676
00677 void OpenController::cleanupPidFile() {
00678 boost::filesystem::path path = boost::filesystem::path(piddir_) / boost::filesystem::path(pidfile_);
00679 unlink(path.c_str());
00680 }
00681
00682 void OpenController::Usage(std::string msg) {
00683 fprintf(stderr, "Usage: main [options]\n");
00684 fprintf(stderr, " Available options\n");
00685 fprintf(stderr, " -s, --stats Publish statistics on the RT loop jitter on \"pr2_etherCAT/realtime\" in seconds\n");
00686 fprintf(stderr, " -x, --xml <file|param> Load the robot description from this file or parameter name\n");
00687 fprintf(stderr, " -d, --dryrun Run in dry run mode, not communicating with the controller.\n");
00688 fprintf(stderr, " -h, --help Print this message and exit\n");
00689 if (msg != "")
00690 {
00691 fprintf(stderr, "Error: %s\n", msg.c_str());
00692 }
00693 }
00694
00695 void OpenController::quitRequest() {
00696 g_halt_requested_ = true;
00697 }
00698
00699 bool OpenController::initRT() {
00700 if (mlockall(MCL_CURRENT | MCL_FUTURE) < 0) {
00701 perror("mlockall");
00702 return false;
00703 }
00704 else {
00705 return true;
00706 }
00707 }
00708
00709 Finalizer::~Finalizer() {
00710 ROS_WARN("Finalizer::~Finalizer is called");
00711 controller->finalize();
00712 }
00713 }
00714