head_control_module.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2 * Copyright 2017 ROBOTIS CO., LTD.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *******************************************************************************/
16 
17 /* Author: Kayman */
18 
19 #include <stdio.h>
21 
22 namespace robotis_op
23 {
24 
26  : control_cycle_msec_(0),
27  stop_process_(false),
28  is_moving_(false),
29  is_direct_control_(true),
30  tra_count_(0),
31  tra_size_(0),
32  moving_time_(3.0),
33  scan_state_(NoScan),
34  has_goal_position_(false),
35  angle_unit_(35),
36  DEBUG(false)
37 {
38  enable_ = false;
39  module_name_ = "head_control_module";
41 
42  result_["head_pan"] = new robotis_framework::DynamixelState();
43  result_["head_tilt"] = new robotis_framework::DynamixelState();
44 
45  using_joint_name_["head_pan"] = 0;
46  using_joint_name_["head_tilt"] = 1;
47 
48  max_angle_[using_joint_name_["head_pan"]] = 85 * DEGREE2RADIAN;
49  min_angle_[using_joint_name_["head_pan"]] = -85 * DEGREE2RADIAN;
50  max_angle_[using_joint_name_["head_tilt"]] = 30 * DEGREE2RADIAN;
51  min_angle_[using_joint_name_["head_tilt"]] = -75 * DEGREE2RADIAN;
52 
53  target_position_ = Eigen::MatrixXd::Zero(1, result_.size());
54  current_position_ = Eigen::MatrixXd::Zero(1, result_.size());
55  goal_position_ = Eigen::MatrixXd::Zero(1, result_.size());
56  goal_velocity_ = Eigen::MatrixXd::Zero(1, result_.size());
57  goal_acceleration_ = Eigen::MatrixXd::Zero(1, result_.size());
58 
60 }
61 
63 {
64  queue_thread_.join();
65 }
66 
67 void HeadControlModule::initialize(const int control_cycle_msec, robotis_framework::Robot *robot)
68 {
69  ros::NodeHandle param_nh("~");
70  angle_unit_ = param_nh.param("angle_unit", 35.0);
71 
72  ROS_WARN_STREAM("Head control - angle unit : " << angle_unit_);
73 
74  queue_thread_ = boost::thread(boost::bind(&HeadControlModule::queueThread, this));
75 
76  control_cycle_msec_ = control_cycle_msec;
77 
78  ros::NodeHandle ros_node;
79 
80  /* publish topics */
81  status_msg_pub_ = ros_node.advertise<robotis_controller_msgs::StatusMsg>("/robotis/status", 0);
82 
83 }
84 
86 {
87  ros::NodeHandle ros_node;
88  ros::CallbackQueue callback_queue;
89 
90  ros_node.setCallbackQueue(&callback_queue);
91 
92  /* subscribe topics */
93  ros::Subscriber set_head_joint_sub = ros_node.subscribe("/robotis/head_control/set_joint_states", 1,
95  ros::Subscriber set_head_joint_offset_sub = ros_node.subscribe("/robotis/head_control/set_joint_states_offset", 1,
97  ros::Subscriber set_head_scan_sub = ros_node.subscribe("/robotis/head_control/scan_command", 1,
99 
100  ros::WallDuration duration(control_cycle_msec_ / 1000.0);
101  while(ros_node.ok())
102  callback_queue.callAvailable(duration);
103 }
104 
105 void HeadControlModule::setHeadJointCallback(const sensor_msgs::JointState::ConstPtr &msg)
106 {
107  setHeadJoint(msg, false);
108 }
109 
110 void HeadControlModule::setHeadJointOffsetCallback(const sensor_msgs::JointState::ConstPtr &msg)
111 {
112  setHeadJoint(msg, true);
113 }
114 
115 void HeadControlModule::setHeadJoint(const sensor_msgs::JointState::ConstPtr &msg, bool is_offset)
116 {
117  if (enable_ == false)
118  {
119  ROS_INFO_THROTTLE(1, "Head module is not enable.");
120  publishStatusMsg(robotis_controller_msgs::StatusMsg::STATUS_ERROR, "Not Enable");
121  return;
122  }
123 
124  while(has_goal_position_ == false)
125  {
126  std::cout << "wait for receiving current position" << std::endl;
127  usleep(80 * 1000);
128 }
129  // moving time
130  moving_time_ = is_offset ? 0.1 : 1.0; // default : 1 sec
131 
132  // set target joint angle
133  target_position_ = goal_position_; // default
134 
135  for (int ix = 0; ix < msg->name.size(); ix++)
136  {
137  std::string joint_name = msg->name[ix];
138  std::map<std::string, int>::iterator joint_it = using_joint_name_.find(joint_name);
139 
140  if (joint_it != using_joint_name_.end())
141  {
142  double target_position = 0.0;
143  int joint_index = joint_it->second;
144 
145  // set target position
146  if (is_offset == true)
147  target_position = goal_position_.coeff(0, joint_index) + msg->position[ix];
148  else
149  target_position = msg->position[ix];
150 
151  // check angle limit
152  bool is_checked = checkAngleLimit(joint_index, target_position);
153  if(is_checked == false)
154  {
155  ROS_ERROR_STREAM("Failed to find limit angle \n id : " << joint_index << ", value : " << (target_position_ * 180 / M_PI));
156  }
157 
158  // apply target position
159  target_position_.coeffRef(0, joint_index) = target_position;
160 
161  // set time
162  //double angle_unit = 35 * M_PI / 180;
163  double angle_unit = is_offset ? (angle_unit_ * M_PI / 180 * 1.5) : (angle_unit_ * M_PI / 180);
164  double calc_moving_time = fabs(goal_position_.coeff(0, joint_index) - target_position_.coeff(0, joint_index))
165  / angle_unit;
166  if (calc_moving_time > moving_time_)
167  moving_time_ = calc_moving_time;
168 
169  if (DEBUG)
170  std::cout << " - joint : " << joint_name << ", Index : " << joint_index << "\n Target Angle : " << target_position_.coeffRef(0, joint_index) << ", Curr Goal : " << goal_position_.coeff(0, joint_index)
171  << ", Time : " << moving_time_ << ", msg : " << msg->position[ix] << std::endl;
172  }
173  }
174 
175  // set mode
176  is_direct_control_ = true;
178 
179  // generate trajectory
180  tra_gene_thread_ = new boost::thread(boost::bind(&HeadControlModule::jointTraGeneThread, this));
181  delete tra_gene_thread_;
182 }
183 
184 void HeadControlModule::setHeadScanCallback(const std_msgs::String::ConstPtr &msg)
185 {
186  if (enable_ == false)
187  {
188  ROS_ERROR_THROTTLE(1, "Head control module is not enabled, scan command is canceled.");
189  return;
190  }
191  else
192  ROS_INFO_THROTTLE(1, "Scan command is accepted. [%d]", scan_state_);
193 
194  if (msg->data == "scan" && scan_state_ == NoScan)
195  {
196  std::srand(std::time(NULL)); // use current time as seed for random generator
197  scan_state_ = std::rand() % 4 + 1;
198 
199  is_direct_control_ = false;
200 
202  }
203  else if (msg->data == "stop")
204  {
206  }
207 }
208 
209 bool HeadControlModule::checkAngleLimit(const int joint_index, double &goal_position)
210 {
211  std::map<int, double>::iterator angle_it = min_angle_.find(joint_index);
212  if (angle_it == min_angle_.end())
213  return false;
214  double min_angle = angle_it->second;
215 
216  angle_it = max_angle_.find(joint_index);
217  if (angle_it == max_angle_.end())
218  return false;
219  double max_angle = angle_it->second;
220 
221  if (goal_position < min_angle)
222  goal_position = min_angle;
223  if (goal_position > max_angle)
224  goal_position = max_angle;
225 
226  return true;
227 }
228 
229 void HeadControlModule::process(std::map<std::string, robotis_framework::Dynamixel *> dxls,
230  std::map<std::string, double> sensors)
231 {
232  if (enable_ == false)
233  return;
234 
235  tra_lock_.lock();
236 
237  // get joint data
238  for (std::map<std::string, robotis_framework::DynamixelState *>::iterator state_it = result_.begin();
239  state_it != result_.end(); state_it++)
240  {
241  std::string joint_name = state_it->first;
242  int index = using_joint_name_[joint_name];
243 
244  robotis_framework::Dynamixel *_dxl = NULL;
245  std::map<std::string, robotis_framework::Dynamixel*>::iterator dxl_it = dxls.find(joint_name);
246  if (dxl_it != dxls.end())
247  _dxl = dxl_it->second;
248  else
249  continue;
250 
251  current_position_.coeffRef(0, index) = _dxl->dxl_state_->present_position_;
252  goal_position_.coeffRef(0, index) = _dxl->dxl_state_->goal_position_;
253  }
254 
255  has_goal_position_= true;
256 
257  // check to stop
258  if (stop_process_ == true)
259  {
260  stopMoving();
261  }
262  else
263  {
264  // process
265  if (tra_size_ != 0)
266  {
267  // start of steps
268  if (tra_count_ == 0)
269  {
270  startMoving();
271  }
272 
273  // end of steps
274  if (tra_count_ >= tra_size_)
275  {
276  finishMoving();
277  }
278  else
279  {
280  // update goal position
281  goal_position_ = calc_joint_tra_.block(tra_count_, 0, 1, result_.size());
282  goal_velocity_ = calc_joint_vel_tra_.block(tra_count_, 0, 1, result_.size());
284 
285  tra_count_ += 1;
286  }
287  }
288  }
289  tra_lock_.unlock();
290 
291  // set joint data
292  for (std::map<std::string, robotis_framework::DynamixelState *>::iterator state_it = result_.begin();
293  state_it != result_.end(); state_it++)
294  {
295  std::string joint_name = state_it->first;
296  int index = using_joint_name_[joint_name];
297  double goal_position = goal_position_.coeff(0, index);
298  checkAngleLimit(index, goal_position);
299 
300  result_[joint_name]->goal_position_ = goal_position;
301  }
302 }
303 
305 {
306  tra_lock_.lock();
307 
308  if (is_moving_ == true)
309  stop_process_ = true;
310 
311  tra_lock_.unlock();
312 
313  return;
314 }
315 
317 {
318  return is_moving_;
319 }
320 
322 {
324 }
325 
327 {
328  // init value
330  tra_size_ = 0;
331  tra_count_ = 0;
332  is_direct_control_ = true;
333  is_moving_ = false;
334  has_goal_position_ = false;
335 
336  goal_velocity_ = Eigen::MatrixXd::Zero(1, result_.size());
337  goal_acceleration_ = Eigen::MatrixXd::Zero(1, result_.size());
338 
340 
341  std::cout << "head_control_module : disable";
342 }
343 
345 {
346  is_moving_ = true;
347 
348  // start procedure
349 }
350 
352 {
353  // init value
355  tra_size_ = 0;
356  tra_count_ = 0;
357  is_direct_control_ = true;
358  is_moving_ = false;
359 
360  goal_velocity_ = Eigen::MatrixXd::Zero(1, result_.size());
361  goal_acceleration_ = Eigen::MatrixXd::Zero(1, result_.size());
362 
363  // log
364  publishStatusMsg(robotis_controller_msgs::StatusMsg::STATUS_INFO, "Head movement is finished.");
365 
366  if (DEBUG)
367  std::cout << "Trajectory End" << std::endl;
368 
369  // head scan
370  switch (scan_state_)
371  {
372  case TopLeft:
374  break;
375 
376  case BottomRight:
378  break;
379 
380  case BottomLeft:
382  break;
383 
384  case TopRight:
386  break;
387 
388  // NoScan
389  default:
390  return;
391  }
392 
394 }
395 
397 {
398  // init value
400  tra_size_ = 0;
401  tra_count_ = 0;
402  is_moving_ = false;
403  is_direct_control_ = true;
404  stop_process_ = false;
406 
407  goal_velocity_ = Eigen::MatrixXd::Zero(1, result_.size());
408  goal_acceleration_ = Eigen::MatrixXd::Zero(1, result_.size());
409 
410  // log
411  publishStatusMsg(robotis_controller_msgs::StatusMsg::STATUS_WARN, "Stop Module.");
412 }
413 
414 void HeadControlModule::generateScanTra(const int head_direction)
415 {
416  switch (head_direction)
417  {
418  case TopLeft:
419  {
420  target_position_.coeffRef(0, using_joint_name_["head_pan"]) = min_angle_[using_joint_name_["head_pan"]] * 0.6;
421  target_position_.coeffRef(0, using_joint_name_["head_tilt"]) = min_angle_[using_joint_name_["head_tilt"]] * 0.25;
422  break;
423  }
424 
425  case TopRight:
426  {
427  target_position_.coeffRef(0, using_joint_name_["head_pan"]) = max_angle_[using_joint_name_["head_pan"]] * 0.6;
428  target_position_.coeffRef(0, using_joint_name_["head_tilt"]) = min_angle_[using_joint_name_["head_tilt"]] * 0.25;
429  break;
430  }
431 
432  case BottomLeft:
433  {
434  target_position_.coeffRef(0, using_joint_name_["head_pan"]) = min_angle_[using_joint_name_["head_pan"]] * 0.45;
435  target_position_.coeffRef(0, using_joint_name_["head_tilt"]) = min_angle_[using_joint_name_["head_tilt"]] * 0.8;
436  break;
437  }
438 
439  case BottomRight:
440  {
441  target_position_.coeffRef(0, using_joint_name_["head_pan"]) = max_angle_[using_joint_name_["head_pan"]] * 0.45;
442  target_position_.coeffRef(0, using_joint_name_["head_tilt"]) = min_angle_[using_joint_name_["head_tilt"]] * 0.8;
443  break;
444  }
445 
446  default:
447  return;
448  }
449 
450  // set moving time
451  moving_time_ = 0.5; // default : 0.5 sec
452 
453  for (std::map<std::string, robotis_framework::DynamixelState *>::iterator state_it = result_.begin();
454  state_it != result_.end(); state_it++)
455  {
456  std::string joint_name = state_it->first;
457  int index = using_joint_name_[joint_name];
458 
459  // set time
460  double calc_moving_time = fabs(goal_position_.coeff(0, index) - target_position_.coeff(0, index)) / (60.0 * M_PI / 180.0);
461  if (calc_moving_time > moving_time_)
462  moving_time_ = calc_moving_time;
463  }
464 
465  // generate trajectory
466  tra_gene_thread_ = new boost::thread(boost::bind(&HeadControlModule::jointTraGeneThread, this));
467  delete tra_gene_thread_;
468 }
469 
470 /*
471  simple minimum jerk trajectory
472 
473  pos_start : position at initial state
474  vel_start : velocity at initial state
475  accel_start : acceleration at initial state
476 
477  pos_end : position at final state
478  vel_end : velocity at final state
479  accel_end : acceleration at final state
480 
481  smp_time : sampling time
482 
483  mov_time : movement time
484  */
485 Eigen::MatrixXd HeadControlModule::calcMinimumJerkTraPVA(double pos_start, double vel_start, double accel_start,
486  double pos_end, double vel_end, double accel_end,
487  double smp_time, double mov_time)
488 {
489  Eigen::MatrixXd poly_matrix(3, 3);
490  Eigen::MatrixXd poly_vector(3, 1);
491 
492  poly_matrix << robotis_framework::powDI(mov_time, 3), robotis_framework::powDI(mov_time, 4), robotis_framework::powDI(
493  mov_time, 5), 3 * robotis_framework::powDI(mov_time, 2), 4 * robotis_framework::powDI(mov_time, 3), 5
494  * robotis_framework::powDI(mov_time, 4), 6 * mov_time, 12 * robotis_framework::powDI(mov_time, 2), 20
495  * robotis_framework::powDI(mov_time, 3);
496 
497  poly_vector << pos_end - pos_start - vel_start * mov_time - accel_start * pow(mov_time, 2) / 2, vel_end - vel_start
498  - accel_start * mov_time, accel_end - accel_start;
499 
500  Eigen::MatrixXd poly_coeff = poly_matrix.inverse() * poly_vector;
501 
502  int all_time_steps = round(mov_time / smp_time + 1);
503 
504  Eigen::MatrixXd time = Eigen::MatrixXd::Zero(all_time_steps, 1);
505  Eigen::MatrixXd minimum_jer_tra = Eigen::MatrixXd::Zero(all_time_steps, 3);
506 
507  for (int step = 0; step < all_time_steps; step++)
508  time.coeffRef(step, 0) = step * smp_time;
509 
510  for (int step = 0; step < all_time_steps; step++)
511  {
512  // position
513  minimum_jer_tra.coeffRef(step, 0) = pos_start + vel_start * time.coeff(step, 0)
514  + 0.5 * accel_start * robotis_framework::powDI(time.coeff(step, 0), 2)
515  + poly_coeff.coeff(0, 0) * robotis_framework::powDI(time.coeff(step, 0), 3)
516  + poly_coeff.coeff(1, 0) * robotis_framework::powDI(time.coeff(step, 0), 4)
517  + poly_coeff.coeff(2, 0) * robotis_framework::powDI(time.coeff(step, 0), 5);
518  // velocity
519  minimum_jer_tra.coeffRef(step, 1) = vel_start + accel_start * time.coeff(step, 0)
520  + 3 * poly_coeff.coeff(0, 0) * robotis_framework::powDI(time.coeff(step, 0), 2)
521  + 4 * poly_coeff.coeff(1, 0) * robotis_framework::powDI(time.coeff(step, 0), 3)
522  + 5 * poly_coeff.coeff(2, 0) * robotis_framework::powDI(time.coeff(step, 0), 4);
523  // accel
524  minimum_jer_tra.coeffRef(step, 2) = accel_start + 6 * poly_coeff.coeff(0, 0) * time.coeff(step, 0)
525  + 12 * poly_coeff.coeff(1, 0) * robotis_framework::powDI(time.coeff(step, 0), 2)
526  + 20 * poly_coeff.coeff(2, 0) * robotis_framework::powDI(time.coeff(step, 0), 3);
527  }
528 
529  return minimum_jer_tra;
530 }
531 
533 {
534  tra_lock_.lock();
535 
536  double smp_time = control_cycle_msec_ * 0.001; // ms -> s
537  int all_time_steps = int(moving_time_ / smp_time) + 1;
538 
539  // for debug
540  try
541  {
542  calc_joint_tra_.resize(all_time_steps, result_.size());
543  calc_joint_vel_tra_.resize(all_time_steps, result_.size());
544  calc_joint_accel_tra_.resize(all_time_steps, result_.size());
545  }
546  catch(std::exception &e)
547  {
548  std::cout << "All step tile : " << all_time_steps << std::endl;
549  std::cout << e.what() << std::endl;
550  throw;
551  }
552 
553  for (std::map<std::string, robotis_framework::DynamixelState *>::iterator state_it = result_.begin();
554  state_it != result_.end(); state_it++)
555  {
556  std::string joint_name = state_it->first;
557  int index = using_joint_name_[joint_name];
558 
559  double ini_value = goal_position_.coeff(0, index);
560  double ini_vel = goal_velocity_.coeff(0, index);
561  double ini_accel = goal_acceleration_.coeff(0, index);
562 
563  double tar_value = target_position_.coeff(0, index);
564 
565  Eigen::MatrixXd tra = calcMinimumJerkTraPVA(ini_value, ini_vel, ini_accel, tar_value, 0.0, 0.0, smp_time,
566  moving_time_);
567 
568  calc_joint_tra_.block(0, index, all_time_steps, 1) = tra.block(0, 0, all_time_steps, 1);
569  calc_joint_vel_tra_.block(0, index, all_time_steps, 1) = tra.block(0, 1, all_time_steps, 1);
570  calc_joint_accel_tra_.block(0, index, all_time_steps, 1) = tra.block(0, 2, all_time_steps, 1);
571  }
572 
573  tra_size_ = calc_joint_tra_.rows();
574  tra_count_ = 0;
575 
576  if (DEBUG)
577  ROS_INFO("[ready] make trajectory : %d, %d", tra_size_, tra_count_);
578 
579  tra_lock_.unlock();
580 }
581 
582 void HeadControlModule::publishStatusMsg(unsigned int type, std::string msg)
583 {
584  ros::Time now = ros::Time::now();
585 
586  if(msg.compare(last_msg_) == 0)
587  {
588  ros::Duration dur = now - last_msg_time_;
589  if(dur.sec < 1)
590  return;
591  }
592 
593  robotis_controller_msgs::StatusMsg status_msg;
594  status_msg.header.stamp = now;
595  status_msg.type = type;
596  status_msg.module_name = "Head Control";
597  status_msg.status_msg = msg;
598 
599  status_msg_pub_.publish(status_msg);
600 
601  last_msg_ = msg;
602  last_msg_time_ = now;
603 }
604 }
Eigen::MatrixXd calcMinimumJerkTraPVA(double pos_start, double vel_start, double accel_start, double pos_end, double vel_end, double accel_end, double smp_time, double mov_time)
std::map< std::string, DynamixelState * > result_
void publish(const boost::shared_ptr< M > &message) const
std::map< std::string, int > using_joint_name_
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
void initialize(const int control_cycle_msec, robotis_framework::Robot *robot)
void process(std::map< std::string, robotis_framework::Dynamixel * > dxls, std::map< std::string, double > sensors)
void setHeadJointCallback(const sensor_msgs::JointState::ConstPtr &msg)
bool checkAngleLimit(const int joint_index, double &goal_position)
std::map< int, double > max_angle_
#define ROS_ERROR_THROTTLE(rate,...)
#define ROS_INFO(...)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
void setCallbackQueue(CallbackQueueInterface *queue)
#define DEGREE2RADIAN
std::map< int, double > min_angle_
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
#define ROS_WARN_STREAM(args)
void setHeadJointOffsetCallback(const sensor_msgs::JointState::ConstPtr &msg)
void setHeadJoint(const sensor_msgs::JointState::ConstPtr &msg, bool is_offset)
double powDI(double a, int b)
void generateScanTra(const int head_direction)
DynamixelState * dxl_state_
void setHeadScanCallback(const std_msgs::String::ConstPtr &msg)
static Time now()
#define ROS_INFO_THROTTLE(rate,...)
bool ok() const
#define ROS_ERROR_STREAM(args)
void publishStatusMsg(unsigned int type, std::string msg)


op3_head_control_module
Author(s): Kayman
autogenerated on Mon Jun 10 2019 14:41:10