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
00330 }
00331 else
00332 ROS_WARN(DEVICE " failed reading steering encoder: "
00333 "initial position may be wrong!");
00334
00335 #else
00336
00337
00338
00339
00340
00341
00342 starting_angle_ = position;
00343 center_ticks_ = 0;
00344 diag_msg_.center_ticks = center_ticks_;
00345 starting_ticks_ = degrees2ticks(starting_angle_);
00346 ROS_INFO("starting ticks = %d, center ticks = %d",
00347 starting_ticks_, center_ticks_);
00348 rc = write_register(20, starting_ticks_);
00349 if (rc != 0)
00350 {
00351 ROS_ERROR(DEVICE " failed to set initial wheel encoder position.");
00352 return rc;
00353 }
00354
00355
00356
00357
00358
00359
00360
00361 write_register(39, center_ticks_ - 300001);
00362 write_register(40, center_ticks_ + 300001);
00363
00364
00365
00366
00367
00368 rc = servo_cmd("@16 240 0 0\r");
00369 if (rc != 0)
00370 ROS_ERROR(DEVICE " failed to enter PMC mode.");
00371
00372 #endif
00373
00374 return rc;
00375 }
00376
00377 int devsteer::steering_absolute(float position)
00378 {
00379 DBG("steering_absolute(%.3f)", position);
00380 req_angle_ = limit_travel(position);
00381 return encoder_goto(req_angle_);
00382 }
00383
00384 int devsteer::steering_relative(float delta)
00385 {
00386 DBG("steering_relative(%.3f)", delta);
00387 return steering_absolute(req_angle_ + delta);
00388 }
00389
00391
00393
00394
00395
00396 int devsteer::configure_steering(void)
00397 {
00398 int rc;
00399
00400 #if 0 // does not seem good (before or after RST)...
00401 rc = servo_cmd("@16 2\r");
00402 if (rc != 0) return rc;
00403 #endif
00404
00405 #if 0 // should clear persistent KMC status, but does not work
00406 rc = servo_cmd("@16 163\r");
00407 if (rc != 0) return rc;
00408 rc = servo_cmd("@16\r");
00409 if (rc != 0) return rc;
00410 rc = servo_cmd("@16 1 65535\r");
00411 if (rc != 0) return rc;
00412 rc = servo_cmd("@16 146\r");
00413 if (rc != 0) return rc;
00414
00415
00416 rc = servo_cmd("@16 167 256 256\r");
00417 if (rc != 0) return rc;
00418
00419 rc = servo_cmd("@16 227\r");
00420 if (rc != 0) return rc;
00421 #endif
00422
00423 #if 1
00424
00425
00426 servo_write_only("@16 4\r");
00427 usleep(1000000);
00428 #endif
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438 servo_cmd("@16 242\r");
00439
00440 rc = write_register(20, 0);
00441 if (rc != 0) return rc;
00442
00443
00444 rc = write_register(21, 193274);
00445 if (rc != 0) return rc;
00446
00447
00448 rc = write_register(22, 322122547);
00449 if (rc != 0) return rc;
00450
00451 rc = write_register(23, 193274);
00452 if (rc != 0) return rc;
00453
00454 rc = write_register(24, 0);
00455 if (rc != 0) return rc;
00456
00457 #if 1
00458
00459 servo_cmd("@16 167 256 256\r");
00460
00461
00462
00463 servo_cmd("@16 181 0\r");
00464 #endif
00465
00466 return rc;
00467 }
00468
00469
00470 int devsteer::encoder_goto(float degrees)
00471 {
00472 int32_t ticks = degrees2ticks(degrees);
00473 diag_msg_.last_request = ticks;
00474 ROS_DEBUG("setting steering angle to %.3f (%d ticks)", degrees, ticks);
00475
00476
00477 return write_register(20, ticks);
00478 }
00479
00480
00481 int devsteer::write_register(int reg, int32_t val)
00482 {
00483 char string[MAX_SERVO_CMD_BUFFER];
00484 ROS_DEBUG("writing %d to register %d", val, reg);
00485 snprintf(string, MAX_SERVO_CMD_BUFFER, "@16 11 %d %d\r", reg, val);
00486 return servo_cmd(string);
00487 }
00488
00489
00490
00491
00492
00493
00494 int devsteer::send_cmd(const char *string)
00495 {
00496 int len = strlen(string);
00497
00498
00499 ROS_DEBUG(" " DEVICE " command: `%s'", string);
00500
00501 if (!have_tty)
00502 {
00503 strncpy(buffer, "* 10", 4);
00504 return 0;
00505 }
00506
00507 int rc=0;
00508 int attempts = 3;
00509 do
00510 {
00511
00512
00513
00514 int res = write(fd, string, len);
00515 if (res < 0)
00516 ROS_ERROR_THROTTLE(100, "write() error: %d", errno);
00517
00518
00519 int timeout = 20;
00520 int linelen = 0;
00521
00522 int64_t start_time = GetTime();
00523 int64_t stop_time = start_time + timeout;
00524
00525 while (true)
00526 {
00527 if (timeout >= 0) {
00528 struct pollfd fds[1];
00529 fds[0].fd=fd;
00530 fds[0].events = POLLIN;
00531 int64_t delay = stop_time - GetTime();
00532 if (delay < 0) delay = 0;
00533 int retval = ::poll(fds, 1, delay);
00534 if (retval < 0)
00535 {
00536 if (errno == EINTR)
00537 continue;
00538 ROS_ERROR("error returned on poll");
00539 rc= errno;
00540 break;
00541 }
00542 else if (!retval)
00543 {
00544 ROS_WARN("timeout on poll");
00545 rc= EBUSY;
00546 break;
00547 }
00548 else {
00549 if ((fds[0].revents & POLLERR) ||
00550 (fds[0].revents & POLLHUP) ||
00551 (fds[0].revents & POLLNVAL))
00552 {
00553 ROS_ERROR("Device error on poll");
00554 rc= EIO;
00555 break;
00556 }
00557 }
00558 }
00559
00560 int bytes = read(fd, buffer + linelen, 1);
00561 if (bytes < 0)
00562 {
00563 if (errno == EINTR)
00564 continue;
00565 rc = errno;
00566 ROS_ERROR("error: %s", strerror(rc));
00567 break;
00568 }
00569 ROS_DEBUG_NAMED("details",
00570 DEVICE " read() returns %d %c(0x%02X)",
00571 bytes, buffer[linelen], buffer[linelen]);
00572 if (bytes==0)
00573 continue;
00574 else if (buffer[linelen] != '\r')
00575 {
00576
00577 if (++linelen >= MAX_SERVO_CMD_BUFFER)
00578 {
00579 ROS_ERROR(DEVICE " buffer overflow: %s", buffer);
00580 rc = ENOSPC;
00581 break;
00582 }
00583 }
00584 else
00585 {
00586 buffer[linelen] = '\0';
00587 return 0;
00588 }
00589 }
00590
00591
00592 tcflush(fd, TCIOFLUSH);
00593 }
00594 while (--attempts > 0);
00595
00596 return rc;
00597 }
00598
00599
00600
00601
00602 int devsteer::servo_cmd(const char *string)
00603 {
00604 if (training_)
00605 return 0;
00606
00607 int rc = send_cmd(string);
00608 if ((rc == 0)
00609 && strncmp(buffer, "* 10", 4) != 0)
00610 {
00611 ROS_INFO(DEVICE " returned error: %s", buffer);
00612 rc = EIO;
00613 }
00614 return rc;
00615 }
00616
00621 void devsteer::servo_write_only(const char *string)
00622 {
00623 ROS_DEBUG("servo_write_only %s", string);
00624
00625
00626
00627 tcflush(fd, TCIOFLUSH);
00628
00629
00630
00631
00632 int res = write(fd, string, strlen(string));
00633 if (res < 0)
00634 ROS_ERROR_THROTTLE(100, "write() error: %d", errno);
00635 }