00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <fcntl.h>
00011 #include <termios.h>
00012 #include <poll.h>
00013
00014 #include <ros/ros.h>
00015 #include <art_msgs/ArtHertz.h>
00016 #include <art_msgs/ArtVehicle.h>
00017 #include "devsteer.h"
00018 #include "silverlode.h"
00019
00021
00023
00024
00025 #define CLASS "devsteer"
00026
00027
00028 int64_t devsteer::GetTime()
00029 {
00030 ros::Time t = ros::Time::now();
00031 return (int64_t) t.sec * 1000 + (int64_t) t.nsec / 1000000;
00032 }
00033
00034 devsteer::devsteer(int32_t center):
00035 req_angle_(0.0),
00036 center_ticks_(center)
00037 {}
00038
00039 int devsteer::Open()
00040 {
00041
00042 int rc = this->Servo::Open(port_.c_str(), (O_RDWR|O_NOCTTY|O_NONBLOCK));
00043 if (fd < 0) {
00044 ROS_ERROR("Couldn't open %s (%s)", port_.c_str(), strerror(errno));
00045 return -1;
00046 }
00047
00048
00049 rc = configure_raw_port((B57600|CS8), 0);
00050 if (rc != 0) goto fail;
00051
00052 rc = configure_steering();
00053 if (rc != 0) goto fail;
00054
00055 req_angle_ = starting_angle_ = 0.0;
00056 starting_ticks_ = 0;
00057
00058 return rc;
00059
00060 fail:
00061 this->Servo::Close();
00062 return rc;
00063 }
00064
00065 int devsteer::Close()
00066 {
00067 if (center_on_exit_)
00068 steering_absolute(0.0);
00069 return this->Servo::Close();
00070 }
00071
00072 int devsteer::Configure()
00073 {
00074
00075 ros::NodeHandle private_nh("~");
00076
00077 private_nh.param("port", port_, std::string("/dev/steering"));
00078 ROS_INFO_STREAM("steering port = " << port_);
00079
00080 private_nh.param("center_on_exit", center_on_exit_, false);
00081 if (center_on_exit_)
00082 ROS_INFO("center steering on exit");
00083 else
00084 ROS_INFO("do not center steering on exit");
00085
00086 private_nh.param("training", training_, false);
00087 if (training_)
00088 ROS_INFO("using training mode");
00089
00090 private_nh.param("simulate_moving_errors",
00091 simulate_moving_errors_, false);
00092 if (simulate_moving_errors_)
00093 ROS_INFO("simulating intermittent moving errors");
00094
00095 if (training_)
00096 ROS_INFO("using training mode");
00097
00098 steering_rate_ = art_msgs::ArtVehicle::max_steer_degrees / 2.0;
00099 private_nh.getParam("steering_rate", steering_rate_);
00100 ROS_INFO("steering rate is %.2f degrees/sec.", steering_rate_);
00101
00102 return 0;
00103 }
00104
00110 int devsteer::check_status(void)
00111 {
00112 int rc = get_status_word(diag_msg_.status_word);
00113
00114 if (rc == 0)
00115 {
00116
00117 uint16_t bad_status = (silverlode::isw::moving_error);
00118 uint16_t required_status = (silverlode::isw::temp_driver_en);
00119 if ((diag_msg_.status_word & bad_status) != 0
00120 || (diag_msg_.status_word & required_status) != required_status)
00121 {
00122 ROS_ERROR("SilverLode internal status error: 0x%04x",
00123 diag_msg_.status_word);
00124 rc = EIO;
00125 }
00126 }
00127
00128 return rc;
00129 }
00130
00144 int devsteer::get_angle(float °rees)
00145 {
00146 int rc = 0;
00147
00148 if (have_tty)
00149 {
00150 int32_t iticks;
00151 rc = get_encoder(iticks);
00152 if (rc == 0)
00153 {
00154 degrees = ticks2degrees(iticks);
00155 }
00156 else
00157 {
00158 ROS_WARN("encoder read failure, cannot estimate position");
00159 }
00160 }
00161 else
00162 {
00163
00164 float remaining_angle = req_angle_ - degrees;
00165 float degrees_per_cycle = (steering_rate_ /
00166 art_msgs::ArtHertz::STEERING);
00167
00168 DBG("remaining angle %.3f, degrees per cycle %.3f",
00169 remaining_angle, degrees_per_cycle);
00170
00171 if (fabs(remaining_angle) <= degrees_per_cycle)
00172 {
00173 degrees = req_angle_;
00174 }
00175 else
00176 {
00177 degrees += ((remaining_angle >= 0.0)?
00178 degrees_per_cycle: -degrees_per_cycle);
00179 }
00180 ROS_DEBUG("simulated angle = %.2f degrees", degrees);
00181
00182
00183 diag_msg_.encoder = degrees2ticks(degrees);
00184 }
00185
00186 return rc;
00187 }
00188
00197 int devsteer::get_encoder(int32_t &iticks)
00198 {
00199 int rc = send_cmd("@16 12 1\r");
00200 if (rc == 0 && have_tty)
00201 {
00202
00203
00204 unsigned int pos_high, pos_low;
00205 if (2 == sscanf(buffer, "# 10 000C %4x %4x", &pos_high, &pos_low))
00206 {
00207 iticks = (pos_high << 16) + pos_low;
00208 ROS_DEBUG(" " DEVICE " response: `%s'", buffer);
00209 diag_msg_.encoder = iticks;
00210 }
00211 else
00212 {
00213 ROS_INFO(" " DEVICE " unexpected response: `%s'", buffer);
00214 rc = EINVAL;
00215 }
00216 }
00217 return rc;
00218 }
00219
00220
00221
00222
00223
00224
00225
00226 int devsteer::get_status_word(uint16_t &status)
00227 {
00228 int rc = send_cmd("@16 20\r");
00229 if (rc == 0)
00230 {
00231 if (have_tty)
00232 {
00233 unsigned int isw;
00234 if (1 == sscanf(buffer, "# 10 0014 %4x", &isw))
00235 {
00236 status = isw;
00237 ROS_DEBUG(" " DEVICE " status: `%s'", buffer);
00238 }
00239 else
00240 {
00241 ROS_WARN(" " DEVICE " unexpected response: `%s'", buffer);
00242 rc = EINVAL;
00243 }
00244 }
00245 else
00246 {
00247 status = silverlode::isw::temp_driver_en;
00248 }
00249
00250 if (simulate_moving_errors_)
00251 {
00252
00253 ros::Time now = ros::Time::now();
00254 if ((now.sec & 0x003C) == 0)
00255 status |= silverlode::isw::moving_error;
00256 }
00257 }
00258 return rc;
00259 }
00260
00266 void devsteer::publish_diag(const ros::Publisher &diag_pub)
00267 {
00268 get_encoder(diag_msg_.encoder);
00269 diag_msg_.header.stamp = ros::Time::now();
00270 diag_pub.publish(diag_msg_);
00271 }
00272
00284 int devsteer::set_initial_angle(float position)
00285 {
00286 int rc = 0;
00287
00288 #if 0
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298 rc = get_encoder(starting_ticks_);
00299 if (rc == 0)
00300 {
00301
00302
00303 starting_angle_ = position;
00304 center_ticks_ =
00305 (int32_t) lrint(-starting_angle_ * TICKS_PER_DEGREE) + starting_ticks_;
00306 diag_msg_.center_ticks = center_ticks_;
00307
00308 ROS_INFO("starting ticks = %d, center ticks = %d",
00309 starting_ticks_, center_ticks_);
00310
00311
00312
00313
00314
00315
00316
00317
00318 write_register(39, center_ticks_ - 300001);
00319 write_register(40, center_ticks_ + 300001);
00320
00321
00322
00323
00324
00325 rc = servo_cmd("@16 240 0 0\r");
00326 if (rc != 0)
00327 ROS_WARN(DEVICE " failed to enter PMC mode.");
00328 }
00329 else
00330 ROS_WARN(DEVICE " failed reading steering encoder: "
00331 "initial position may be wrong!");
00332
00333 #else
00334
00335
00336
00337
00338
00339
00340 starting_angle_ = position;
00341 center_ticks_ = 0;
00342 diag_msg_.center_ticks = center_ticks_;
00343 starting_ticks_ = degrees2ticks(starting_angle_);
00344 ROS_INFO("starting ticks = %d, center ticks = %d",
00345 starting_ticks_, center_ticks_);
00346 rc = write_register(20, starting_ticks_);
00347 if (rc != 0)
00348 {
00349 ROS_ERROR(DEVICE " failed to set initial wheel encoder position.");
00350 return rc;
00351 }
00352
00353
00354
00355
00356
00357
00358
00359 write_register(39, center_ticks_ - 300001);
00360 write_register(40, center_ticks_ + 300001);
00361
00362
00363
00364
00365
00366 rc = servo_cmd("@16 240 0 0\r");
00367 if (rc != 0)
00368 ROS_ERROR(DEVICE " failed to enter PMC mode.");
00369
00370 #endif
00371
00372 return rc;
00373 }
00374
00375 int devsteer::steering_absolute(float position)
00376 {
00377 DBG("steering_absolute(%.3f)", position);
00378 req_angle_ = limit_travel(position);
00379 return encoder_goto(req_angle_);
00380 }
00381
00382 int devsteer::steering_relative(float delta)
00383 {
00384 DBG("steering_relative(%.3f)", delta);
00385 return steering_absolute(req_angle_ + delta);
00386 }
00387
00389
00391
00392
00393
00394 int devsteer::configure_steering(void)
00395 {
00396 int rc;
00397
00398 #if 0 // does not seem good (before or after RST)...
00399 rc = servo_cmd("@16 2\r");
00400 if (rc != 0) return rc;
00401 #endif
00402
00403 #if 0 // should clear persistent KMC status, but does not work
00404 rc = servo_cmd("@16 163\r");
00405 if (rc != 0) return rc;
00406 rc = servo_cmd("@16\r");
00407 if (rc != 0) return rc;
00408 rc = servo_cmd("@16 1 65535\r");
00409 if (rc != 0) return rc;
00410 rc = servo_cmd("@16 146\r");
00411 if (rc != 0) return rc;
00412
00413
00414 rc = servo_cmd("@16 167 256 256\r");
00415 if (rc != 0) return rc;
00416
00417 rc = servo_cmd("@16 227\r");
00418 if (rc != 0) return rc;
00419 #endif
00420
00421 #if 1
00422
00423
00424 servo_write_only("@16 4\r");
00425 usleep(1000000);
00426 #endif
00427
00428
00429
00430
00431
00432
00433
00434
00435 servo_cmd("@16 242\r");
00436
00437 rc = write_register(20, 0);
00438 if (rc != 0) return rc;
00439
00440
00441 rc = write_register(21, 193274);
00442 if (rc != 0) return rc;
00443
00444
00445 rc = write_register(22, 322122547);
00446 if (rc != 0) return rc;
00447
00448 rc = write_register(23, 193274);
00449 if (rc != 0) return rc;
00450
00451 rc = write_register(24, 0);
00452 if (rc != 0) return rc;
00453
00454 #if 1
00455
00456 rc = servo_cmd("@16 167 256 256\r");
00457
00458
00459
00460 rc = servo_cmd("@16 181 0\r");
00461 #endif
00462
00463 return rc;
00464 }
00465
00466
00467 int devsteer::encoder_goto(float degrees)
00468 {
00469 int32_t ticks = degrees2ticks(degrees);
00470 diag_msg_.last_request = ticks;
00471 ROS_DEBUG("setting steering angle to %.3f (%d ticks)", degrees, ticks);
00472
00473
00474 return write_register(20, ticks);
00475 }
00476
00477
00478 int devsteer::write_register(int reg, int32_t val)
00479 {
00480 char string[MAX_SERVO_CMD_BUFFER];
00481 ROS_DEBUG("writing %d to register %d", val, reg);
00482 snprintf(string, MAX_SERVO_CMD_BUFFER, "@16 11 %d %d\r", reg, val);
00483 return servo_cmd(string);
00484 }
00485
00486
00487
00488
00489
00490
00491 int devsteer::send_cmd(const char *string)
00492 {
00493 int len = strlen(string);
00494
00495
00496 ROS_DEBUG(" " DEVICE " command: `%s'", string);
00497
00498 if (!have_tty)
00499 {
00500 strncpy(buffer, "* 10", 4);
00501 return 0;
00502 }
00503
00504 int rc=0;
00505 int attempts = 3;
00506 do
00507 {
00508
00509
00510
00511 int res;
00512 res = write(fd, string, len);
00513
00514
00515 int timeout = 20;
00516 int linelen = 0;
00517
00518 int64_t start_time = GetTime();
00519 int64_t stop_time = start_time + timeout;
00520
00521 while (true)
00522 {
00523 if (timeout >= 0) {
00524 struct pollfd fds[1];
00525 fds[0].fd=fd;
00526 fds[0].events = POLLIN;
00527 int64_t delay = stop_time - GetTime();
00528 if (delay < 0) delay = 0;
00529 int retval = ::poll(fds, 1, delay);
00530 if (retval < 0)
00531 {
00532 if (errno == EINTR)
00533 continue;
00534 ROS_ERROR("error returned on poll");
00535 rc= errno;
00536 break;
00537 }
00538 else if (!retval)
00539 {
00540 ROS_WARN("timeout on poll");
00541 rc= EBUSY;
00542 break;
00543 }
00544 else {
00545 if ((fds[0].revents & POLLERR) ||
00546 (fds[0].revents & POLLHUP) ||
00547 (fds[0].revents & POLLNVAL))
00548 {
00549 ROS_ERROR("Device error on poll");
00550 rc= EIO;
00551 break;
00552 }
00553 }
00554 }
00555
00556 int bytes = read(fd, buffer + linelen, 1);
00557 if (bytes < 0)
00558 {
00559 if (errno == EINTR)
00560 continue;
00561 rc = errno;
00562 ROS_ERROR("error: %s", strerror(rc));
00563 break;
00564 }
00565 ROS_DEBUG_NAMED("details",
00566 DEVICE " read() returns %d %c(0x%02X)",
00567 bytes, buffer[linelen], buffer[linelen]);
00568 if (bytes==0)
00569 continue;
00570 else if (buffer[linelen] != '\r')
00571 {
00572
00573 if (++linelen >= MAX_SERVO_CMD_BUFFER)
00574 {
00575 ROS_ERROR(DEVICE " buffer overflow: %s", buffer);
00576 rc = ENOSPC;
00577 break;
00578 }
00579 }
00580 else
00581 {
00582 buffer[linelen] = '\0';
00583 return 0;
00584 }
00585 }
00586
00587
00588 tcflush(fd, TCIOFLUSH);
00589 }
00590 while (--attempts > 0);
00591
00592 return rc;
00593 }
00594
00595
00596
00597
00598 int devsteer::servo_cmd(const char *string)
00599 {
00600 if (training_)
00601 return 0;
00602
00603 int rc = send_cmd(string);
00604 if ((rc == 0)
00605 && strncmp(buffer, "* 10", 4) != 0)
00606 {
00607 ROS_INFO(DEVICE " returned error: %s", buffer);
00608 rc = EIO;
00609 }
00610 return rc;
00611 }
00612
00617 void devsteer::servo_write_only(const char *string)
00618 {
00619 ROS_DEBUG("servo_write_only %s", string);
00620
00621
00622
00623 tcflush(fd, TCIOFLUSH);
00624
00625
00626
00627
00628 int res;
00629 res = write(fd, string, strlen(string));
00630 }