00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <iostream>
00025 #include <sstream>
00026 #include <cmath>
00027
00028 #include "ros/ros.h"
00029 #include <tf/transform_broadcaster.h>
00030 #include "geometry_msgs/Twist.h"
00031 #include "nav_msgs/Odometry.h"
00032 #include "segway_rmpX/SegwayStatusStamped.h"
00033
00034 #include "segwayrmp/segwayrmp.h"
00035
00036 #include <boost/thread.hpp>
00037
00038 class SegwayRMPNode;
00039
00040 static SegwayRMPNode * segwayrmp_node_instance;
00041
00042 static double radians_to_degrees = 180.0 / M_PI;
00043 static double degrees_to_radians = M_PI / 180.0;
00044
00045
00046 void handleDebugMessages(const std::string &msg) {ROS_DEBUG("%s",msg.c_str());}
00047 void handleInfoMessages(const std::string &msg) {ROS_INFO("%s",msg.c_str());}
00048 void handleErrorMessages(const std::string &msg) {ROS_ERROR("%s",msg.c_str());}
00049
00050 void handleStatusWrapper(segwayrmp::SegwayStatus::Ptr &ss);
00051
00052
00053 class SegwayRMPNode {
00054 public:
00055 SegwayRMPNode() {
00056 n = new ros::NodeHandle("~");
00057 this->segway_rmp = NULL;
00058 this->first_odometry = false;
00059 this->last_forward_displacement = 0.0;
00060 this->last_yaw_displacement = 0.0;
00061 this->odometry_x = 0.0;
00062 this->odometry_y = 0.0;
00063 this->odometry_w = 0.0;
00064 this->count = 0;
00065 }
00066
00067 ~SegwayRMPNode() {
00068 this->disconnect();
00069 }
00070
00071 void disconnect() {
00072 if (this->segway_rmp != NULL)
00073 delete this->segway_rmp;
00074 this->segway_rmp = NULL;
00075 }
00076
00077 void run() {
00078 if (this->getParameters()) {
00079 return;
00080 }
00081
00082 this->setupSegwayRMP();
00083
00084 this->setupROSComms();
00085
00086
00087 this->keep_alive_timer = n->createTimer(ros::Duration(1.0/20.0), &SegwayRMPNode::keepAliveCallback, this);
00088
00089 ros::AsyncSpinner spinner(1);
00090 spinner.start();
00091
00092 this->connected = false;
00093 while (ros::ok()) {
00094 try {
00095 this->segway_rmp->connect();
00096 this->connected = true;
00097 } catch (std::exception& e) {
00098 std::string e_msg(e.what());
00099 ROS_ERROR("Exception while connecting to the SegwayRMP, check your cables and power buttons.");
00100 ROS_ERROR(" %s", e_msg.c_str());
00101 this->connected = false;
00102 }
00103 if (this->spin()) {
00104 ROS_WARN("Not connected to the SegwayRMP, will retry in 5 seconds...");
00105 ros::Duration(5).sleep();
00106 }
00107 }
00108 }
00109
00110 bool spin() {
00111 if (ros::ok() && this->connected) {
00112 ROS_INFO("Segway RMP Ready.");
00113 while (ros::ok() && this->connected) {
00114 ros::Duration(1.0).sleep();
00115 }
00116 }
00117 if (ros::ok()) {
00118 return true;
00119 } else {
00120 return false;
00121 }
00122 }
00123
00128 void keepAliveCallback(const ros::TimerEvent& e) {
00129 if (ros::ok()) {
00130 boost::mutex::scoped_lock lock(this->m_mutex);
00131
00132
00133 if (this->linear_vel < this->target_linear_vel) {
00134
00135 if (this->linear_pos_accel_limit == 0.0
00136 || this->target_linear_vel - this->linear_vel < this->linear_pos_accel_limit)
00137 this->linear_vel = this->target_linear_vel;
00138 else
00139 this->linear_vel += this->linear_pos_accel_limit;
00140 } else if (this->linear_vel > this->target_linear_vel) {
00141
00142 if (this->linear_neg_accel_limit == 0.0
00143 || this->linear_vel - this->target_linear_vel < this->linear_neg_accel_limit)
00144 this->linear_vel = this->target_linear_vel;
00145 else
00146 this->linear_vel -= this->linear_neg_accel_limit;
00147 }
00148
00149
00150 if (this->angular_vel < this->target_angular_vel) {
00151
00152 if (this->angular_pos_accel_limit == 0.0
00153 || this->target_angular_vel - this->angular_vel < this->angular_pos_accel_limit)
00154 this->angular_vel = this->target_angular_vel;
00155 else
00156 this->angular_vel += this->angular_pos_accel_limit;
00157 } else if (this->angular_vel > this->target_angular_vel) {
00158
00159 if (this->angular_neg_accel_limit == 0.0
00160 || this->angular_vel - this->target_angular_vel < this->angular_neg_accel_limit)
00161 this->angular_vel = this->target_angular_vel;
00162 else
00163 this->angular_vel -= this->angular_neg_accel_limit;
00164 }
00165
00166 ROS_DEBUG("Sending move command: linear velocity = %f, angular velocity = %f",
00167 this->linear_vel, this->angular_vel);
00168
00169
00170
00171
00172 try {
00173 this->segway_rmp->move(this->linear_vel, this->angular_vel);
00174 } catch (std::exception& e) {
00175 std::string e_msg(e.what());
00176 ROS_ERROR("Error commanding Segway RMP: %s", e_msg.c_str());
00177 this->connected = false;
00178 this->disconnect();
00179 }
00180 }
00181 }
00182
00183 void handleStatus(segwayrmp::SegwayStatus::Ptr &ss_ptr) {
00184 if (!this->connected)
00185 return;
00186
00187 ros::Time current_time = ros::Time::now();
00188
00189 this->sss_msg.header.stamp = current_time;
00190
00191 segwayrmp::SegwayStatus &ss = *(ss_ptr);
00192
00193 this->sss_msg.segway.pitch_angle = ss.pitch * degrees_to_radians;
00194 this->sss_msg.segway.pitch_rate = ss.pitch_rate * degrees_to_radians;
00195 this->sss_msg.segway.roll_angle = ss.roll * degrees_to_radians;
00196 this->sss_msg.segway.roll_rate = ss.roll_rate * degrees_to_radians;
00197 this->sss_msg.segway.left_wheel_velocity = ss.left_wheel_speed;
00198 this->sss_msg.segway.right_wheel_velocity = ss.right_wheel_speed;
00199 this->sss_msg.segway.yaw_rate = ss.yaw_rate * degrees_to_radians;
00200 this->sss_msg.segway.servo_frames = ss.servo_frames;
00201 this->sss_msg.segway.left_wheel_displacement = ss.integrated_left_wheel_position;
00202 this->sss_msg.segway.right_wheel_displacement = ss.integrated_right_wheel_position;
00203 this->sss_msg.segway.forward_displacement = ss.integrated_forward_position;
00204 this->sss_msg.segway.yaw_displacement = ss.integrated_turn_position * degrees_to_radians;
00205 this->sss_msg.segway.left_motor_torque = ss.left_motor_torque;
00206 this->sss_msg.segway.right_motor_torque = ss.right_motor_torque;
00207 this->sss_msg.segway.operation_mode = ss.operational_mode;
00208 this->sss_msg.segway.gain_schedule = ss.controller_gain_schedule;
00209 this->sss_msg.segway.ui_battery = ss.ui_battery_voltage;
00210 this->sss_msg.segway.powerbase_battery = ss.powerbase_battery_voltage;
00211 this->sss_msg.segway.motors_enabled = (bool)(ss.motor_status);
00212
00213 segway_status_pub.publish(this->sss_msg);
00214
00215
00216
00217
00218 float forward_displacement = ss.integrated_forward_position;
00219 float yaw_displacement = ss.integrated_turn_position * degrees_to_radians;
00220 float yaw_rate = ss.yaw_rate * degrees_to_radians;
00221
00222
00223
00224 float vel_x = 0.0;
00225 float vel_y = 0.0;
00226 if(!this->first_odometry) {
00227 float delta_forward_displacement =
00228 forward_displacement - this->last_forward_displacement;
00229 double delta_time = (current_time-this->last_time).toSec();
00230
00231
00232 this->odometry_w = yaw_displacement;
00233 float new_odometry_x =
00234 delta_forward_displacement * std::cos(this->odometry_w);
00235 vel_x = (new_odometry_x - this->odometry_x)/delta_time;
00236 this->odometry_x += new_odometry_x;
00237 float new_odometry_y =
00238 delta_forward_displacement * std::sin(this->odometry_w);
00239 vel_y = (new_odometry_y - this->odometry_y)/delta_time;
00240 this->odometry_y += new_odometry_y;
00241 } else {
00242 this->first_odometry = false;
00243 }
00244
00245 this->last_forward_displacement = forward_displacement;
00246 this->last_yaw_displacement = yaw_displacement;
00247 this->last_time = current_time;
00248
00249
00250 geometry_msgs::Quaternion quat =
00251 tf::createQuaternionMsgFromYaw(yaw_displacement);
00252
00253
00254 if (this->broadcast_tf) {
00255 this->odom_trans.header.stamp = current_time;
00256
00257 this->odom_trans.transform.translation.x = this->odometry_x;
00258 this->odom_trans.transform.translation.y = this->odometry_y;
00259 this->odom_trans.transform.translation.z = 0.0;
00260 this->odom_trans.transform.rotation = quat;
00261
00262
00263 this->odom_broadcaster.sendTransform(this->odom_trans);
00264 }
00265
00266
00267 this->odom_msg.header.stamp = current_time;
00268 this->odom_msg.pose.pose.position.x = this->odometry_x;
00269 this->odom_msg.pose.pose.position.y = this->odometry_y;
00270 this->odom_msg.pose.pose.position.z = 0.0;
00271 this->odom_msg.pose.pose.orientation = quat;
00272 this->odom_msg.pose.covariance[0] = 0.00001;
00273 this->odom_msg.pose.covariance[7] = 0.00001;
00274 this->odom_msg.pose.covariance[14] = 1000000000000.0;
00275 this->odom_msg.pose.covariance[21] = 1000000000000.0;
00276 this->odom_msg.pose.covariance[28] = 1000000000000.0;
00277 this->odom_msg.pose.covariance[35] = 0.001;
00278
00279 this->odom_msg.twist.twist.linear.x = vel_x;
00280 this->odom_msg.twist.twist.linear.y = vel_y;
00281 this->odom_msg.twist.twist.angular.z = yaw_rate;
00282
00283 this->odom_pub.publish(this->odom_msg);
00284 }
00285
00291 void motor_timeoutCallback(const ros::TimerEvent& e) {
00292 boost::mutex::scoped_lock lock(m_mutex);
00293
00294 this->target_linear_vel = 0.0;
00295 this->target_angular_vel = 0.0;
00296 }
00297
00301 void cmd_velCallback(const geometry_msgs::Twist::ConstPtr& msg) {
00302 if (!this->connected)
00303 return;
00304 boost::mutex::scoped_lock lock(m_mutex);
00305 double x = msg->linear.x, z = msg->angular.z;
00306 if (this->invert_x) {
00307 x *= -1;
00308 }
00309 if (this->invert_z) {
00310 z *= -1;
00311 }
00312 this->target_linear_vel = x;
00313 this->target_angular_vel = z * radians_to_degrees;
00314
00315
00316
00317
00318 this->motor_timeout_timer =
00319 this->n->createTimer(
00320 ros::Duration(this->segway_motor_timeout),
00321 &SegwayRMPNode::motor_timeoutCallback,
00322 this,
00323 true);
00324 }
00325 private:
00326
00327 void setupROSComms() {
00328
00329 this->cmd_velSubscriber = n->subscribe("cmd_vel", 1000, &SegwayRMPNode::cmd_velCallback, this);
00330
00331 this->segway_status_pub = n->advertise<segway_rmpX::SegwayStatusStamped>("segway_status", 1000);
00332
00333 this->odom_pub = n->advertise<nav_msgs::Odometry>("odom", 50);
00334 }
00335
00336 void setupSegwayRMP() {
00337 std::stringstream ss;
00338 ss << "Connecting to Segway RMP via ";
00339 this->segway_rmp = new segwayrmp::SegwayRMP(this->interface_type, this->segway_rmp_type);
00340 if (this->interface_type_str == "serial") {
00341 ss << "serial on serial port: " << this->serial_port;
00342 this->segway_rmp->configureSerial(this->serial_port);
00343 } else if (this->interface_type_str == "usb") {
00344 ss << "usb ";
00345 if (this->usb_selector == "serial_number") {
00346 ss << "identified by the device serial number: " << this->serial_number;
00347 this->segway_rmp->configureUSBBySerial(this->serial_number);
00348 }
00349 if (this->usb_selector == "description") {
00350 ss << "identified by the device description: " << this->usb_description;
00351 this->segway_rmp->configureUSBByDescription(this->usb_description);
00352 }
00353 if (this->usb_selector == "index") {
00354 ss << "identified by the device index: " << this->usb_index;
00355 this->segway_rmp->configureUSBByIndex(this->usb_index);
00356 }
00357 }
00358 ROS_INFO("%s", ss.str().c_str());
00359
00360
00361 segwayrmp_node_instance = this;
00362
00363
00364 this->segway_rmp->setStatusCallback(handleStatusWrapper);
00365 this->segway_rmp->setLogMsgCallback("debug", handleDebugMessages);
00366 this->segway_rmp->setLogMsgCallback("info", handleInfoMessages);
00367 this->segway_rmp->setLogMsgCallback("error", handleErrorMessages);
00368 }
00369
00370 int getParameters() {
00371
00372 n->param("interface_type", this->interface_type_str, std::string("serial"));
00373
00374 if (this->interface_type_str == "serial") {
00375 this->interface_type = segwayrmp::serial;
00376 n->param("serial_port", this->serial_port, std::string("/dev/ttyUSB0"));
00377 } else if (this->interface_type_str == "usb") {
00378 this->interface_type = segwayrmp::usb;
00379 n->param("usb_selector", this->usb_selector, std::string("index"));
00380 if (this->usb_selector == "index") {
00381 n->param("usb_index", this->usb_index, 0);
00382 } else if (this->usb_selector == "serial_number") {
00383 n->param("usb_serial_number", this->serial_number, std::string("00000000"));
00384 if (this->serial_number == std::string("00000000")) {
00385 ROS_WARN("The serial_number parameter is set to the default 00000000, which shouldn't work.");
00386 }
00387 } else if (this->usb_selector == "description") {
00388 n->param("usb_description", this->serial_number, std::string("Robotic Mobile Platform"));
00389 } else {
00390 ROS_ERROR(
00391 "Invalid USB selector: %s, valid types are 'index', 'serial_number', and 'description'.",
00392 this->usb_selector.c_str());
00393 return 1;
00394 }
00395 } else {
00396 ROS_ERROR(
00397 "Invalid interface type: %s, valid interface types are 'serial' and 'usb'.",
00398 this->interface_type_str.c_str());
00399 return 1;
00400 }
00401
00402 n->param("motor_timeout", this->segway_motor_timeout, 0.5);
00403
00404 n->param("frame_id", frame_id, std::string("base_link"));
00405 this->sss_msg.header.frame_id = this->frame_id;
00406 this->odom_trans.header.frame_id = "odom";
00407 this->odom_trans.child_frame_id = this->frame_id;
00408 this->odom_msg.header.frame_id = "odom";
00409 this->odom_msg.child_frame_id = this->frame_id;
00410
00411 n->param("invert_linear_vel_cmds", invert_x, false);
00412 n->param("invert_angular_vel_cmds", invert_z, false);
00413
00414 n->param("broadcast_tf", this->broadcast_tf, true);
00415
00416 std::string segway_rmp_type_str;
00417 n->param("rmp_type", segway_rmp_type_str, std::string("200/400"));
00418 if (segway_rmp_type_str == "200/400") {
00419 this->segway_rmp_type = segwayrmp::rmp200;
00420 } else if (segway_rmp_type_str == "50/100") {
00421 this->segway_rmp_type = segwayrmp::rmp100;
00422 } else {
00423 ROS_ERROR(
00424 "Invalid rmp type: %s, valid rmp types are '200/400' and '50/100'.",
00425 segway_rmp_type_str.c_str());
00426 return 1;
00427 }
00428
00429
00430 n->param("linear_pos_accel_limit", this->linear_pos_accel_limit, 0.0);
00431 n->param("linear_neg_accel_limit", this->linear_neg_accel_limit, 0.0);
00432
00433
00434 n->param("angular_pos_accel_limit", this->angular_pos_accel_limit, 0.0);
00435 n->param("angular_neg_accel_limit", this->angular_neg_accel_limit, 0.0);
00436
00437
00438 if (this->linear_pos_accel_limit < 0) {
00439 ROS_ERROR("Invalid linear positive acceleration limit of %f (must be non-negative).",
00440 this->linear_pos_accel_limit);
00441 return 1;
00442 }
00443 if (this->linear_neg_accel_limit < 0) {
00444 ROS_ERROR("Invalid linear negative acceleration limit of %f (must be non-negative).",
00445 this->linear_neg_accel_limit);
00446 return 1;
00447 }
00448 if (this->angular_pos_accel_limit < 0) {
00449 ROS_ERROR("Invalid angular positive acceleration limit of %f (must be non-negative).",
00450 this->angular_pos_accel_limit);
00451 return 1;
00452 }
00453 if (this->angular_neg_accel_limit < 0) {
00454 ROS_ERROR("Invalid angular negative acceleration limit of %f (must be non-negative).",
00455 this->angular_neg_accel_limit);
00456 return 1;
00457 }
00458
00459 ROS_INFO("Accel limits: linear: pos = %f, neg = %f, angular: pos = %f, neg = %f.",
00460 this->linear_pos_accel_limit, this->linear_neg_accel_limit,
00461 this->angular_pos_accel_limit, this->angular_neg_accel_limit);
00462
00463
00464
00465 this->linear_pos_accel_limit /= 20;
00466 this->linear_neg_accel_limit /= 20;
00467
00468
00469
00470 this->angular_pos_accel_limit /= 20;
00471 this->angular_neg_accel_limit /= 20;
00472
00473 return 0;
00474 }
00475
00476
00477 ros::NodeHandle * n;
00478
00479 ros::Timer keep_alive_timer;
00480
00481 ros::Subscriber cmd_velSubscriber;
00482 ros::Publisher segway_status_pub;
00483 ros::Publisher odom_pub;
00484 tf::TransformBroadcaster odom_broadcaster;
00485
00486 segwayrmp::SegwayRMP * segway_rmp;
00487
00488 std::string interface_type_str;
00489 segwayrmp::InterfaceType interface_type;
00490 segwayrmp::SegwayRMPType segway_rmp_type;
00491 std::string serial_port;
00492 std::string usb_selector;
00493 std::string serial_number;
00494 std::string usb_description;
00495 int usb_index;
00496
00497 double segway_motor_timeout;
00498 ros::Timer motor_timeout_timer;
00499
00500 std::string frame_id;
00501 bool invert_x, invert_z;
00502 bool broadcast_tf;
00503
00504 double linear_vel;
00505 double angular_vel;
00506
00507 double target_linear_vel;
00508 double target_angular_vel;
00509
00510 double linear_pos_accel_limit;
00511 double linear_neg_accel_limit;
00512 double angular_pos_accel_limit;
00513 double angular_neg_accel_limit;
00514
00515 bool connected;
00516
00517 segway_rmpX::SegwayStatusStamped sss_msg;
00518 geometry_msgs::TransformStamped odom_trans;
00519 nav_msgs::Odometry odom_msg;
00520
00521 int count;
00522
00523 bool first_odometry;
00524 float last_forward_displacement;
00525 float last_yaw_displacement;
00526 float odometry_x;
00527 float odometry_y;
00528 float odometry_w;
00529 ros::Time last_time;
00530
00531 boost::mutex m_mutex;
00532 };
00533
00534
00535 void handleStatusWrapper(segwayrmp::SegwayStatus::Ptr &ss) {
00536 segwayrmp_node_instance->handleStatus(ss);
00537 }
00538
00539 int main(int argc, char **argv) {
00540 ros::init(argc, argv, "segway_rmp_node");
00541
00542 SegwayRMPNode segwayrmp_node;
00543
00544 segwayrmp_node.run();
00545
00546 return 0;
00547 }