segwayrmp.cc

00001 /*
00002  *  Player - One Hell of a Robot Server
00003  *  Copyright (C) 2003  John Sweeney & Brian Gerkey
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  *
00019  */
00020 
00021 /*
00022   Desc: Driver for the Segway RMP
00023   Author: John Sweeney and Brian Gerkey
00024   Date:
00025   CVS: $Id: segwayrmp.cc 4135 2007-08-23 19:58:48Z gerkey $
00026 */
00027 
00127 #include <sys/types.h>
00128 #include <netinet/in.h>
00129 #include <sys/stat.h>
00130 #include <fcntl.h>
00131 #include <string.h>
00132 #include <errno.h>
00133 #include <unistd.h>
00134 #include <math.h>
00135 
00136 #define PLAYER_ENABLE_MSG 0
00137 
00138 #include "player.h"
00139 #include "error.h"
00140 #include "driver.h"
00141 #include "drivertable.h"
00142 
00143 #include "rmp_frame.h"
00144 #include "segwayrmp.h"
00145 
00146 
00147 // Number of RMP read cycles, without new speed commands from clients,
00148 // after which we'll stop the robot (for safety).  The read loop
00149 // seems to run at about 50Hz, or 20ms per cycle.
00150 #define RMP_TIMEOUT_CYCLES 20 // about 400ms
00151 
00152 
00154 // A factory creation function
00155 Driver* SegwayRMP_Init(ConfigFile* cf, int section)
00156 {
00157   // Create and return a new instance of this driver
00158   return ((Driver*) (new SegwayRMP(cf, section)));
00159 }
00160 
00161 
00163 // A driver registration function.
00164 void SegwayRMP_Register(DriverTable* table)
00165 {
00166   table->AddDriver("segwayrmp", SegwayRMP_Init);
00167 }
00168 
00169 
00171 // Constructor
00172 SegwayRMP::SegwayRMP(ConfigFile* cf, int section)
00173     : Driver(cf, section, true, PLAYER_MSGQUEUE_DEFAULT_MAXLEN)
00174 {
00175   memset(&this->position_id, 0, sizeof(player_devaddr_t));
00176   memset(&this->position3d_id, 0, sizeof(player_devaddr_t));
00177   memset(&this->power_id, 0, sizeof(player_devaddr_t));
00178 
00179   // Do we create a position interface?
00180   if(cf->ReadDeviceAddr(&(this->position_id), section, "provides", 
00181                       PLAYER_POSITION2D_CODE, -1, NULL) == 0)
00182   {
00183     if(this->AddInterface(this->position_id) != 0)
00184     {
00185       this->SetError(-1);    
00186       return;
00187     }
00188   }
00189 
00190   // Do we create a position3d interface?
00191   if(cf->ReadDeviceAddr(&(this->position3d_id), section, "provides", 
00192                         PLAYER_POSITION3D_CODE, -1, NULL) == 0)
00193   {
00194     if(this->AddInterface(this->position3d_id) != 0)
00195     {
00196       this->SetError(-1);    
00197       return;
00198     }
00199   }
00200 
00201   // Do we create a power interface?
00202   if(cf->ReadDeviceAddr(&(this->power_id), section, "provides", 
00203                         PLAYER_POWER_CODE, -1, NULL) == 0)
00204   {
00205     if(this->AddInterface(this->power_id) != 0)
00206     {
00207       this->SetError(-1);    
00208       return;
00209     }
00210   }
00211 
00212   this->canio = NULL;
00213   this->caniotype = cf->ReadString(section, "canio", "kvaser");
00214   this->max_xspeed = (int) (1000.0 * cf->ReadLength(section, "max_xspeed", 0.5));
00215   if(this->max_xspeed < 0)
00216     this->max_xspeed = -this->max_xspeed;
00217   this->max_yawspeed = (int) (RTOD(cf->ReadAngle(section, "max_yawspeed", 40)));
00218   if(this->max_yawspeed < 0)
00219     this->max_yawspeed = -this->max_yawspeed;
00220   
00221   return;
00222 }
00223 
00224 
00225 SegwayRMP::~SegwayRMP()
00226 {
00227   return;
00228 }
00229 
00230 int
00231 SegwayRMP::Setup()
00232 {
00233   // Clear the command buffers
00234 #if 0
00235   if (this->position_id.code)
00236     ClearCommand(this->position_id);
00237   if (this->position3d_id.code)
00238     ClearCommand(this->position3d_id);
00239 #endif
00240 
00241   PLAYER_MSG0(2, "CAN bus initializing");
00242 
00243   if(!strcmp(this->caniotype, "kvaser"))
00244     assert(this->canio = new CANIOKvaser);
00245   else
00246   {
00247     PLAYER_ERROR1("Unknown CAN I/O type: \"%s\"", this->caniotype);
00248     return(-1);
00249   }
00250 
00251   // start the CAN at 500 kpbs
00252   if(this->canio->Init(BAUD_500K) < 0) 
00253   {
00254     PLAYER_ERROR("error on CAN Init");
00255     return(-1);
00256   }
00257 
00258   // Initialize odometry
00259   this->odom_x = this->odom_y = this->odom_yaw = 0.0;
00260 
00261   this->curr_xspeed = this->curr_yawspeed = 0.0;
00262   this->motor_allow_enable = false;
00263   this->motor_enabled = false;
00264   this->firstread = true;
00265   this->timeout_counter = 0;
00266 
00267   this->StartThread();
00268 
00269   return(0);
00270 }
00271 
00272 int
00273 SegwayRMP::Shutdown()
00274 {
00275   PLAYER_MSG0(2, "Shutting down CAN bus");
00276   fflush(stdout);
00277   
00278   // TODO: segfaulting in here somewhere on client disconnect, but only 
00279   // sometimes.  
00280   //
00281   // UPDATE: This might have been fixed by moving the call to StopThread()
00282   // to before the sending of zero velocities.   There could have been
00283   // a race condition, since Shutdown() is called from the server's thread 
00284   // context.
00285 
00286   StopThread();
00287   
00288   // send zero velocities, for some semblance of safety
00289   CanPacket pkt;
00290 
00291   MakeVelocityCommand(&pkt,0,0);
00292   Write(pkt);
00293 
00294   // shutdown the CAN
00295   canio->Shutdown();
00296   delete canio;
00297   canio = NULL;
00298   
00299   return(0);
00300 }
00301 
00302 // Main function for device thread.
00303 void 
00304 SegwayRMP::Main()
00305 {
00306   //unsigned char buffer[256];
00307   //size_t buffer_len;
00308   //player_position2d_cmd_t position_cmd;
00309   //player_position3d_cmd_t position3d_cmd;
00310   //void *client;
00311   CanPacket pkt;
00312   //int32_t xspeed,yawspeed;
00313   //bool got_command;
00314   bool first;
00315 
00316   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
00317   pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
00318 
00319   first = true;  
00320   PLAYER_MSG0(2, "starting main loop");
00321   
00322   for(;;)
00323   {
00324     pthread_testcancel();
00325     ProcessMessages(); 
00326     // Read from the RMP
00327     if(Read() < 0)
00328     {
00329       PLAYER_ERROR("Read() errored; bailing");
00330       pthread_exit(NULL);
00331     }
00332 
00333     // Note that we got some data
00334     if (first)
00335     {
00336       first = false;
00337       PLAYER_MSG0(2, "got data from rmp");
00338     }
00339 
00340     // TODO: report better timestamps, possibly using time info from the RMP
00341 
00342     // Send data to clients
00343     Publish(this->position_id, PLAYER_MSGTYPE_DATA, PLAYER_POSITION2D_DATA_STATE,
00344               &this->position_data, sizeof(this->position_data), NULL);
00345     Publish(this->position3d_id, PLAYER_MSGTYPE_DATA, PLAYER_POSITION3D_DATA_STATE,
00346               &this->position3d_data, sizeof(this->position3d_data), NULL);
00347     Publish(this->power_id, PLAYER_MSGTYPE_DATA, PLAYER_POWER_DATA_STATE,
00348               &this->power_data, sizeof(this->power_data), NULL);
00349     
00350 /*    // check for config requests from the position interface
00351     if((buffer_len = GetConfig(this->position_id, &client, buffer, sizeof(buffer),NULL)) > 0)
00352     {
00353       // if we write to the CAN bus as a result of the config, don't write
00354       // a velocity command (may need to make this smarter if we get slow
00355       // velocity control).
00356       if(HandlePositionConfig(client,buffer,buffer_len) > 0)
00357         continue;
00358     }
00359 
00360     // check for config requests from the position3d interface
00361     if((buffer_len = GetConfig(this->position3d_id, &client, buffer, sizeof(buffer),NULL)) > 0)
00362     {
00363       // if we write to the CAN bus as a result of the config, don't write
00364       // a velocity command (may need to make this smarter if we get slow
00365       // velocity control).
00366       if(HandlePosition3DConfig(client,buffer,buffer_len) > 0)
00367         continue;
00368     }
00369 
00370     // start with the last commanded values
00371     xspeed = this->last_xspeed;
00372     yawspeed = this->last_yawspeed;
00373     got_command = false;
00374 
00375     // Check for commands from the position interface
00376     if (this->position_id.code)
00377     {
00378       if(GetCommand(this->position_id, (void*) &position_cmd, 
00379                     sizeof(position_cmd),NULL))
00380       {
00381         // zero the command buffer, so that we can timeout if a client doesn't
00382         // send commands for a while
00383         ClearCommand(this->position_id);
00384 
00385         // convert to host order; let MakeVelocityCommand do the rest
00386         xspeed = ntohl(position_cmd.xspeed);
00387         yawspeed = ntohl(position_cmd.yawspeed);
00388         motor_enabled = position_cmd.state && motor_allow_enable;
00389         timeout_counter=0;
00390         got_command = true;
00391       }
00392     }
00393 
00394     // Check for commands from the position3d interface
00395     if (this->position3d_id.code)
00396     {
00397       if(GetCommand(this->position3d_id, (void*) &position3d_cmd, 
00398                     sizeof(position3d_cmd),NULL))
00399       {
00400         // zero the command buffer, so that we can timeout if a client doesn't
00401         // send commands for a while
00402         ClearCommand(this->position3d_id);
00403 
00404         // convert to host order; let MakeVelocityCommand do the rest
00405         // Position3d uses milliradians/sec, so convert here to
00406         // degrees/sec
00407         xspeed = ntohl(position3d_cmd.xspeed);
00408         yawspeed = (int32_t) (((double) (int32_t) ntohl(position3d_cmd.yawspeed)) / 1000 * 180 / M_PI);
00409         motor_enabled = position3d_cmd.state && motor_allow_enable;
00410         timeout_counter=0;
00411         got_command = true;
00412       }
00413     }
00414     // No commands, so we may timeout soon
00415     if (!got_command)*/
00416       timeout_counter++;
00417 
00418     if(timeout_counter >= RMP_TIMEOUT_CYCLES)
00419     {
00420       if(curr_xspeed || curr_yawspeed)
00421       {
00422         PLAYER_WARN("timeout exceeded without new commands; stopping robot");
00423         curr_xspeed = 0;
00424         curr_yawspeed = 0;
00425       }
00426       // set it to the limit, to prevent overflow, but keep the robot
00427       // stopped until a new command comes in.
00428       timeout_counter = RMP_TIMEOUT_CYCLES;
00429     }
00430 
00431     if(!motor_enabled) 
00432     {
00433       curr_xspeed = 0;
00434       curr_yawspeed = 0;
00435     }
00436 
00437     // make a velocity command... could be zero
00438     MakeVelocityCommand(&pkt,static_cast<int> (curr_xspeed),static_cast<int> (curr_yawspeed));
00439     if(Write(pkt) < 0)
00440       PLAYER_ERROR("error on write");
00441   }
00442 }
00443 
00444 int
00445 SegwayRMP::ProcessMessage(QueuePointer & resp_queue,
00446                           player_msghdr * hdr,
00447                           void * data)
00448 {
00451   
00452   // 2-D velocity command
00453   if(Message::MatchMessage(hdr, PLAYER_MSGTYPE_CMD, 
00454                            PLAYER_POSITION2D_CMD_VEL, 
00455                            this->position_id))
00456   {
00457     player_position2d_cmd_vel_t* cmd = (player_position2d_cmd_vel_t*)data;
00458     this->curr_xspeed = cmd->vel.px * 1000.0; //Added multiply by 1000.0, BMS.
00459     this->curr_yawspeed = cmd->vel.pa * 180.0/M_PI;  //Added multiply by 1000.0, BMS.
00460     this->motor_enabled = cmd->state & this->motor_allow_enable;
00461     this->timeout_counter = 0;
00462     return 0;
00463   }
00464   // 3-D velocity command
00465   if(Message::MatchMessage(hdr, PLAYER_MSGTYPE_CMD, 
00466                            PLAYER_POSITION3D_CMD_SET_VEL,
00467                            this->position3d_id))
00468   {
00469     player_position3d_cmd_vel_t* cmd = (player_position3d_cmd_vel_t*)data;
00470     this->curr_xspeed = cmd->vel.px * 1000.0; //Added multiply by 1000.0, BMS.
00471     this->curr_yawspeed = cmd->vel.pyaw * 180.0/M_PI; 
00472     this->motor_enabled = cmd->state & this->motor_allow_enable;
00473     this->timeout_counter = 0;
00474     return 0;
00475   }
00476 
00477   if (hdr->type == PLAYER_MSGTYPE_REQ && hdr->addr.interf == position_id.interf
00478           && hdr->addr.index == position_id.index)
00479   {
00480     return HandlePositionConfig(resp_queue, hdr->subtype, data, hdr->size);
00481   }
00482 
00483   if (hdr->type == PLAYER_MSGTYPE_REQ && hdr->addr.interf == position3d_id.interf
00484           && hdr->addr.index == position3d_id.index)
00485   {
00486     return HandlePosition3DConfig(resp_queue, hdr->subtype, data, hdr->size);
00487   }
00488 
00489 
00490 
00491   return(-1);
00492 }
00493   
00494 // helper to handle config requests
00495 // returns 1 to indicate we wrote to the CAN bus
00496 // returns 0 to indicate we did NOT write to CAN bus
00497 int
00498 SegwayRMP::HandlePositionConfig(QueuePointer &client, uint32_t subtype, void* buffer, size_t len)
00499 {
00500   uint16_t rmp_cmd,rmp_val;
00501   //player_rmp_config_t *rmp;
00502   CanPacket pkt;
00503   
00504   switch(subtype) 
00505   {
00506     case PLAYER_POSITION2D_REQ_MOTOR_POWER:
00507       // just set a flag telling us whether we should
00508       // act on motor commands
00509       // set the commands to 0... think it will automatically
00510       // do this for us.  
00511       if(((char *) buffer)[0]) 
00512         this->motor_allow_enable = true;
00513       else
00514         this->motor_allow_enable = false;
00515 
00516       printf("SEGWAYRMP: motors state: %d\n", this->motor_allow_enable);
00517 
00518       Publish(position_id, client, PLAYER_MSGTYPE_RESP_ACK,subtype);
00519       return 0;
00520 
00521     case PLAYER_POSITION2D_REQ_GET_GEOM:
00522       player_position2d_geom_t geom;
00523       geom.pose.px = 0;
00524       geom.pose.py = 0;
00525       geom.pose.pa = 0;
00526       geom.size.sw = 0.508;
00527       geom.size.sl = 0.610;
00528 
00529       Publish(position_id, client, PLAYER_MSGTYPE_RESP_ACK, PLAYER_POSITION2D_REQ_GET_GEOM, &geom, sizeof(geom),NULL);
00530       return 0;
00531 
00532     case PLAYER_POSITION2D_REQ_RESET_ODOM:
00533       // we'll reset all the integrators
00534 
00535       MakeStatusCommand(&pkt, (uint16_t)RMP_CAN_CMD_RST_INT, 
00536                         (uint16_t)RMP_CAN_RST_ALL);
00537       if(Write(pkt) < 0)
00538       {
00539         Publish(position_id, client, PLAYER_MSGTYPE_RESP_NACK,PLAYER_POSITION2D_REQ_RESET_ODOM);
00540       }
00541       else
00542       {
00543 
00544         if (Write(pkt) < 0) {
00545           Publish(position_id, client, PLAYER_MSGTYPE_RESP_NACK,PLAYER_POSITION2D_REQ_RESET_ODOM);
00546         } else {
00547           Publish(position_id, client, PLAYER_MSGTYPE_RESP_ACK,PLAYER_POSITION2D_REQ_RESET_ODOM);
00548         }
00549       }
00550 
00551       odom_x = odom_y = odom_yaw = 0.0;
00552       firstread = true;
00553 
00554       // return 1 to indicate that we wrote to the CAN bus this time
00555       return(0);
00556 
00557 /*    case PLAYER_POSITION_RMP_VELOCITY_SCALE:
00558       rmp_cmd = RMP_CAN_CMD_MAX_VEL;
00559       rmp = (player_rmp_config_t *)buffer;
00560       rmp_val = ntohs(rmp->value);
00561 
00562       printf("SEGWAYRMP: velocity scale %d\n", rmp_val);
00563 
00564       MakeStatusCommand(&pkt, rmp_cmd, rmp_val);
00565 
00566       if(Write(pkt) < 0)
00567       {
00568         if(PutReply(client, PLAYER_MSGTYPE_RESP_NACK,NULL))
00569           PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00570       }
00571       else
00572       {
00573         if(PutReply(client, PLAYER_MSGTYPE_RESP_ACK,NULL))
00574           PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00575       }
00576       // return 1 to indicate that we wrote to the CAN bus this time
00577       return(1);
00578 
00579     case PLAYER_POSITION_RMP_ACCEL_SCALE:
00580       rmp_cmd = RMP_CAN_CMD_MAX_ACCL;
00581       rmp = (player_rmp_config_t *)buffer;
00582       rmp_val = ntohs(rmp->value);
00583 
00584       MakeStatusCommand(&pkt, rmp_cmd, rmp_val);
00585 
00586       if(Write(pkt) < 0)
00587       {
00588         if(PutReply(client, PLAYER_MSGTYPE_RESP_NACK,NULL))
00589           PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00590       }
00591       else
00592       {
00593         if(PutReply(client, PLAYER_MSGTYPE_RESP_ACK,NULL))
00594           PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00595       }
00596       // return 1 to indicate that we wrote to the CAN bus this time
00597       return(1);
00598 
00599     case PLAYER_POSITION_RMP_TURN_SCALE:
00600       rmp_cmd = RMP_CAN_CMD_MAX_TURN;
00601       rmp = (player_rmp_config_t *)buffer;
00602       rmp_val = ntohs(rmp->value);
00603 
00604       MakeStatusCommand(&pkt, rmp_cmd, rmp_val);
00605 
00606       if(Write(pkt) < 0)
00607       {
00608         if(PutReply(client, PLAYER_MSGTYPE_RESP_NACK,NULL))
00609           PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00610       }
00611       else
00612       {
00613         if(PutReply(client, PLAYER_MSGTYPE_RESP_ACK,NULL))
00614           PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00615       }
00616       // return 1 to indicate that we wrote to the CAN bus this time
00617       return(1);
00618 
00619     case PLAYER_POSITION_RMP_GAIN_SCHEDULE:
00620       rmp_cmd = RMP_CAN_CMD_GAIN_SCHED;
00621       rmp = (player_rmp_config_t *)buffer;
00622       rmp_val = ntohs(rmp->value);
00623 
00624       MakeStatusCommand(&pkt, rmp_cmd, rmp_val);
00625 
00626       if(Write(pkt) < 0)
00627       {
00628         if(PutReply(client, PLAYER_MSGTYPE_RESP_NACK,NULL))
00629           PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00630       }
00631       else
00632       {
00633         if(PutReply(client, PLAYER_MSGTYPE_RESP_ACK,NULL))
00634           PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00635       }
00636       // return 1 to indicate that we wrote to the CAN bus this time
00637       return(1);
00638 
00639     case PLAYER_POSITION_RMP_CURRENT_LIMIT:
00640       rmp_cmd = RMP_CAN_CMD_CURR_LIMIT;
00641       rmp = (player_rmp_config_t *)buffer;
00642       rmp_val = ntohs(rmp->value);
00643 
00644       MakeStatusCommand(&pkt, rmp_cmd, rmp_val);
00645 
00646       if(Write(pkt) < 0)
00647       {
00648         if(PutReply(client, PLAYER_MSGTYPE_RESP_NACK,NULL))
00649           PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00650       }
00651       else
00652       {
00653         if(PutReply(client, PLAYER_MSGTYPE_RESP_ACK,NULL))
00654           PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00655       }
00656       // return 1 to indicate that we wrote to the CAN bus this time
00657       return(1);
00658 
00659     case PLAYER_POSITION_RMP_RST_INTEGRATORS:
00660       rmp_cmd = RMP_CAN_CMD_RST_INT;
00661       rmp = (player_rmp_config_t *)buffer;
00662       rmp_val = ntohs(rmp->value);
00663 
00664       MakeStatusCommand(&pkt, rmp_cmd, rmp_val);
00665 
00666       if(Write(pkt) < 0)
00667       {
00668         if(PutReply(client, PLAYER_MSGTYPE_RESP_NACK,NULL))
00669           PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00670       }
00671       else
00672       {
00673         if (Write(pkt) < 0) {
00674           if(PutReply(client, PLAYER_MSGTYPE_RESP_NACK,NULL))
00675             PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00676         } else {
00677           if(PutReply(client, PLAYER_MSGTYPE_RESP_ACK,NULL))
00678             PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00679         }
00680       }
00681       // return 1 to indicate that we wrote to the CAN bus this time
00682       return(1);
00683 
00684     case PLAYER_POSITION_RMP_SHUTDOWN:
00685       MakeShutdownCommand(&pkt);
00686 
00687       if(Write(pkt) < 0)
00688       {
00689         if(PutReply(client, PLAYER_MSGTYPE_RESP_NACK,NULL))
00690           PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00691       }
00692       else
00693       {
00694         if(PutReply(client, PLAYER_MSGTYPE_RESP_ACK,NULL))
00695           PLAYER_ERROR("SEGWAY: Failed to PutReply\n");
00696       }
00697       // return 1 to indicate that we wrote to the CAN bus this time
00698       return(1);
00699 */
00700     default:
00701       printf("segwayrmp received unknown config request %d\n", 
00702              subtype);
00703 /*      if(PutReply(client, PLAYER_MSGTYPE_RESP_NACK,NULL))
00704         PLAYER_ERROR("Failed to PutReply in segwayrmp\n");*/
00705       break;
00706   }
00707 
00708   // return -1, to indicate that we did NOT handle the message
00709   return(-1);
00710 }
00711 
00712 // helper to handle config requests
00713 // returns 1 to indicate we wrote to the CAN bus
00714 // returns 0 to indicate we did NOT write to CAN bus
00715 int
00716 SegwayRMP::HandlePosition3DConfig(QueuePointer &client, uint32_t subtype, void* buffer, size_t len)
00717 {
00718   switch(subtype) 
00719   {
00720     case PLAYER_POSITION3D_REQ_MOTOR_POWER:
00721       // just set a flag telling us whether we should
00722       // act on motor commands
00723       // set the commands to 0... think it will automatically
00724       // do this for us.  
00725       if(((char*)buffer)[0]) 
00726         this->motor_allow_enable = true;
00727       else
00728         this->motor_allow_enable = false;
00729 
00730       printf("SEGWAYRMP: motors state: %d\n", this->motor_allow_enable);
00731 
00732       Publish(this->position3d_id, client, PLAYER_MSGTYPE_RESP_ACK, PLAYER_POSITION3D_REQ_MOTOR_POWER);
00733       return 0;
00734 
00735     default:
00736       printf("segwayrmp received unknown config request %d\n", 
00737              subtype);
00738   }
00739 
00740   // return -1, to indicate that we did process the message
00741   return(-1);
00742 }
00743 
00744 
00745 int
00746 SegwayRMP::Read()
00747 {
00748   CanPacket pkt;
00749   int channel;
00750   int ret;
00751   rmp_frame_t data_frame[2];
00752   
00753   //static struct timeval last;
00754   //struct timeval curr;
00755 
00756   data_frame[0].ready = 0;
00757   data_frame[1].ready = 0;
00758 
00759   // read one cycle of data from each channel
00760   for(channel = 0; channel < DUALCAN_NR_CHANNELS; channel++) 
00761   {
00762     ret=0;
00763     // read until we've gotten all 5 packets for this cycle on this channel
00764     while((ret = canio->ReadPacket(&pkt, channel)) >= 0)
00765     {
00766       // then we got a new packet...
00767 
00768       //printf("SEGWAYIO: pkt: %s\n", pkt.toString());
00769 
00770       data_frame[channel].AddPacket(pkt);
00771 
00772       // if data has been filled in, then let's make it the latest data 
00773       // available to player...
00774       if(data_frame[channel].IsReady()) 
00775       {
00776         // Only bother doing the data conversions for one channel.
00777         // It appears that the data on channel 0 is garbarge, so we'll read
00778         // from channel 1.
00779         if(channel == 1)
00780         {
00781           UpdateData(&data_frame[channel]);
00782         }
00783 
00784         data_frame[channel].ready = 0;
00785         break;
00786       }
00787     }
00788 
00789     if (ret < 0) 
00790     {
00791       PLAYER_ERROR2("error (%d) reading packet on channel %d",
00792                     ret, channel);
00793     }
00794   }
00795 
00796   return(0);
00797 }
00798 
00799 void
00800 SegwayRMP::UpdateData(rmp_frame_t * data_frame)
00801 {
00802   int delta_lin_raw, delta_ang_raw;
00803   double delta_lin, delta_ang;
00804   double tmp;
00805 
00806   // Get the new linear and angular encoder values and compute
00807   // odometry.  Note that we do the same thing here, regardless of 
00808   // whether we're presenting 2D or 3D position info.
00809   delta_lin_raw = Diff(this->last_raw_foreaft,
00810                        data_frame->foreaft,
00811                        this->firstread);
00812   this->last_raw_foreaft = data_frame->foreaft;
00813   
00814   delta_ang_raw = Diff(this->last_raw_yaw,
00815                        data_frame->yaw,
00816                        this->firstread);
00817   this->last_raw_yaw = data_frame->yaw;
00818   
00819   delta_lin = (double)delta_lin_raw / (double)RMP_COUNT_PER_M;
00820   delta_ang = DTOR((double)delta_ang_raw / 
00821                    (double)RMP_COUNT_PER_REV * 360.0);
00822   
00823   // First-order odometry integration
00824   this->odom_x += delta_lin * cos(this->odom_yaw);
00825   this->odom_y += delta_lin * sin(this->odom_yaw);
00826   this->odom_yaw += delta_ang;
00827   
00828   // Normalize yaw in [0, 360]
00829   this->odom_yaw = atan2(sin(this->odom_yaw), cos(this->odom_yaw));
00830   if (this->odom_yaw < 0)
00831     this->odom_yaw += 2 * M_PI;
00832   
00833   // first, do 2D info.
00834   this->position_data.pos.px = this->odom_x;
00835   this->position_data.pos.py = this->odom_y;
00836   this->position_data.pos.pa = this->odom_yaw;
00837   
00838   // combine left and right wheel velocity to get foreward velocity
00839   // change from counts/s into mm/s
00840   this->position_data.vel.px = ((double)data_frame->left_dot +
00841                           (double)data_frame->right_dot) /
00842                          (double)RMP_COUNT_PER_M_PER_S 
00843                          / 2.0;
00844   
00845   // no side speeds for this bot
00846   this->position_data.vel.py = 0;
00847   
00848   // from counts/sec into deg/sec.  also, take the additive
00849   // inverse, since the RMP reports clockwise angular velocity as
00850   // positive.
00851   this->position_data.vel.pa = 
00852           DTOR(-(double)data_frame->yaw_dot / (double)RMP_COUNT_PER_DEG_PER_S);
00853   
00854   this->position_data.stall = 0;
00855   
00856   // now, do 3D info.
00857   this->position3d_data.pos.px = this->odom_x;
00858   this->position3d_data.pos.py = this->odom_y;
00859   // this robot doesn't fly
00860   this->position3d_data.pos.pz = 0;
00861 
00863   
00864   // normalize angles to [0,360]
00865   tmp = NORMALIZE(DTOR((double)data_frame->roll /
00866                        (double)RMP_COUNT_PER_DEG));
00867   if(tmp < 0)
00868     tmp += 2*M_PI;
00869   this->position3d_data.pos.proll = tmp;//htonl((int32_t)rint(tmp * 1000.0));
00870   
00871   // normalize angles to [0,360]
00872   tmp = NORMALIZE(DTOR((double)data_frame->pitch /
00873                        (double)RMP_COUNT_PER_DEG));
00874   if(tmp < 0)
00875     tmp += 2*M_PI;
00876   this->position3d_data.pos.ppitch = tmp;//htonl((int32_t)rint(tmp * 1000.0));
00877   
00878   this->position3d_data.pos.pyaw = tmp;//htonl(((int32_t)(this->odom_yaw * 1000.0)));
00879   
00880   // combine left and right wheel velocity to get foreward velocity
00881   // change from counts/s into m/s
00882   this->position3d_data.vel.px = 
00883     ((double)data_frame->left_dot +
00884                           (double)data_frame->right_dot) /
00885                          (double)RMP_COUNT_PER_M_PER_S 
00886                           / 2.0;
00887   // no side or vertical speeds for this bot
00888   this->position3d_data.vel.py = 0;
00889   this->position3d_data.vel.pz = 0;
00890   
00891   this->position3d_data.vel.proll = 
00892     (double)data_frame->roll_dot /
00893                         (double)RMP_COUNT_PER_DEG_PER_S * M_PI / 180;
00894   this->position3d_data.vel.ppitch = 
00895     (double)data_frame->pitch_dot /
00896                         (double)RMP_COUNT_PER_DEG_PER_S * M_PI / 180;
00897   // from counts/sec into millirad/sec.  also, take the additive
00898   // inverse, since the RMP reports clockwise angular velocity as
00899   // positive.
00900 
00901   // This one uses left_dot and right_dot, which comes from odometry
00902   this->position3d_data.vel.pyaw = 
00903     (double)(data_frame->right_dot - data_frame->left_dot) /
00904                          (RMP_COUNT_PER_M_PER_S * RMP_GEOM_WHEEL_SEP * M_PI);
00905   // This one uses yaw_dot, which comes from the IMU
00906   //data.position3d_data.yawspeed = 
00907   //  htonl((int32_t)(-rint((double)data_frame->yaw_dot / 
00908   //                        (double)RMP_COUNT_PER_DEG_PER_S * M_PI / 180 * 1000)));
00909   
00910   this->position3d_data.stall = 0;
00911   
00912   // fill in power data.  the RMP returns a percentage of full,
00913   // and the specs for the HT say that it's a 72 volt system.  assuming
00914   // that the RMP is the same, we'll convert to decivolts for Player.
00915   this->power_data.volts = 
00916     data_frame->battery * 72;
00917   
00918   firstread = false;  
00919 }  
00920 
00921 
00922 int
00923 SegwayRMP::Write(CanPacket& pkt)
00924 {
00925   return(canio->WritePacket(pkt));
00926 }
00927 
00928 /* Creates a status CAN packet from the given arguments
00929  */  
00930 void
00931 SegwayRMP::MakeStatusCommand(CanPacket* pkt, uint16_t cmd, uint16_t val)
00932 {
00933   int16_t trans,rot;
00934 
00935   pkt->id = RMP_CAN_ID_COMMAND;
00936   pkt->PutSlot(2, cmd);
00937  
00938   // it was noted in the windows demo code that they
00939   // copied the 8-bit value into both bytes like this
00940   pkt->PutByte(6, val);
00941   pkt->PutByte(7, val);
00942 
00943   trans = (int16_t) rint((double)this->curr_xspeed * 
00944                          (double)RMP_COUNT_PER_MM_PER_S);
00945 
00946   if(trans > RMP_MAX_TRANS_VEL_COUNT)
00947     trans = RMP_MAX_TRANS_VEL_COUNT;
00948   else if(trans < -RMP_MAX_TRANS_VEL_COUNT)
00949     trans = -RMP_MAX_TRANS_VEL_COUNT;
00950 
00951   rot = (int16_t) rint((double)this->curr_yawspeed * 
00952                        (double)RMP_COUNT_PER_DEG_PER_SS);
00953 
00954   if(rot > RMP_MAX_ROT_VEL_COUNT)
00955     rot = RMP_MAX_ROT_VEL_COUNT;
00956   else if(rot < -RMP_MAX_ROT_VEL_COUNT)
00957     rot = -RMP_MAX_ROT_VEL_COUNT;
00958 
00959   // put in the last speed commands as well
00960   pkt->PutSlot(0,(uint16_t)trans);
00961   pkt->PutSlot(1,(uint16_t)rot);
00962   
00963   if(cmd) 
00964   {
00965     printf("SEGWAYIO: STATUS: cmd: %02x val: %02x pkt: %s\n", 
00966            cmd, val, pkt->toString());
00967   }
00968 }
00969 
00970 /* takes a player command (in host byte order) and turns it into CAN packets 
00971  * for the RMP 
00972  */
00973 void
00974 SegwayRMP::MakeVelocityCommand(CanPacket* pkt, 
00975                                int32_t xspeed, 
00976                                int32_t yawspeed)
00977 {
00978   pkt->id = RMP_CAN_ID_COMMAND;
00979   pkt->PutSlot(2, (uint16_t)RMP_CAN_CMD_NONE);
00980   
00981   // we only care about cmd.xspeed and cmd.yawspeed
00982   // translational velocity is given to RMP in counts 
00983   // [-1176,1176] ([-8mph,8mph])
00984 
00985   // player is mm/s
00986   // 8mph is 3576.32 mm/s
00987   // so then mm/s -> counts = (1176/3576.32) = 0.32882963
00988 
00989   if(xspeed > this->max_xspeed)
00990   {
00991     PLAYER_WARN2("xspeed thresholded! (%d > %d)", xspeed, this->max_xspeed);
00992     xspeed = this->max_xspeed;
00993   }
00994   else if(xspeed < -this->max_xspeed)
00995   {
00996     PLAYER_WARN2("xspeed thresholded! (%d < %d)", xspeed, -this->max_xspeed);
00997     xspeed = -this->max_xspeed;
00998   }
00999 
01000   this->curr_xspeed = xspeed;
01001 
01002   int16_t trans = (int16_t) rint((double)xspeed * 
01003                                  (double)RMP_COUNT_PER_MM_PER_S);
01004 
01005   if(trans > RMP_MAX_TRANS_VEL_COUNT)
01006     trans = RMP_MAX_TRANS_VEL_COUNT;
01007   else if(trans < -RMP_MAX_TRANS_VEL_COUNT)
01008     trans = -RMP_MAX_TRANS_VEL_COUNT;
01009 
01010   if(yawspeed > this->max_yawspeed)
01011   {
01012     PLAYER_WARN2("yawspeed thresholded! (%d > %d)", 
01013                  yawspeed, this->max_yawspeed);
01014     yawspeed = this->max_yawspeed;
01015   }
01016   else if(yawspeed < -this->max_yawspeed)
01017   {
01018     PLAYER_WARN2("yawspeed thresholded! (%d < %d)", 
01019                  yawspeed, -this->max_yawspeed);
01020     yawspeed = -this->max_yawspeed;
01021   }
01022   this->curr_yawspeed = yawspeed;
01023 
01024   // rotational RMP command \in [-1024, 1024]
01025   // this is ripped from rmi_demo... to go from deg/s to counts
01026   // deg/s -> count = 1/0.013805056
01027   int16_t rot = (int16_t) rint((double)yawspeed * 
01028                                (double)RMP_COUNT_PER_DEG_PER_S);
01029 
01030   if(rot > RMP_MAX_ROT_VEL_COUNT)
01031     rot = RMP_MAX_ROT_VEL_COUNT;
01032   else if(rot < -RMP_MAX_ROT_VEL_COUNT)
01033     rot = -RMP_MAX_ROT_VEL_COUNT;
01034 
01035   pkt->PutSlot(0, (uint16_t)trans);
01036   pkt->PutSlot(1, (uint16_t)rot);
01037 }
01038 
01039 void
01040 SegwayRMP::MakeShutdownCommand(CanPacket* pkt)
01041 {
01042   pkt->id = RMP_CAN_ID_SHUTDOWN;
01043 
01044   printf("SEGWAYIO: SHUTDOWN: pkt: %s\n",
01045          pkt->toString());
01046 }
01047 
01048 // Calculate the difference between two raw counter values, taking care
01049 // of rollover.
01050 int
01051 SegwayRMP::Diff(uint32_t from, uint32_t to, bool first)
01052 {
01053   int diff1, diff2;
01054   static uint32_t max = (uint32_t)pow(2,32)-1;
01055 
01056   // if this is the first time, report no change
01057   if(first)
01058     return(0);
01059 
01060   diff1 = to - from;
01061 
01062   /* find difference in two directions and pick shortest */
01063   if(to > from)
01064     diff2 = -(from + max - to);
01065   else 
01066     diff2 = max - from + to;
01067 
01068   if(abs(diff1) < abs(diff2)) 
01069     return(diff1);
01070   else
01071     return(diff2);
01072 }

Last updated 12 September 2005 21:38:45