main.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2008, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Modified 2016, by Shadow Robot Company Ltd.
8  * Modified 2017, by Tokyo Opensource Robotics Kyokai Association
9  * Modified 2019, by Kawasaki Heavy Industries, LTD.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * * Redistributions of source code must retain the above copyright
16  * notice, this list of conditions and the following disclaimer.
17  * * Redistributions in binary form must reproduce the above
18  * copyright notice, this list of conditions and the following
19  * disclaimer in the documentation and/or other materials provided
20  * with the distribution.
21  * * Neither the name of the Willow Garage nor the names of its
22  * contributors may be used to endorse or promote products derived
23  * from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  *********************************************************************/
38 
39 #include <getopt.h>
40 #include <execinfo.h>
41 #include <csignal>
42 #include <pthread.h>
43 #include <numeric>
44 
45 #include <boost/accumulators/accumulators.hpp>
46 #include <boost/accumulators/statistics/stats.hpp>
47 #include <boost/accumulators/statistics/max.hpp>
48 #include <boost/accumulators/statistics/mean.hpp>
51 #include <diagnostic_msgs/DiagnosticArray.h>
52 #include <string>
53 #include <vector>
54 
57 
58 using boost::accumulators::accumulator_set;
59 using boost::accumulators::stats;
60 using boost::accumulators::extract_result;
61 using boost::accumulators::tag::max;
62 using boost::accumulators::tag::mean;
63 using std::string;
64 using std::vector;
65 using std::accumulate;
67 
68 static struct
69 {
70  char *program_;
71  bool write_;
72  double period_;
73  std::string ip_;
75  std::string robot_;
76 }
77 g_options;
78 
79 void Usage( const string &msg = "" )
80 {
81  fprintf(stderr, "Usage: %s [options]\n", g_options.program_);
82  fprintf(stderr, " Available options\n");
83  fprintf(stderr, " -i, --ip IP address for Controller\n");
84  fprintf(stderr, " -l, --loopback Use loopback interface for Controller (i.e. simulation mode)\n");
85  fprintf(stderr, " -p, --period RT loop period in msec\n");
86  fprintf(stderr, " -v, --viewer Viewing robot through Rviz\n");
87  fprintf(stderr, " -r, --robot Robot name\n");
88  fprintf(stderr, " -h, --help Print this message and exit\n");
89  if ( msg != "" )
90  {
91  fprintf(stderr, "Error: %s\n", msg.c_str());
92  exit(-1);
93  }
94  else
95  {
96  exit(0);
97  }
98 }
99 
100 static int g_quit = 0;
102 static const int SEC_2_NSEC = 1e+9;
103 static const int SEC_2_USEC = 1e+6;
104 static const double PERIOD_SLEEP = 0.1; /* 100msec */
105 static const double PERIOD_DIFF_WEIGHT = 1e-2;
106 
107 static struct
108 {
109  accumulator_set<double, stats<max, mean> > read_acc;
110  accumulator_set<double, stats<max, mean> > write_acc;
111  accumulator_set<double, stats<max, mean> > loop_acc;
112  accumulator_set<double, stats<max, mean> > jitter_acc;
113  int overruns;
114  int recent_overruns; /* overruns during publishing */
115  int last_overrun; /* diagnostics count after last overruns occurred */
116  int last_severe_overrun; /* diagnostics severe count after last overruns occurred */
118  double overrun_read;
120 
121  /* These values are set when realtime loop does not meet performance expectations */
125 }
126 g_stats;
127 
129 {
130  if ( publisher.trylock() )
131  {
132  accumulator_set<double, stats<max, mean> > zero;
133  vector<diagnostic_msgs::DiagnosticStatus> statuses;
135 
136  static double max_read = 0, max_write = 0, max_loop = 0, max_jitter = 0;
137  double avg_read, avg_write, avg_loop, avg_jitter;
138 
139  avg_read = extract_result<mean>(g_stats.read_acc);
140  avg_write = extract_result<mean>(g_stats.write_acc);
141  avg_loop = extract_result<mean>(g_stats.loop_acc);
142  max_read = std::max(max_read, extract_result<max>(g_stats.read_acc));
143  max_write = std::max(max_write, extract_result<max>(g_stats.write_acc));
144  max_loop = std::max(max_loop, extract_result<max>(g_stats.loop_acc));
145  g_stats.read_acc = zero;
146  g_stats.write_acc = zero;
147  g_stats.loop_acc = zero;
148 
149  /* Publish average loop jitter */
150  avg_jitter = extract_result<mean>(g_stats.jitter_acc);
151  max_jitter = std::max(max_jitter, extract_result<max>(g_stats.jitter_acc));
152  g_stats.jitter_acc = zero;
153 
154  status.addf("Max READ roundtrip (us)", "%.2f", max_read * SEC_2_USEC);
155  status.addf("Avg READ roundtrip (us)", "%.2f", avg_read * SEC_2_USEC);
156  status.addf("Max WRITE roundtrip (us)", "%.2f", max_write * SEC_2_USEC);
157  status.addf("Avg WRITE roundtrip (us)", "%.2f", avg_write * SEC_2_USEC);
158  status.addf("Max Total Loop roundtrip (us)", "%.2f", max_loop * SEC_2_USEC);
159  status.addf("Avg Total Loop roundtrip (us)", "%.2f", avg_loop * SEC_2_USEC);
160  status.addf("Max Loop Jitter (us)", "%.2f", max_jitter * SEC_2_USEC);
161  status.addf("Avg Loop Jitter (us)", "%.2f", avg_jitter * SEC_2_USEC);
162  status.addf("Control Loop Overruns", "%d", g_stats.overruns);
163  status.addf("Recent Control Loop Overruns", "%d", g_stats.recent_overruns);
164  status.addf("Last Control Loop Overrun Cause", "READ: %.2fus, WRITE: %.2fus",
165  g_stats.overrun_read*SEC_2_USEC, g_stats.overrun_write * SEC_2_USEC);
166  status.addf("Last Overrun Loop Time (us)", "%.2f", g_stats.overrun_loop_sec * SEC_2_USEC);
167  status.addf("Realtime Loop Frequency", "%.4f", g_stats.rt_loop_frequency);
168 
169  status.name = "Realtime Control Loop";
170  if (g_stats.overruns > 0 && g_stats.last_overrun < 30)
171  {
172  if (g_stats.last_severe_overrun < 30)
173  status.level = 1;
174  else
175  status.level = 0;
176  status.message = "Realtime loop used too much time in the last 30 seconds.";
177  }
178  else
179  {
180  status.level = 0;
181  status.message = "OK";
182  }
183  g_stats.recent_overruns = 0;
184  ++g_stats.last_overrun;
185  ++g_stats.last_severe_overrun;
186 
187  if (g_stats.rt_loop_not_making_timing)
188  status.mergeSummaryf(status.ERROR, "realtime loop only ran at %.4f Hz", g_stats.halt_rt_loop_frequency);
189 
190  statuses.push_back(status);
191  publisher.msg_.status = statuses;
192  publisher.msg_.header.stamp = ros::Time::now();
193  publisher.unlockAndPublish();
194  }
195 }
196 
197 static inline double now()
198 {
199  struct timespec n;
200  clock_gettime( CLOCK_MONOTONIC, &n );
201  return static_cast<double>(n.tv_nsec) / SEC_2_NSEC + n.tv_sec;
202 }
203 
204 static void timespecInc( struct timespec& tick, const int& nsec )
205 {
206  tick.tv_nsec += nsec;
207  if ( tick.tv_nsec > 0 )
208  {
209  while (tick.tv_nsec >= SEC_2_NSEC)
210  {
211  tick.tv_nsec -= SEC_2_NSEC;
212  ++tick.tv_sec;
213  }
214  }
215  else
216  {
217  while (tick.tv_nsec < 0)
218  {
219  tick.tv_nsec += SEC_2_NSEC;
220  --tick.tv_sec;
221  }
222  }
223 }
224 
226 {
227 public:
228  RTLoopHistory(unsigned length, double default_value) :
229  index_(0),
230  length_(length),
231  history_(length, default_value)
232  {
233  }
234 
235  void sample(double value)
236  {
237  index_ = (index_ + 1) % length_;
238  history_[index_] = value;
239  }
240 
241  double average() const
242  {
243  return accumulate(history_.begin(), history_.end(), 0.0) / static_cast<double>(length_);
244  }
245 
246 protected:
247  unsigned index_;
248  unsigned length_;
249  vector<double> history_;
250 };
251 
252 inline bool activate( khi_robot_control::KhiRobotHardwareInterface& robot, struct timespec* tick )
253 {
254  bool ret = true;
255 
256  if ( g_options.write_ )
257  {
258  ret = robot.activate();
259  if ( ret == false )
260  {
261  ROS_ERROR( "Failed to activate KHI robot" );
262  }
263  }
264 
265  clock_gettime( CLOCK_REALTIME, tick );
266 
267  /* Snap to the nearest second */
268  tick->tv_nsec = (tick->tv_nsec / g_options.period_ + 1) * g_options.period_;
269  clock_nanosleep( CLOCK_REALTIME, TIMER_ABSTIME, tick, NULL );
270 
271  last_published = now();
273  last_loop_start = now();
274 
275  return ret;
276 }
277 
278 void quitRequested( int sig )
279 {
280  ROS_WARN( "received signal %d", sig );
281  g_quit = 1;
282 }
283 
284 void *controlLoop( void* )
285 {
286  ros::NodeHandle nh;
287  int state_trigger = khi_robot_control::NONE;
288 
289  /* Catch attempts to quit */
290  signal( SIGTERM, quitRequested );
291  signal( SIGINT, quitRequested );
292  signal( SIGHUP, quitRequested );
293 
294  /* Diagnostics */
295  RealtimePublisher<diagnostic_msgs::DiagnosticArray> publisher( nh, "/diagnostics", 2 );
296 
297  /* Realtime Loop Frequency */
298  const double rt_loop_monitor_period = 0.2; /* Calculate realtime loop frequency every 200msec */
299  const double period_in_secs = 1e+9 * g_options.period_;
300  const double given_frequency = 1 / period_in_secs;
301  double min_acceptable_rt_loop_frequency = 0.75 * given_frequency;
302  if ( nh.getParam("min_acceptable_rt_loop_frequency", min_acceptable_rt_loop_frequency ) )
303  {
304  ROS_WARN("min_acceptable_rt_loop_frequency changed to %f", min_acceptable_rt_loop_frequency);
305  }
306  unsigned rt_cycle_count = 0;
307  RTLoopHistory rt_loop_history( 3, 1000.0 ); /* Keep history of last 3 calculation intervals. */
308 
309  /* Realtime Scheduler */
310  struct sched_param thread_param;
311  int policy = SCHED_FIFO;
312  thread_param.sched_priority = sched_get_priority_max( policy );
313  if ( pthread_setschedparam( pthread_self(), policy, &thread_param ) == 0 )
314  {
315  ROS_INFO( "KHI robot control started. [REALTIME]" );
316  }
317  else
318  {
319  ROS_INFO( "KHI robot control started. [NOT REALTIME]" );
320  }
321 
322  /* Publish one-time before entering real-time to pre-allocate message vectors */
323  publishDiagnostics( publisher );
324 
325  /* Initialization */
326  struct timespec tick;
327  ros::Duration durp( g_options.period_ / 1e+9 );
330  double period_diff = 0;
331  if ( !robot.open( g_options.robot_, g_options.ip_, g_options.period_, g_options.simulation_ ) )
332  {
333  ROS_ERROR( "Failed to open KHI robot" );
334  publisher.stop();
335  robot.close();
336  delete &robot;
337  ros::shutdown();
338  return NULL;
339  }
340  if ( !activate( robot, &tick ) )
341  {
342  publisher.stop();
343  robot.deactivate();
344  robot.close();
345  delete &robot;
346  ros::shutdown();
347  return NULL;
348  }
349 
350  while ( !g_quit )
351  {
352  double this_loop_start = now();
353  g_stats.loop_acc( this_loop_start - last_loop_start );
354  last_loop_start = this_loop_start;
355 
356  double start = now();
357 
358  ros::Time this_moment( tick.tv_sec, tick.tv_nsec );
359  robot.read( this_moment, durp );
360 
361  double after_read = now();
362 
363  cm.update( this_moment, durp );
364  if ( g_options.write_ )
365  {
366  /* Robot State */
367  robot.updateState();
368  state_trigger = robot.getStateTrigger();
369  if ( state_trigger == khi_robot_control::HOLD )
370  {
371  robot.hold();
372  continue;
373  }
374  else if ( state_trigger == khi_robot_control::RESTART )
375  {
376  if ( activate( robot, &tick ) )
377  {
378  ros::Time activate_moment( tick.tv_sec, tick.tv_nsec );
379  robot.read( activate_moment, durp );
380  cm.update( activate_moment, durp, true );
381  }
382  continue;
383  }
384  else if ( state_trigger == khi_robot_control::QUIT )
385  {
386  g_quit = true;
387  continue;
388  }
389 
390  robot.write( this_moment, durp );
391  }
392 
393  /* Cycle Adjustment */
394  if ( robot.getPeriodDiff( period_diff ) )
395  {
396  timespecInc( tick, PERIOD_DIFF_WEIGHT * period_diff );
397  }
398 
399  double end = now();
400 
401  g_stats.read_acc( after_read - start );
402  g_stats.write_acc( end - after_read );
403 
404  if ( ( end - last_published ) > 1.0 )
405  {
406  publishDiagnostics( publisher );
407  last_published = end;
408  }
409 
410  /* Check Realtime Loop Frequency */
411  ++rt_cycle_count;
412  if ( ( start - last_rt_monitor_time ) > rt_loop_monitor_period )
413  {
414  /* Calculate new average rt loop frequency */
415  double rt_loop_frequency = static_cast<double>(rt_cycle_count) / rt_loop_monitor_period;
416 
417  /* Use last X samples of frequency when deciding whether or not to halt */
418  rt_loop_history.sample(rt_loop_frequency);
419  double avg_rt_loop_frequency = rt_loop_history.average();
420  if ( avg_rt_loop_frequency < min_acceptable_rt_loop_frequency )
421  {
422  if ( !g_stats.rt_loop_not_making_timing )
423  {
424  /* Only update this value if motors when this first occurs (used for diagnostics error message) */
425  g_stats.halt_rt_loop_frequency = avg_rt_loop_frequency;
426  }
427  g_stats.rt_loop_not_making_timing = true;
428  }
429  g_stats.rt_loop_frequency = avg_rt_loop_frequency;
430  rt_cycle_count = 0;
431  last_rt_monitor_time = start;
432  }
433 
434  /* Compute end of next g_options.period_ */
435  timespecInc( tick, g_options.period_ );
436 
437  /* Check Overrun */
438  struct timespec before;
439  clock_gettime( CLOCK_REALTIME, &before );
440  if ( ( before.tv_sec + static_cast<double>(before.tv_nsec) / SEC_2_NSEC) >
441  ( tick.tv_sec + static_cast<double>(tick.tv_nsec) / SEC_2_NSEC ) )
442  {
443  /* Total amount of time the loop took to run */
444  g_stats.overrun_loop_sec = ( before.tv_sec + static_cast<double>(before.tv_nsec) / SEC_2_NSEC ) -
445  ( tick.tv_sec + static_cast<double>(tick.tv_nsec) / SEC_2_NSEC );
446 
447  /* We overran, snap to next "g_options.period_" */
448  tick.tv_sec = before.tv_sec;
449  tick.tv_nsec = ( before.tv_nsec / g_options.period_ ) * g_options.period_;
450  timespecInc( tick, g_options.period_ );
451 
452  /* Initialize overruns */
453  if ( g_stats.overruns == 0 )
454  {
455  g_stats.last_overrun = 1000;
456  g_stats.last_severe_overrun = 1000;
457  }
458  /* Check for overruns */
459  if ( g_stats.recent_overruns > 10 )
460  {
461  g_stats.last_severe_overrun = 0;
462  }
463  g_stats.last_overrun = 0;
464 
465  ++g_stats.overruns;
466  ++g_stats.recent_overruns;
467  g_stats.overrun_read = after_read - start;
468  g_stats.overrun_write = end - after_read;
469  }
470 
471  /* Sleep until end of g_options.period_ */
472  clock_nanosleep( CLOCK_REALTIME, TIMER_ABSTIME, &tick, NULL );
473 
474  /* Check Jitter */
475  struct timespec after;
476  clock_gettime( CLOCK_REALTIME, &after );
477  double jitter = ( after.tv_sec - tick.tv_sec + static_cast<double>(after.tv_nsec - tick.tv_nsec) / SEC_2_NSEC );
478  g_stats.jitter_acc( jitter );
479  }
480  publisher.stop();
481  robot.deactivate();
482  robot.close();
483  delete &robot;
484  ROS_INFO( "KHI robot control ended." );
485  ros::shutdown();
486  return NULL;
487 }
488 
489 static pthread_t controlThread;
490 static pthread_attr_t controlThreadAttr;
491 
492 int main(int argc, char *argv[])
493 {
494  /* Initialize ROS and parse command-line arguments */
495  ros::init( argc, argv, "KhiRobotControl" );
496 
497  /* Options */
498  g_options.program_ = argv[0];
499  g_options.period_ = 4e+6; /* 4ms */
500  g_options.simulation_ = false;
501  g_options.write_ = true;
502 
503  while (true)
504  {
505  static struct option long_options[] =
506  {
507  {"help", no_argument, 0, 'h'},
508  {"interface", required_argument, 0, 'i'},
509  {"loopback", no_argument, 0, 'l'},
510  {"viewer", no_argument, 0, 'v'},
511  {"period", required_argument, 0, 'p'},
512  {"robot", required_argument, 0, 'r'},
513  };
514  int option_index = 0;
515  int c = getopt_long( argc, argv, "hi:lvp:r:", long_options, &option_index );
516  if (c == -1)
517  {
518  break;
519  }
520 
521  switch (c)
522  {
523  case 'h':
524  Usage();
525  break;
526  case 'i':
527  g_options.ip_ = std::string(optarg);
528  break;
529  case 'l':
530  g_options.simulation_ = true;
531  break;
532  case 'v':
533  ROS_INFO( "Viewer mode" );
534  g_options.write_ = false;
535  break;
536  case 'p':
537  /* convert period given in msec to nsec */
538  g_options.period_ = fabs(atof(optarg))*1e+6;
539  break;
540  case 'r':
541  g_options.robot_ = std::string(optarg);
542  break;
543  default:
544  break;
545  }
546  }
547 
548  if ( optind < argc )
549  {
550  Usage( "Extra arguments" );
551  }
552 
553  /* Start controlLoop thread */
554  int rv = pthread_create( &controlThread, &controlThreadAttr, controlLoop, 0 );
555  if ( rv != 0 )
556  {
557  ROS_FATAL( "Unable to create control thread: rv = %d", rv );
558  exit( EXIT_FAILURE );
559  }
560 
561  ros::AsyncSpinner spinner(boost::thread::hardware_concurrency());
562  spinner.start();
563  pthread_join(controlThread, reinterpret_cast<void **>(&rv));
565  return rv;
566 }
567 
msg
accumulator_set< double, stats< max, mean > > write_acc
Definition: main.cpp:110
double overrun_loop_sec
Definition: main.cpp:117
void quitRequested(int sig)
Definition: main.cpp:278
#define ROS_FATAL(...)
std::string robot_
Definition: main.cpp:75
ROSCPP_DECL void start()
double overrun_write
Definition: main.cpp:119
static double last_published
Definition: main.cpp:101
static void timespecInc(struct timespec &tick, const int &nsec)
Definition: main.cpp:204
void write(const ros::Time &time, const ros::Duration &period) override
void Usage(const string &msg="")
Definition: main.cpp:79
static const double PERIOD_DIFF_WEIGHT
Definition: main.cpp:105
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
static const int SEC_2_NSEC
Definition: main.cpp:102
static pthread_t controlThread
Definition: main.cpp:489
unsigned length_
Definition: main.cpp:248
double average() const
Definition: main.cpp:241
static int g_quit
Definition: main.cpp:100
#define ROS_WARN(...)
void addf(const std::string &key, const char *format,...)
static void publishDiagnostics(RealtimePublisher< diagnostic_msgs::DiagnosticArray > &publisher)
Definition: main.cpp:128
bool simulation_
Definition: main.cpp:74
void spinner()
bool rt_loop_not_making_timing
Definition: main.cpp:122
static const int SEC_2_USEC
Definition: main.cpp:103
bool write_
Definition: main.cpp:71
void sample(double value)
Definition: main.cpp:235
double period_
Definition: main.cpp:72
#define ROS_INFO(...)
static double last_rt_monitor_time
Definition: main.cpp:101
int overruns
Definition: main.cpp:113
unsigned index_
Definition: main.cpp:247
static pthread_attr_t controlThreadAttr
Definition: main.cpp:490
int last_severe_overrun
Definition: main.cpp:116
accumulator_set< double, stats< max, mean > > read_acc
Definition: main.cpp:109
bool activate(khi_robot_control::KhiRobotHardwareInterface &robot, struct timespec *tick)
Definition: main.cpp:252
static struct @0 g_options
RTLoopHistory(unsigned length, double default_value)
Definition: main.cpp:228
void read(const ros::Time &time, const ros::Duration &period) override
bool getParam(const std::string &key, std::string &s) const
char * program_
Definition: main.cpp:70
static struct @1 g_stats
static Time now()
void * controlLoop(void *)
Definition: main.cpp:284
ROSCPP_DECL void shutdown()
void mergeSummaryf(unsigned char lvl, const char *format,...)
static const double PERIOD_SLEEP
Definition: main.cpp:104
double rt_loop_frequency
Definition: main.cpp:124
vector< double > history_
Definition: main.cpp:249
static double last_loop_start
Definition: main.cpp:101
void update(const ros::Time &time, const ros::Duration &period, bool reset_controllers=false)
accumulator_set< double, stats< max, mean > > loop_acc
Definition: main.cpp:111
int main(int argc, char *argv[])
Definition: main.cpp:492
static double now()
Definition: main.cpp:197
#define ROS_ERROR(...)
double halt_rt_loop_frequency
Definition: main.cpp:123
std::string ip_
Definition: main.cpp:73
double overrun_read
Definition: main.cpp:118
ROSCPP_DECL void waitForShutdown()
int recent_overruns
Definition: main.cpp:114
accumulator_set< double, stats< max, mean > > jitter_acc
Definition: main.cpp:112
bool open(const std::string &robot_name, const std::string &ip_address, const double &period, const bool in_simulation=false)
int last_overrun
Definition: main.cpp:115


khi_robot_control
Author(s): nakamichi_d
autogenerated on Fri Mar 26 2021 02:34:21