p2os.cpp
Go to the documentation of this file.
1 /*
2  * P2OS for ROS
3  * Copyright (C) 2009 David Feil-Seifer, Brian Gerkey, Kasper Stoy,
4  * Richard Vaughan, & Andrew Howard
5  * Copyright (C) 2018 Hunter L. Allen
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21  */
22 #include <termios.h>
23 #include <fcntl.h>
24 #include <string.h>
25 
26 #include <ros/ros.h>
27 #include <p2os_driver/p2os.hpp>
28 
29 #include <string>
30 
32 : n(nh),
33  batt_pub_(n.advertise<p2os_msgs::BatteryState>("battery_state", 1000),
34  diagnostic_,
35  diagnostic_updater::FrequencyStatusParam(&desired_freq, &desired_freq, 0.1),
36  diagnostic_updater::TimeStampStatusParam()),
37  ptz_(this)
38 {
44  // Use sonar
45  ros::NodeHandle n_private("~");
46  n_private.param(std::string("odom_frame_id"), odom_frame_id, std::string("odom"));
47  n_private.param(std::string("base_link_frame_id"), base_link_frame_id, std::string("base_link"));
48  n_private.param("use_sonar", use_sonar_, false);
49 
50  // read in config options
51  // bumpstall
52  n_private.param("bumpstall", bumpstall, -1);
53  // pulse
54  n_private.param("pulse", pulse, -1.0);
55  // rot_kp
56  n_private.param("rot_kp", rot_kp, -1);
57  // rot_kv
58  n_private.param("rot_kv", rot_kv, -1);
59  // rot_ki
60  n_private.param("rot_ki", rot_ki, -1);
61  // trans_kp
62  n_private.param("trans_kp", trans_kp, -1);
63  // trans_kv
64  n_private.param("trans_kv", trans_kv, -1);
65  // trans_ki
66  n_private.param("trans_ki", trans_ki, -1);
67  // !!! port !!!
68  std::string def = DEFAULT_P2OS_PORT;
69  n_private.param("port", psos_serial_port, def);
70  ROS_INFO("using serial port: [%s]", psos_serial_port.c_str());
71  n_private.param("use_tcp", psos_use_tcp, false);
72  std::string host = DEFAULT_P2OS_TCP_REMOTE_HOST;
73  n_private.param("tcp_remote_host", psos_tcp_host, host);
74  n_private.param("tcp_remote_port", psos_tcp_port, DEFAULT_P2OS_TCP_REMOTE_PORT);
75  // radio
76  n_private.param("radio", radio_modemp, 0);
77  // joystick
78  n_private.param("joystick", joystick, 0);
79  // direct_wheel_vel_control
80  n_private.param("direct_wheel_vel_control", direct_wheel_vel_control, 0);
81  // max xpeed
82  double spd;
83  n_private.param("max_xspeed", spd, MOTOR_DEF_MAX_SPEED);
84  motor_max_speed = static_cast<int>(rint(1e3 * spd));
85  // max_yawspeed
86  n_private.param("max_yawspeed", spd, MOTOR_DEF_MAX_TURNSPEED);
87  motor_max_turnspeed = static_cast<int16_t>(rint(RTOD(spd)));
88  // max_xaccel
89  n_private.param("max_xaccel", spd, 0.0);
90  motor_max_trans_accel = static_cast<int16_t>(rint(1e3 * spd));
91  // max_xdecel
92  n_private.param("max_xdecel", spd, 0.0);
93  motor_max_trans_decel = static_cast<int16_t>(rint(1e3 * spd));
94  // max_yawaccel
95  n_private.param("max_yawaccel", spd, 0.0);
96  motor_max_rot_accel = static_cast<int16_t>(rint(RTOD(spd)));
97  // max_yawdecel
98  n_private.param("max_yawdecel", spd, 0.0);
99  motor_max_rot_decel = static_cast<int16_t>(rint(RTOD(spd)));
100 
101  desired_freq = 10;
102 
103  // advertise services
104  pose_pub_ = n.advertise<nav_msgs::Odometry>("pose", 1000);
105  // advertise topics
106  mstate_pub_ = n.advertise<p2os_msgs::MotorState>("motor_state", 1000);
107  grip_state_pub_ = n.advertise<p2os_msgs::GripperState>("gripper_state", 1000);
108  ptz_state_pub_ = n.advertise<p2os_msgs::PTZState>("ptz_state", 1000);
109  sonar_pub_ = n.advertise<p2os_msgs::SonarArray>("sonar", 1000);
110  aio_pub_ = n.advertise<p2os_msgs::AIO>("aio", 1000);
111  dio_pub_ = n.advertise<p2os_msgs::DIO>("dio", 1000);
112 
113  // subscribe to services
114  cmdvel_sub_ = n.subscribe("cmd_vel", 1, &P2OSNode::cmdvel_cb, this);
115  cmdmstate_sub_ = n.subscribe("cmd_motor_state", 1, &P2OSNode::cmdmotor_state,
116  this);
117  gripper_sub_ = n.subscribe("gripper_control", 1, &P2OSNode::gripperCallback,
118  this);
119  ptz_cmd_sub_ = n.subscribe("ptz_control", 1, &P2OSPtz::callback, &ptz_);
120 
122 
123  // add diagnostic functions
124  diagnostic_.add("Motor Stall", this, &P2OSNode::check_stall);
125  diagnostic_.add("Battery Voltage", this, &P2OSNode::check_voltage);
126 
127  // initialize robot parameters (player legacy)
129 }
130 
132 
133 void P2OSNode::cmdmotor_state(const p2os_msgs::MotorStateConstPtr & msg)
134 {
135  motor_dirty = true;
136  cmdmotor_state_ = *msg;
137 }
138 
140 {
141  if (!motor_dirty) {
142  return;
143  }
144  motor_dirty = false;
145 
146  unsigned char val = static_cast<unsigned char>(cmdmotor_state_.state);
147  unsigned char command[4];
148  P2OSPacket packet;
149  command[0] = ENABLE;
150  command[1] = ARGINT;
151  command[2] = val;
152  command[3] = 0;
153  packet.Build(command, 4);
154 
155  // Store the current motor state so that we can set it back
156  p2os_data.motors.state = cmdmotor_state_.state;
157  SendReceive(&packet, false);
158 }
159 
161 {
162  if (!gripper_dirty_) {
163  return;
164  }
165  gripper_dirty_ = false;
166 
167  // Send the gripper command
168  unsigned char grip_val = (unsigned char) gripper_state_.grip.state;
169  unsigned char grip_command[4];
170 
171  P2OSPacket grip_packet;
172  grip_command[0] = GRIPPER;
173  grip_command[1] = ARGINT;
174  grip_command[2] = grip_val;
175  grip_command[3] = 0;
176  grip_packet.Build(grip_command, 4);
177  SendReceive(&grip_packet, false);
178 
179  // Send the lift command
180  unsigned char lift_val = (unsigned char) gripper_state_.lift.state;
181  unsigned char lift_command[4];
182 
183  P2OSPacket lift_packet;
184  lift_command[0] = GRIPPER;
185  lift_command[1] = ARGINT;
186  lift_command[2] = lift_val;
187  lift_command[3] = 0;
188  lift_packet.Build(lift_command, 4);
189 
190  SendReceive(&lift_packet, false);
191 }
192 
193 void P2OSNode::cmdvel_cb(const geometry_msgs::TwistConstPtr & msg)
194 {
195  if (fabs(msg->linear.x - cmdvel_.linear.x) > 0.01 ||
196  fabs(msg->angular.z - cmdvel_.angular.z) > 0.01)
197  {
199  ROS_DEBUG("new speed: [%0.2f,%0.2f](%0.3f)", msg->linear.x * 1e3, msg->angular.z,
200  veltime.toSec());
201  vel_dirty = true;
202  cmdvel_ = *msg;
203  } else {
204  ros::Duration veldur = ros::Time::now() - veltime;
205  if (veldur.toSec() > 2.0 &&
206  ((fabs(cmdvel_.linear.x) > 0.01) || (fabs(cmdvel_.angular.z) > 0.01)))
207  {
208  ROS_DEBUG("maintaining old speed: %0.3f|%0.3f", veltime.toSec(), ros::Time::now().toSec());
209  vel_dirty = true;
211  }
212  }
213 }
214 
216 {
217  if (!vel_dirty) {return;}
218 
219  ROS_DEBUG("setting vel: [%0.2f,%0.2f]", cmdvel_.linear.x, cmdvel_.angular.z);
220  vel_dirty = false;
221 
222  uint16_t absSpeedDemand, absturnRateDemand;
223  unsigned char motorcommand[4];
224  P2OSPacket motorpacket;
225 
226  int vx = static_cast<int>(cmdvel_.linear.x * 1e3);
227  int va = static_cast<int>(rint(RTOD(cmdvel_.angular.z)));
228 
229  // non-direct wheel control
230  motorcommand[0] = VEL;
231  motorcommand[1] = (vx >= 0) ? ARGINT : ARGNINT;
232 
233  absSpeedDemand = static_cast<uint16_t>(abs(vx));
234 
235  if (absSpeedDemand <= this->motor_max_speed) {
236  motorcommand[2] = absSpeedDemand & 0x00FF;
237  motorcommand[3] = (absSpeedDemand & 0xFF00) >> 8;
238  } else {
239  ROS_WARN("speed demand thresholded! (true: %u, max: %u)", absSpeedDemand, motor_max_speed);
240  motorcommand[2] = motor_max_speed & 0x00FF;
241  motorcommand[3] = (motor_max_speed & 0xFF00) >> 8;
242  }
243 
244  motorpacket.Build(motorcommand, 4);
245  SendReceive(&motorpacket);
246 
247  motorcommand[0] = RVEL;
248  motorcommand[1] = (va >= 0) ? ARGINT : ARGNINT;
249 
250  absturnRateDemand = static_cast<uint16_t>((va >= 0) ? va : (-1 * va));
251 
252  if (absturnRateDemand <= motor_max_turnspeed) {
253  motorcommand[2] = absturnRateDemand & 0x00FF;
254  motorcommand[3] = (absturnRateDemand & 0xFF00) >> 8;
255  } else {
256  ROS_WARN("Turn rate demand threshholded!");
257  motorcommand[2] = this->motor_max_turnspeed & 0x00FF;
258  motorcommand[3] = (this->motor_max_turnspeed & 0xFF00) >> 8;
259  }
260 
261  motorpacket.Build(motorcommand, 4);
262  SendReceive(&motorpacket);
263 }
264 
265 void P2OSNode::gripperCallback(const p2os_msgs::GripperStateConstPtr & msg)
266 {
267  gripper_dirty_ = true;
268  gripper_state_ = *msg;
269 }
270 
272 {
273  int i;
274  int bauds[] = {B9600, B38400, B19200, B115200, B57600};
275  int numbauds = sizeof(bauds);
276  int currbaud = 0;
277  sippacket = NULL;
278  lastPulseTime = 0.0;
279 
280  struct termios term;
281  unsigned char command;
282  P2OSPacket packet, receivedpacket;
283  int flags = 0;
284  bool sent_close = false;
285 
286  enum
287  {
288  NO_SYNC,
289  AFTER_FIRST_SYNC,
290  AFTER_SECOND_SYNC,
291  READY
292  } psos_state;
293 
294  psos_state = NO_SYNC;
295 
296  char name[20], type[20], subtype[20];
297  int cnt;
298 
299 
300  // use serial port
301 
302  ROS_INFO("P2OS connection opening serial port %s...", psos_serial_port.c_str());
303 
304  if ((this->psos_fd = open(this->psos_serial_port.c_str(),
305  O_RDWR | O_SYNC | O_NONBLOCK, S_IRUSR | S_IWUSR)) < 0)
306  {
307  ROS_ERROR("P2OS::Setup():open():");
308  return 1;
309  }
310 
311  if (tcgetattr(this->psos_fd, &term) < 0) {
312  ROS_ERROR("P2OS::Setup():tcgetattr():");
313  close(this->psos_fd);
314  this->psos_fd = -1;
315  return 1;
316  }
317 
318  cfmakeraw(&term);
319  cfsetispeed(&term, bauds[currbaud]);
320  cfsetospeed(&term, bauds[currbaud]);
321 
322  if (tcsetattr(this->psos_fd, TCSAFLUSH, &term) < 0) {
323  ROS_ERROR("P2OS::Setup():tcsetattr():");
324  close(this->psos_fd);
325  this->psos_fd = -1;
326  return 1;
327  }
328 
329  if (tcflush(this->psos_fd, TCIOFLUSH) < 0) {
330  ROS_ERROR("P2OS::Setup():tcflush():");
331  close(this->psos_fd);
332  this->psos_fd = -1;
333  return 1;
334  }
335 
336  if ((flags = fcntl(this->psos_fd, F_GETFL)) < 0) {
337  ROS_ERROR("P2OS::Setup():fcntl()");
338  close(this->psos_fd);
339  this->psos_fd = -1;
340  return 1;
341  }
342  // Sync:
343 
344  int num_sync_attempts = 3;
345  while (psos_state != READY) {
346  switch (psos_state) {
347  case NO_SYNC:
348  command = SYNC0;
349  packet.Build(&command, 1);
350  packet.Send(this->psos_fd);
351  usleep(P2OS_CYCLETIME_USEC);
352  break;
353  case AFTER_FIRST_SYNC:
354  ROS_INFO("turning off NONBLOCK mode...");
355  if (fcntl(this->psos_fd, F_SETFL, flags ^ O_NONBLOCK) < 0) {
356  ROS_ERROR("P2OS::Setup():fcntl()");
357  close(this->psos_fd);
358  this->psos_fd = -1;
359  return 1;
360  }
361  command = SYNC1;
362  packet.Build(&command, 1);
363  packet.Send(this->psos_fd);
364  break;
365  case AFTER_SECOND_SYNC:
366  command = SYNC2;
367  packet.Build(&command, 1);
368  packet.Send(this->psos_fd);
369  break;
370  default:
371  ROS_WARN("P2OS::Setup():shouldn't be here...");
372  break;
373  }
374  usleep(P2OS_CYCLETIME_USEC);
375 
376  if (receivedpacket.Receive(this->psos_fd)) {
377  if ((psos_state == NO_SYNC) && (num_sync_attempts >= 0)) {
378  num_sync_attempts--;
379  usleep(P2OS_CYCLETIME_USEC);
380  continue;
381  } else {
382  // couldn't connect; try different speed.
383  if (++currbaud < numbauds) {
384  cfsetispeed(&term, bauds[currbaud]);
385  cfsetospeed(&term, bauds[currbaud]);
386  if (tcsetattr(this->psos_fd, TCSAFLUSH, &term) < 0) {
387  ROS_ERROR("P2OS::Setup():tcsetattr():");
388  close(this->psos_fd);
389  this->psos_fd = -1;
390  return 1;
391  }
392 
393  if (tcflush(this->psos_fd, TCIOFLUSH) < 0) {
394  ROS_ERROR("P2OS::Setup():tcflush():");
395  close(this->psos_fd);
396  this->psos_fd = -1;
397  return 1;
398  }
399  num_sync_attempts = 3;
400  continue;
401  } else {
402  // tried all speeds; bail
403  break;
404  }
405  }
406  }
407  switch (receivedpacket.packet[3]) {
408  case SYNC0:
409  ROS_INFO("SYNC0");
410  psos_state = AFTER_FIRST_SYNC;
411  break;
412  case SYNC1:
413  ROS_INFO("SYNC1");
414  psos_state = AFTER_SECOND_SYNC;
415  break;
416  case SYNC2:
417  ROS_INFO("SYNC2");
418  psos_state = READY;
419  break;
420  default:
421  // maybe P2OS is still running from last time. let's try to CLOSE
422  // and reconnect
423  if (!sent_close) {
424  ROS_DEBUG("sending CLOSE");
425  command = CLOSE;
426  packet.Build(&command, 1);
427  packet.Send(this->psos_fd);
428  sent_close = true;
429  usleep(2 * P2OS_CYCLETIME_USEC);
430  tcflush(this->psos_fd, TCIFLUSH);
431  psos_state = NO_SYNC;
432  }
433  break;
434  }
435  usleep(P2OS_CYCLETIME_USEC);
436  }
437  if (psos_state != READY) {
438  if (this->psos_use_tcp) {
439  ROS_INFO("Couldn't synchronize with P2OS.\n"
440  " Most likely because the robot is not connected %s %s",
441  this->psos_use_tcp ? "to the ethernet-serial bridge device " : "to the serial port",
442  this->psos_use_tcp ? this->psos_tcp_host.c_str() : this->psos_serial_port.c_str());
443  }
444  close(this->psos_fd);
445  this->psos_fd = -1;
446  return 1;
447  }
448  cnt = 4;
449  cnt += snprintf(name, sizeof(name), "%s", &receivedpacket.packet[cnt]);
450  cnt++;
451  cnt += snprintf(type, sizeof(type), "%s", &receivedpacket.packet[cnt]);
452  cnt++;
453  cnt += snprintf(subtype, sizeof(subtype), "%s", &receivedpacket.packet[cnt]);
454  cnt++;
455 
456  std::string hwID = std::string(name) + ": " + std::string(type) + "/" + std::string(subtype);
458 
459  command = OPEN;
460  packet.Build(&command, 1);
461  packet.Send(this->psos_fd);
462  usleep(P2OS_CYCLETIME_USEC);
463  command = PULSE;
464  packet.Build(&command, 1);
465  packet.Send(this->psos_fd);
466  usleep(P2OS_CYCLETIME_USEC);
467 
468  ROS_INFO("Done.\n Connected to %s, a %s %s", name, type, subtype);
469 
470  // now, based on robot type, find the right set of parameters
471  for (i = 0; i < PLAYER_NUM_ROBOT_TYPES; i++) {
472  if (!strcasecmp(PlayerRobotParams[i].Class.c_str(), type) &&
473  !strcasecmp(PlayerRobotParams[i].Subclass.c_str(), subtype))
474  {
475  param_idx = i;
476  break;
477  }
478  }
479  if (i == PLAYER_NUM_ROBOT_TYPES) {
480  ROS_WARN("P2OS: Warning: couldn't find parameters for this robot; "
481  "using defaults");
482  param_idx = 0;
483  }
484 
485  // first, receive a packet so we know we're connected.
486  if (!sippacket) {
487  sippacket = new SIP(param_idx);
490  }
491  /*
492  sippacket->x_offset = 0;
493  sippacket->y_offset = 0;
494  sippacket->angle_offset = 0;
495 
496  SendReceive((P2OSPacket*)NULL,false);
497  */
498  // turn off the sonars at first
499  this->ToggleSonarPower(0);
500  // if requested, set max accel/decel limits
501  P2OSPacket accel_packet;
502  unsigned char accel_command[4];
503  if (this->motor_max_trans_accel > 0) {
504  accel_command[0] = SETA;
505  accel_command[1] = ARGINT;
506  accel_command[2] = this->motor_max_trans_accel & 0x00FF;
507  accel_command[3] = (this->motor_max_trans_accel & 0xFF00) >> 8;
508  accel_packet.Build(accel_command, 4);
509  this->SendReceive(&accel_packet, false);
510  }
511 
512  if (this->motor_max_trans_decel < 0) {
513  accel_command[0] = SETA;
514  accel_command[1] = ARGNINT;
515  accel_command[2] = -1 * (this->motor_max_trans_decel) & 0x00FF;
516  accel_command[3] = (-1 * (this->motor_max_trans_decel) & 0xFF00) >> 8;
517  accel_packet.Build(accel_command, 4);
518  this->SendReceive(&accel_packet, false);
519  }
520  if (this->motor_max_rot_accel > 0) {
521  accel_command[0] = SETRA;
522  accel_command[1] = ARGINT;
523  accel_command[2] = this->motor_max_rot_accel & 0x00FF;
524  accel_command[3] = (this->motor_max_rot_accel & 0xFF00) >> 8;
525  accel_packet.Build(accel_command, 4);
526  this->SendReceive(&accel_packet, false);
527  }
528  if (this->motor_max_rot_decel < 0) {
529  accel_command[0] = SETRA;
530  accel_command[1] = ARGNINT;
531  accel_command[2] = -1 * (this->motor_max_rot_decel) & 0x00FF;
532  accel_command[3] = (-1 * (this->motor_max_rot_decel) & 0xFF00) >> 8;
533  accel_packet.Build(accel_command, 4);
534  this->SendReceive(&accel_packet, false);
535  }
536 
537 
538  // if requested, change PID settings
539  P2OSPacket pid_packet;
540  unsigned char pid_command[4];
541  if (this->rot_kp >= 0) {
542  pid_command[0] = ROTKP;
543  pid_command[1] = ARGINT;
544  pid_command[2] = this->rot_kp & 0x00FF;
545  pid_command[3] = (this->rot_kp & 0xFF00) >> 8;
546  pid_packet.Build(pid_command, 4);
547  this->SendReceive(&pid_packet);
548  }
549  if (this->rot_kv >= 0) {
550  pid_command[0] = ROTKV;
551  pid_command[1] = ARGINT;
552  pid_command[2] = this->rot_kv & 0x00FF;
553  pid_command[3] = (this->rot_kv & 0xFF00) >> 8;
554  pid_packet.Build(pid_command, 4);
555  this->SendReceive(&pid_packet);
556  }
557  if (this->rot_ki >= 0) {
558  pid_command[0] = ROTKI;
559  pid_command[1] = ARGINT;
560  pid_command[2] = this->rot_ki & 0x00FF;
561  pid_command[3] = (this->rot_ki & 0xFF00) >> 8;
562  pid_packet.Build(pid_command, 4);
563  this->SendReceive(&pid_packet);
564  }
565  if (this->trans_kp >= 0) {
566  pid_command[0] = TRANSKP;
567  pid_command[1] = ARGINT;
568  pid_command[2] = this->trans_kp & 0x00FF;
569  pid_command[3] = (this->trans_kp & 0xFF00) >> 8;
570  pid_packet.Build(pid_command, 4);
571  this->SendReceive(&pid_packet);
572  }
573  if (this->trans_kv >= 0) {
574  pid_command[0] = TRANSKV;
575  pid_command[1] = ARGINT;
576  pid_command[2] = this->trans_kv & 0x00FF;
577  pid_command[3] = (this->trans_kv & 0xFF00) >> 8;
578  pid_packet.Build(pid_command, 4);
579  this->SendReceive(&pid_packet);
580  }
581  if (this->trans_ki >= 0) {
582  pid_command[0] = TRANSKI;
583  pid_command[1] = ARGINT;
584  pid_command[2] = this->trans_ki & 0x00FF;
585  pid_command[3] = (this->trans_ki & 0xFF00) >> 8;
586  pid_packet.Build(pid_command, 4);
587  this->SendReceive(&pid_packet);
588  }
589 
590 
591  // if requested, change bumper-stall behavior
592  // 0 = don't stall
593  // 1 = stall on front bumper contact
594  // 2 = stall on rear bumper contact
595  // 3 = stall on either bumper contact
596  if (this->bumpstall >= 0) {
597  if (this->bumpstall > 3) {
598  ROS_INFO("ignoring bumpstall value %d; should be 0, 1, 2, or 3",
599  this->bumpstall);
600  } else {
601  ROS_INFO("setting bumpstall to %d", this->bumpstall);
602  P2OSPacket bumpstall_packet;
603  unsigned char bumpstall_command[4];
604  bumpstall_command[0] = BUMP_STALL;
605  bumpstall_command[1] = ARGINT;
606  bumpstall_command[2] = (unsigned char)this->bumpstall;
607  bumpstall_command[3] = 0;
608  bumpstall_packet.Build(bumpstall_command, 4);
609  this->SendReceive(&bumpstall_packet, false);
610  }
611  }
612 
613  // Turn on the sonar
614  if (use_sonar_) {
615  this->ToggleSonarPower(1);
616  ROS_DEBUG("Sonar array powered on.");
617  }
618  ptz_.setup();
619 
620  return 0;
621 }
622 
624 {
625  unsigned char command[20], buffer[20];
626  P2OSPacket packet;
627 
628  if (ptz_.isOn()) {
629  ptz_.shutdown();
630  }
631 
632  memset(buffer, 0, 20);
633 
634  if (this->psos_fd == -1) {
635  return -1;
636  }
637 
638  command[0] = STOP;
639  packet.Build(command, 1);
640  packet.Send(this->psos_fd);
641  usleep(P2OS_CYCLETIME_USEC);
642 
643  command[0] = CLOSE;
644  packet.Build(command, 1);
645  packet.Send(this->psos_fd);
646  usleep(P2OS_CYCLETIME_USEC);
647 
648  close(this->psos_fd);
649  this->psos_fd = -1;
650  ROS_INFO("P2OS has been shutdown");
651  delete this->sippacket;
652  this->sippacket = NULL;
653 
654  return 0;
655 }
656 
657 
658 void
660 {
661  p2os_data.position.header.stamp = ts;
663  p2os_data.odom_trans.header.stamp = ts;
665 
666  p2os_data.batt.header.stamp = ts;
669 
670  // put sonar data
671  p2os_data.sonar.header.stamp = ts;
673 
674  // put aio data
676  // put dio data
678 
679  // put gripper and lift data
682 
683  // put bumper data
684  // put compass data
685 }
686 
687 /* send the packet, then receive and parse an SIP */
688 int P2OSNode::SendReceive(P2OSPacket * pkt, bool publish_data)
689 {
690  P2OSPacket packet;
691 
692  if ((this->psos_fd >= 0) && this->sippacket) {
693  if (pkt) {
694  pkt->Send(this->psos_fd);
695  }
696 
697  /* receive a packet */
698  pthread_testcancel();
699  if (packet.Receive(this->psos_fd)) {
700  ROS_ERROR("RunPsosThread(): Receive errored");
701  pthread_exit(NULL);
702  }
703 
704  const bool packet_check =
705  packet.packet[0] == 0xFA && packet.packet[1] == 0xFB &&
706  (packet.packet[3] == 0x30 || packet.packet[3] == 0x31 ||
707  packet.packet[3] == 0x32 || packet.packet[3] == 0x33 ||
708  packet.packet[3] == 0x34);
709  const bool ser_aux =
710  (packet.packet[0] == 0xFA && packet.packet[1] == 0xFB && packet.packet[3] == SERAUX);
711  if (packet_check) {
712  /* It is a server packet, so process it */
713  this->sippacket->ParseStandard(&packet.packet[3]);
714  this->sippacket->FillStandard(&(this->p2os_data));
715 
716  if (publish_data) {
717  this->StandardSIPPutData(packet.timestamp);
718  }
719  } else if (ser_aux) {
720  // This is an AUX serial packet
721  if (ptz_.isOn()) {
722  int len = packet.packet[2] - 3;
723  if (ptz_.cb_.gotPacket()) {
724  ROS_ERROR("PTZ got a message, but alread has the complete packet.");
725  } else {
726  for (int i = 4; i < 4 + len; ++i) {
727  ptz_.cb_.putOnBuf(packet.packet[i]);
728  }
729  }
730  }
731  } else {
732  ROS_ERROR("Received other packet!");
733  packet.PrintHex();
734  }
735  }
736 
737  return 0;
738 }
739 
741 {
743 }
744 
746 {
747  double voltage = sippacket->battery / 10.0;
748  if (voltage < 11.0) {
749  stat.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "battery voltage critically low");
750  } else if (voltage < 11.75) {
751  stat.summary(diagnostic_msgs::DiagnosticStatus::WARN, "battery voltage getting low");
752  } else {stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "battery voltage OK");}
753 
754  stat.add("voltage", voltage);
755 }
756 
758 {
759  if (sippacket->lwstall || sippacket->rwstall) {
760  stat.summary(diagnostic_msgs::DiagnosticStatus::ERROR,
761  "wheel stalled");
762  } else {stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "no wheel stall");}
763 
764  stat.add("left wheel stall", sippacket->lwstall);
765  stat.add("right wheel stall", sippacket->rwstall);
766 }
767 
769 {
770  P2OSPacket pkt;
771  unsigned char p2oscommand[4];
772 
773  if (this->sippacket) {
774  this->sippacket->rawxpos = 0;
775  this->sippacket->rawypos = 0;
776  this->sippacket->xpos = 0;
777  this->sippacket->ypos = 0;
778  p2oscommand[0] = SETO;
779  p2oscommand[1] = ARGINT;
780  pkt.Build(p2oscommand, 2);
781  this->SendReceive(&pkt, false);
782  ROS_INFO("resetting raw positions");
783  }
784 }
785 
786 /* toggle sonars on/off, according to val */
787 void P2OSNode::ToggleSonarPower(unsigned char val)
788 {
789  unsigned char command[4];
790  P2OSPacket packet;
791 
792  command[0] = SONAR;
793  command[1] = ARGINT;
794  command[2] = val;
795  command[3] = 0;
796  packet.Build(command, 4);
797  SendReceive(&packet, false);
798 }
799 
805 void P2OSNode::ToggleMotorPower(unsigned char val)
806 {
807  unsigned char command[4];
808  P2OSPacket packet;
809  ROS_INFO("motor state: %d\n", p2os_data.motors.state);
810  p2os_data.motors.state = static_cast<int>(val);
811  command[0] = ENABLE;
812  command[1] = ARGINT;
813  command[2] = val;
814  command[3] = 0;
815  packet.Build(command, 4);
816  SendReceive(&packet, false);
817 }
818 
820 // Actarray stuff
822 
823 // Ticks to degrees from the ARIA software
825 inline double P2OSNode::TicksToDegrees(int joint, unsigned char ticks)
826 {
827  if ((joint < 0) || (joint >= sippacket->armNumJoints)) {
828  return 0;
829  }
830 
831  double result;
832  int pos = ticks - sippacket->armJoints[joint].centre;
833  result = 90.0 / static_cast<double>(sippacket->armJoints[joint].ticksPer90);
834  result = result * pos;
835  if ((joint >= 0) && (joint <= 2)) {
836  result = -result;
837  }
838 
839  return result;
840 }
841 
842 // Degrees to ticks from the ARIA software
844 inline unsigned char P2OSNode::DegreesToTicks(int joint, double degrees)
845 {
846  double val;
847 
848  if ((joint < 0) || (joint >= sippacket->armNumJoints)) {
849  return 0;
850  }
851 
852  val = static_cast<double>(sippacket->armJoints[joint].ticksPer90) * degrees / 90.0;
853  val = round(val);
854  if ((joint >= 0) && (joint <= 2)) {
855  val = -val;
856  }
857  val += sippacket->armJoints[joint].centre;
858 
859  if (val < sippacket->armJoints[joint].min) {
860  return sippacket->armJoints[joint].min;
861  } else if (val > sippacket->armJoints[joint].max) {return sippacket->armJoints[joint].max;} else {
862  return static_cast<int>(round(val));
863  }
864 }
865 
867 inline double P2OSNode::TicksToRadians(int joint, unsigned char ticks)
868 {
869  double result = DTOR(TicksToDegrees(joint, ticks));
870  return result;
871 }
872 
874 inline unsigned char P2OSNode::RadiansToTicks(int joint, double rads)
875 {
876  unsigned char result = static_cast<unsigned char>(DegreesToTicks(joint, RTOD(rads)));
877  return result;
878 }
879 
881 inline double P2OSNode::RadsPerSectoSecsPerTick(int joint, double speed)
882 {
883  double degs = RTOD(speed);
884  double ticksPerDeg = static_cast<double>(sippacket->armJoints[joint].ticksPer90) / 90.0;
885  double ticksPerSec = degs * ticksPerDeg;
886  double secsPerTick = 1000.0f / ticksPerSec;
887 
888  if (secsPerTick > 127) {
889  return 127;
890  } else if (secsPerTick < 1) {
891  return 1;
892  }
893  return secsPerTick;
894 }
895 
897 inline double P2OSNode::SecsPerTicktoRadsPerSec(int joint, double msecs)
898 {
899  double ticksPerSec = 1.0 / (static_cast<double>(msecs) / 1000.0);
900  double ticksPerDeg = static_cast<double>(sippacket->armJoints[joint].ticksPer90) / 90.0;
901  double degs = ticksPerSec / ticksPerDeg;
902  double rads = DTOR(degs);
903 
904  return rads;
905 }
906 
908 {
909  unsigned char command;
910  P2OSPacket packet;
911 
912  command = PULSE;
913  packet.Build(&command, 1);
914  SendReceive(&packet);
915 }
std::string psos_tcp_host
Definition: p2os.hpp:152
double TicksToDegrees(int joint, unsigned char ticks)
Convert ticks to degrees.
Definition: p2os.cpp:825
diagnostic_updater::Updater diagnostic_
Definition: p2os.hpp:139
int rot_kv
Definition: p2os.hpp:162
void PrintHex()
Definition: packet.cpp:42
virtual ~P2OSNode()
Definition: p2os.cpp:131
ros::Publisher aio_pub_
Definition: p2os.hpp:142
void initialize_robot_params(void)
int motor_max_speed
Maximum motor speed in Meters per second.
Definition: p2os.hpp:173
bool rwstall
Definition: sip.hpp:63
int rot_kp
Definition: p2os.hpp:162
unsigned char packet[packet_len]
Definition: packet.hpp:36
p2os_msgs::PTZState getCurrentState()
Definition: p2os_ptz.hpp:168
bool motor_dirty
Definition: p2os.hpp:158
int trans_kv
Definition: p2os.hpp:162
void publish(const boost::shared_ptr< M > &message) const
void ToggleMotorPower(unsigned char val)
Toggle motors on/off.
Definition: p2os.cpp:805
ros::Publisher mstate_pub_
Definition: p2os.hpp:142
int SendReceive(P2OSPacket *pkt, bool publish_data=true)
Definition: p2os.cpp:688
unsigned char centre
Definition: sip.hpp:35
p2os_msgs::MotorState cmdmotor_state_
Motor state publisher.
Definition: p2os.hpp:198
void summary(unsigned char lvl, const std::string s)
p2os_msgs::SonarArray sonar
Container for sonar data.
Definition: p2os.hpp:64
double RadsPerSectoSecsPerTick(int joint, double speed)
Convert radians per second to radians per encoder tick.
Definition: p2os.cpp:881
int xpos
Definition: sip.hpp:70
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
int psos_tcp_port
Definition: p2os.hpp:157
void shutdown()
Definition: p2os_ptz.cpp:138
ros::Publisher dio_pub_
Definition: p2os.hpp:142
std::string odom_frame_id
Definition: sip.hpp:72
int rot_ki
Definition: p2os.hpp:162
#define DEFAULT_P2OS_TCP_REMOTE_PORT
bool use_sonar_
Use the sonar array?
Definition: p2os.hpp:190
void setHardwareID(const std::string &hwid)
SIP * sippacket
Definition: p2os.hpp:150
unsigned char RadiansToTicks(int joint, double rads)
Convert radians to ticks.
Definition: p2os.cpp:874
void updateDiagnostics()
Definition: p2os.cpp:740
int16_t motor_max_rot_decel
Minimum rotational acceleration in Meters per second per second.
Definition: p2os.hpp:183
uint16_t rawypos
Definition: sip.hpp:67
#define SYNC0
void add(const std::string &name, TaskFunction f)
p2os_msgs::AIO aio
Analog In/Out.
Definition: p2os.hpp:68
virtual void publish(const boost::shared_ptr< T > &message)
ros::Time timestamp
Definition: packet.hpp:38
#define DTOR(a)
bool isOn() const
Definition: p2os_ptz.hpp:167
int16_t motor_max_trans_decel
Minimum translational acceleration in Meters per second per second.
Definition: p2os.hpp:179
ros::NodeHandle n
Node Handler used for publication of data.
Definition: p2os.hpp:135
void check_voltage(diagnostic_updater::DiagnosticStatusWrapper &stat)
Definition: p2os.cpp:745
ArmJoint * armJoints
Definition: sip.hpp:93
std::string psos_serial_port
Definition: p2os.hpp:151
int joystick
Use Joystick?
Definition: p2os.hpp:167
void gripperCallback(const p2os_msgs::GripperStateConstPtr &msg)
Definition: p2os.cpp:265
std::string base_link_frame_id
Definition: p2os.hpp:154
#define SYNC2
#define DEFAULT_P2OS_PORT
#define ROS_WARN(...)
p2os_msgs::GripperState gripper
Provides the state of the gripper.
Definition: p2os.hpp:62
geometry_msgs::TransformStamped odom_trans
Transformed odometry frame.
Definition: p2os.hpp:70
bool gotPacket()
Definition: p2os_ptz.cpp:966
ros::Publisher sonar_pub_
Definition: p2os.hpp:142
void check_and_set_gripper_state()
Definition: p2os.cpp:160
tf::TransformBroadcaster odom_broadcaster
Definition: p2os.hpp:147
int16_t motor_max_rot_accel
Maximum rotational acceleration in radians per second per second.
Definition: p2os.hpp:181
void ParseStandard(unsigned char *buffer)
Definition: sip.cpp:325
#define PLAYER_NUM_ROBOT_TYPES
void SendPulse(void)
Definition: p2os.cpp:907
void putOnBuf(unsigned char c)
Definition: p2os_ptz.cpp:924
circbuf cb_
Definition: p2os_ptz.hpp:176
diagnostic_updater::DiagnosedPublisher< p2os_msgs::BatteryState > batt_pub_
Definition: p2os.hpp:141
int16_t motor_max_trans_accel
Maximum translational acceleration in Meters per second per second.
Definition: p2os.hpp:177
#define MOTOR_DEF_MAX_TURNSPEED
#define RTOD(a)
int ypos
Definition: sip.hpp:70
ROSLIB_DECL std::string command(const std::string &cmd)
int Build(unsigned char *data, unsigned char datasize)
Definition: packet.cpp:128
int bumpstall
Stall I hit a wall?
Definition: p2os.hpp:165
#define ROS_INFO(...)
#define ARGINT
bool param(const std::string &param_name, T &param_val, const T &default_val) const
double pulse
Pulse time.
Definition: p2os.hpp:185
p2os_msgs::GripperState gripper_state_
Gripper state publisher.
Definition: p2os.hpp:200
int trans_ki
Definition: p2os.hpp:162
void callback(const p2os_msgs::PTZStateConstPtr &msg)
Definition: p2os_ptz.cpp:149
void sendTransform(const StampedTransform &transform)
uint16_t rawxpos
Definition: sip.hpp:66
ros::Publisher grip_state_pub_
Definition: p2os.hpp:142
RobotParams_t PlayerRobotParams[]
double desired_freq
Definition: p2os.hpp:186
int Setup()
Setup the robot for use. Communicates with the robot directly.
Definition: p2os.cpp:271
double min(double a, double b)
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
bool vel_dirty
Definition: p2os.hpp:158
bool psos_use_tcp
Definition: p2os.hpp:156
unsigned char max
Definition: sip.hpp:36
ros::Time veltime
Definition: p2os.hpp:148
P2OSNode(ros::NodeHandle n)
P2OS robot driver node.
Definition: p2os.cpp:31
ros::Subscriber ptz_cmd_sub_
Definition: p2os.hpp:145
P2OSPtz ptz_
Definition: p2os.hpp:192
#define P2OS_CYCLETIME_USEC
void cmdmotor_state(const p2os_msgs::MotorStateConstPtr &)
Definition: p2os.cpp:133
int trans_kp
Definition: p2os.hpp:162
ros::Subscriber cmdvel_sub_
Definition: p2os.hpp:145
void ResetRawPositions()
Definition: p2os.cpp:768
unsigned char ticksPer90
Definition: sip.hpp:37
p2os_msgs::BatteryState batt
Provides the battery voltage.
Definition: p2os.hpp:58
int motor_max_turnspeed
Maximum turn speed in radians per second.
Definition: p2os.hpp:175
int Shutdown()
Prepare for shutdown.
Definition: p2os.cpp:623
double lastPulseTime
Last time the node received or sent a pulse.
Definition: p2os.hpp:188
void FillStandard(ros_p2os_data_t *data)
Definition: sip.cpp:36
Definition: sip.hpp:55
INLINE Rall1d< T, V, S > abs(const Rall1d< T, V, S > &x)
unsigned char min
Definition: sip.hpp:34
int Receive(int fd)
Definition: packet.cpp:79
int psos_fd
Definition: p2os.hpp:155
unsigned char battery
Definition: sip.hpp:65
ros::Subscriber gripper_sub_
Definition: p2os.hpp:145
#define SERAUX
bool lwstall
Definition: sip.hpp:63
bool gripper_dirty_
Definition: p2os.hpp:159
unsigned char armNumJoints
Definition: sip.hpp:92
ros::Subscriber cmdmstate_sub_
Definition: p2os.hpp:145
void StandardSIPPutData(ros::Time ts)
Definition: p2os.cpp:659
void ToggleSonarPower(unsigned char val)
Definition: p2os.cpp:787
#define DEFAULT_P2OS_TCP_REMOTE_HOST
#define MOTOR_DEF_MAX_SPEED
static Time now()
int setup()
Definition: p2os_ptz.cpp:55
int param_idx
Definition: p2os.hpp:160
std::string odom_frame_id
Definition: p2os.hpp:153
void add(const std::string &key, const T &val)
double TicksToRadians(int joint, unsigned char ticks)
Convert ticks to radians.
Definition: p2os.cpp:867
void check_and_set_vel()
Definition: p2os.cpp:215
ros_p2os_data_t p2os_data
sensor data container
Definition: p2os.hpp:202
geometry_msgs::Twist cmdvel_
Command Velocity subscriber.
Definition: p2os.hpp:196
p2os_msgs::MotorState motors
Provides the state of the motors (enabled or disabled)
Definition: p2os.hpp:60
int radio_modemp
Definition: p2os.hpp:170
double SecsPerTicktoRadsPerSec(int joint, double secs)
Convert Seconds per encoder tick to radians per second.
Definition: p2os.cpp:897
unsigned char DegreesToTicks(int joint, double degrees)
convert degrees to ticks
Definition: p2os.cpp:844
int Send(int fd)
Definition: packet.cpp:157
void check_and_set_motor_state()
Definition: p2os.cpp:139
#define ROS_ERROR(...)
void cmdvel_cb(const geometry_msgs::TwistConstPtr &)
Definition: p2os.cpp:193
#define SYNC1
ros::Publisher ptz_state_pub_
Definition: p2os.hpp:142
nav_msgs::Odometry position
Provides the position of the robot.
Definition: p2os.hpp:56
#define ARGNINT
int direct_wheel_vel_control
Control wheel velocities individually?
Definition: p2os.hpp:169
void check_stall(diagnostic_updater::DiagnosticStatusWrapper &stat)
Definition: p2os.cpp:757
std::string base_link_frame_id
Definition: sip.hpp:73
p2os_msgs::DIO dio
Digital In/Out.
Definition: p2os.hpp:66
ros::Publisher pose_pub_
Definition: p2os.hpp:144
#define ROS_DEBUG(...)


p2os_driver
Author(s): Hunter L. Allen , David Feil-Seifer , Aris Synodinos , Brian Gerkey, Kasper Stoy, Richard Vaughan, Andrew Howard, Tucker Hermans, ActivMedia Robotics LLC, MobileRobots Inc.
autogenerated on Sat Jun 20 2020 03:29:42