clodbuster.cc

00001 /*
00002  *  Player - One Hell of a Robot Server
00003  *  Copyright (C) 2000  
00004  *     Brian Gerkey, Kasper Stoy, Richard Vaughan, & Andrew Howard
00005  *                      
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  *
00021  */
00022 
00023 /*
00024  * $Id: clodbuster.cc 4135 2007-08-23 19:58:48Z gerkey $
00025  */
00026 
00078 #if HAVE_CONFIG_H
00079 #include <config.h>
00080 #endif
00081 
00082 #include <fcntl.h> // POSIX file i/o
00083 #include <signal.h>
00084 #include <sys/stat.h>
00085 #include <sys/types.h>
00086 #include <stdio.h>
00087 #include <string.h>
00088 #include <unistd.h> 
00089 #include <math.h>
00090 #include <stdlib.h>  /* for abs() */
00091 #include <netinet/in.h> // socket things...
00092 #include <termios.h> // serial port things
00093 
00094 #include <libplayercore/playercore.h>
00095 #include <replace/replace.h>
00096 #include "clodbuster.h"
00097 #include "packet.h" // What's this for?
00098 
00099 // initialization function
00100 Driver* ClodBuster_Init( ConfigFile* cf, int section)
00101 {
00102   return((Driver*)(new ClodBuster( cf, section)));
00103 }
00104 
00105 // a driver registration function
00106 void 
00107 ClodBuster_Register(DriverTable* table)
00108 {
00109   table->AddDriver("clodbuster",  ClodBuster_Init);
00110 }
00111 
00112 
00113 ClodBuster::ClodBuster( ConfigFile* cf, int section)
00114         : Driver(cf, section, true, PLAYER_MSGQUEUE_DEFAULT_MAXLEN, PLAYER_POSITION2D_CODE)
00115 {
00116   clodbuster_fd = -1;
00117 
00118   speedDemand=0, turnRateDemand=0;
00119 
00120   
00121   strncpy(clodbuster_serial_port,
00122           cf->ReadString(section, "port", DEFAULT_CLODBUSTER_PORT),
00123           sizeof(clodbuster_serial_port));
00124 
00125   // set parameters
00126   CountsPerRev = 408;
00127   WheelRadius = 0.076;
00128   WheelBase = .2921;
00129   WheelSeparation = .275;
00130   Kenc = 2*M_PI*WheelRadius/CountsPerRev;
00131   LoopFreq = 5;
00132 
00133   // set PID gains
00134   //  Kv = new PIDGains(-1.0,-1.5,0.0,LoopFreq);
00135   //  Kw = new PIDGains(-0.5112,-1.5,0.0,LoopFreq);
00136   Kv = new PIDGains(-10,-20.0,0.0,LoopFreq);
00137   Kw = new PIDGains(-5,-20.0,0.0,LoopFreq);
00138 }
00139 
00140 ClodBuster::~ClodBuster()
00141 {
00142   delete Kv;
00143   delete Kw;
00144 }
00145 
00146 int ClodBuster::Setup()
00147 {
00148   //  int i;
00149   // this is the order in which we'll try the possible baud rates. we try 9600
00150   // first because most robots use it, and because otherwise the radio modem
00151   // connection code might not work (i think that the radio modems operate at
00152   // 9600).
00153   //int baud = B38400;
00154   //  int numbauds = sizeof(bauds);
00155   // int currbaud = 0;
00156 
00157   struct termios term;
00158   //unsigned char command;
00159   // GRASPPacket packet, receivedpacket;
00160   int flags;
00161   //bool sent_close = false;
00162   
00163   printf("clodbuster connection initializing (%s)...",clodbuster_serial_port);
00164   fflush(stdout);
00165 
00166   if((clodbuster_fd = open(clodbuster_serial_port, 
00167                            O_RDWR | O_SYNC, S_IRUSR | S_IWUSR )) < 0 ) // O_NONBLOCK later..
00168     {
00169       perror("ClodBuster::Setup():open():");
00170       return(1);
00171     }  
00172  
00173   if( tcgetattr( clodbuster_fd, &term ) < 0 )
00174     {
00175       perror("ClodBuster::Setup():tcgetattr():");
00176       close(clodbuster_fd);
00177       clodbuster_fd = -1;
00178       return(1);
00179     }
00180   
00181   cfmakeraw( &term );
00182   term.c_cc[VTIME] = 10; /* wait 1 second on port A */
00183   term.c_cc[VMIN] = 0;  
00184 
00185   cfsetispeed(&term, B38400);
00186   cfsetospeed(&term, B38400);
00187   
00188   if( tcsetattr( clodbuster_fd, TCSAFLUSH, &term ) < 0 )
00189     {
00190       perror("ClodBuster::Setup():tcsetattr():");
00191       close(clodbuster_fd);
00192       clodbuster_fd = -1;
00193       return(1);
00194     }
00195 
00196   if( tcflush( clodbuster_fd, TCIOFLUSH ) < 0 )
00197     {
00198       perror("ClodBuster::Setup():tcflush():");
00199       close(clodbuster_fd);
00200       clodbuster_fd = -1;
00201       return(1);
00202     }
00203 
00204   if((flags = fcntl(clodbuster_fd, F_GETFL)) < 0)
00205     {
00206       perror("ClodBuster::Setup():fcntl()");
00207       close(clodbuster_fd);
00208       clodbuster_fd = -1;
00209       return(1);
00210     }
00211   /* turn on blocking mode */
00212   flags &= ~O_NONBLOCK;
00213   fcntl(clodbuster_fd, F_SETFL, flags);
00214 
00215   
00216   usleep(CLODBUSTER_CYCLETIME_USEC);
00217   
00218   GRASPPacket packet; 
00219   // disable motor power
00220   packet.Build(SET_SLEEP_MODE,SLEEP_MODE_OFF);
00221   packet.Send(clodbuster_fd);
00222   // reset odometry
00223   ResetRawPositions();
00224  
00225   direct_command_control = true;
00226   
00227   /* now spawn reading thread */
00228   StartThread();
00229   return(0);
00230 }
00231 
00232 int ClodBuster::Shutdown()
00233 {
00234   GRASPPacket packet; 
00235 
00236   if(clodbuster_fd == -1)
00237     {
00238       return(0);
00239     }
00240 
00241   StopThread();
00242 
00243   packet.Build(SET_SLEEP_MODE,SLEEP_MODE_OFF);
00244   packet.Send(clodbuster_fd);
00245   usleep(CLODBUSTER_CYCLETIME_USEC);
00246 
00247   close(clodbuster_fd);
00248   clodbuster_fd = -1;
00249   puts("ClodBuster has been shutdown");
00250  
00251   return(0);
00252 }
00253 
00255 // Process an incoming message
00256 int ClodBuster::ProcessMessage (QueuePointer &resp_queue, player_msghdr * hdr, void * data)
00257 {
00258 
00259   assert(hdr);
00260   assert(data);
00261         
00262   if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_SET_ODOM, device_addr))
00263   {
00264     assert(hdr->size == sizeof(player_position2d_set_odom_req_t));
00265     player_position2d_set_odom_req_t & set_odom_req = *((player_position2d_set_odom_req_t*)data);
00266     
00267     this->position_data.pos = set_odom_req.pose;
00268 
00269     Publish(device_addr, resp_queue, PLAYER_MSGTYPE_RESP_ACK, PLAYER_POSITION2D_REQ_SET_ODOM);
00270     return 0;
00271   }
00272 
00273   if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_GET_GEOM, device_addr))
00274   {
00275     player_position2d_geom_t geom={{0}};
00276         
00277     // TODO : get values from somewhere.
00278     geom.pose.px = -0.1;//htons((short) (-100));
00279     geom.pose.py = 0;//htons((short) (0));
00280     geom.pose.pyaw = 0;//htons((short) (0));
00281     geom.size.sw = 0.5;//htons((short) (2 * 250));
00282     geom.size.sl = 0.45;//htons((short) (2 * 225));
00283 
00284     Publish(device_addr, resp_queue, PLAYER_MSGTYPE_RESP_ACK, PLAYER_POSITION2D_REQ_GET_GEOM, &geom, sizeof(geom));
00285     return 0;
00286   }
00287 
00288   if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_MOTOR_POWER, device_addr))
00289   {
00290     assert(hdr->size == sizeof(player_position2d_power_config_t));
00291     player_position2d_power_config_t & power_config = *((player_position2d_power_config_t*)data);
00292     GRASPPacket packet; 
00293                 
00294     if(power_config.state==1)
00295       packet.Build(SET_SLEEP_MODE,SLEEP_MODE_OFF);
00296     else 
00297        packet.Build(SET_SLEEP_MODE,SLEEP_MODE_ON);
00298 
00299     packet.Send(clodbuster_fd);
00300 
00301     Publish(device_addr, resp_queue, PLAYER_MSGTYPE_RESP_ACK, PLAYER_POSITION2D_REQ_MOTOR_POWER);
00302     return 0;
00303   }
00304 
00305   /* velocity control mode:
00306    *   0 = direct wheel velocity control (default)
00307    *   1 = separate translational and rotational control
00308    */
00309   if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_VELOCITY_MODE, device_addr))
00310   {
00311     assert(hdr->size == sizeof(player_position2d_velocity_mode_config_t));
00312     player_position2d_velocity_mode_config_t & velmode_config = *((player_position2d_velocity_mode_config_t*)data);
00313                 
00314     if(velmode_config.value)
00315       direct_command_control = false;
00316     else
00317       direct_command_control = true;
00318 
00319     Publish(device_addr, resp_queue, PLAYER_MSGTYPE_RESP_ACK, PLAYER_POSITION2D_REQ_VELOCITY_MODE);
00320     return 0;
00321   }
00322 
00323   if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_RESET_ODOM, device_addr))
00324   {
00325     ResetRawPositions();
00326 
00327     Publish(device_addr, resp_queue, PLAYER_MSGTYPE_RESP_ACK, PLAYER_POSITION2D_REQ_RESET_ODOM);
00328     return 0;
00329   }
00330 
00331   if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, PLAYER_POSITION2D_REQ_SPEED_PID, device_addr))
00332   {
00333     assert(hdr->size == sizeof(player_position2d_speed_pid_req_t));
00334         player_position2d_speed_pid_req_t & pid = *((player_position2d_speed_pid_req_t*)data);
00335                 
00336     kp = static_cast<int> (pid.kp);
00337     ki = static_cast<int> (pid.ki);
00338     kd = static_cast<int> (pid.kd);
00339 
00340     Publish(device_addr, resp_queue, PLAYER_MSGTYPE_RESP_ACK, PLAYER_POSITION2D_REQ_SPEED_PID);
00341     return 0;
00342   }
00343 
00344   if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_CMD, PLAYER_POSITION2D_CMD_VEL, device_addr))
00345   {
00346     assert(hdr->size == sizeof(player_position2d_cmd_vel_t));
00347         player_position2d_cmd_vel_t & command = *((player_position2d_cmd_vel_t*)data);
00348                 
00349     newmotorspeed = false;
00350     if( speedDemand != (int) command.vel.px)
00351       newmotorspeed = true;
00352     speedDemand = (int) command.vel.px;
00353       
00354     newmotorturn = false;
00355     if(turnRateDemand != (int) command.vel.pa)
00356       newmotorturn = true;
00357     turnRateDemand = (int) command.vel.pa;      
00358 
00359     return 0;
00360   }     
00361   
00362   return -1;
00363 }
00364 
00365 void 
00366 ClodBuster::Main()
00367 {
00368 //  player_position_speed_pid_req_t pid;
00369 //  unsigned char config[CLODBUSTER_CONFIG_BUFFER_SIZE];
00370   
00371   
00372 //  int config_size;
00373 
00374   GetGraspBoardParams();
00375 
00376   // memory for PID
00377   int uVmax=max_limits[SET_SERVO_THROTTLE];//-center_limits[SET_SERVO_THROTTLE];
00378   int uVmin=min_limits[SET_SERVO_THROTTLE];//-center_limits[SET_SERVO_THROTTLE];
00379   int uV0=center_limits[SET_SERVO_THROTTLE];
00380   int uWmax=max_limits[SET_SERVO_FRONTSTEER];//-center_limits[SET_SERVO_FRONTSTEER];
00381   int uWmin=min_limits[SET_SERVO_FRONTSTEER];//-center_limits[SET_SERVO_FRONTSTEER];
00382   int uW0=center_limits[SET_SERVO_FRONTSTEER];
00383   float uV = uV0, uW=uW0, errV[3]={0}, errW[3]={0}, uVlast=uV0, uWlast=uW0,V;
00384 
00385   printf("V max min centre %d %d %d\n",uVmax,uVmin,uV0);
00386   printf("W max min centre %d %d %d\n",uWmax,uWmin,uW0);
00387 
00388   for(;;)
00389     {
00390       ProcessMessages();
00391       pthread_testcancel();
00392       
00393       // read encoders & update pose and velocity
00394       encoder_measurement = ReadEncoders();
00395       
00396       DifferenceEncoders();
00397       IntegrateEncoders();
00398       
00399       // remember old values
00400       old_encoder_measurement = encoder_measurement;
00401       
00402       Publish(device_addr,PLAYER_MSGTYPE_DATA,PLAYER_POSITION2D_DATA_STATE,(void*)&this->position_data,
00403                     sizeof(player_position2d_data_t),NULL);
00404       //      printf("left: %d , right %d count: %u\n",encoder_measurement.left,encoder_measurement.right,encoder_measurement.time_count);
00405       //      printf("left: %d , right %d\n",encoder_measurement.left-encoder_offset.left,encoder_measurement.right-encoder_offset.right);
00406 
00407 
00408       // do control
00409       /* NEXT, write commands */
00410       if(!direct_command_control) // fix this it's reversed for playerv
00411         {
00412           // do direct wheel velocity control here
00413           //printf("speedDemand: %d\t turnRateDemand: %d\n",
00414           //speedDemand, turnRateDemand);
00415           unsigned char v,w;
00416           v = SetServo(SET_SERVO_THROTTLE,speedDemand);
00417           w = SetServo(SET_SERVO_FRONTSTEER,turnRateDemand);
00418           printf("The vel/turn command numbers : v:%d (%u) w:%d (%u)\n",center_limits[SET_SERVO_THROTTLE]+speedDemand,v,center_limits[SET_SERVO_FRONTSTEER]+turnRateDemand,w);
00419     
00420           
00421         }
00422       else
00423         {
00424              //   printf("Do PID !\n");
00425              // find tracking errors
00426              errV[0] = EncV - speedDemand*1e-3;
00427              errW[0] = EncOmega - turnRateDemand*M_PI/180.0;
00428              
00429              // find actions
00430              uV = uVlast + Kv->K1()*errV[0] + + Kv->K2()*errV[1] + Kv->K3()*errV[2];
00431              if (uV > uVmax)
00432                   {
00433                        uV = uVmax;
00434                        printf("+V control saturated!\n");
00435                   }
00436              if (uV < uVmin)
00437                   {
00438                        uV = uVmin;
00439                        printf("-V control saturated!\n");
00440                   }
00441              printf("V loop err: %f, u = %f, r = %f, x = %f\n",errV[0], uV,speedDemand*1e-3,EncV);
00442              //      if (speedDemand*1e-3 > .0125) 
00443              if (fabs(EncV) > .0125) 
00444                   {
00445                     if (fabs(EncV) <.1)
00446                       if (EncV <.1)
00447                         V = -.1;
00448                       else
00449                         V = .1;
00450                     else
00451                       V = EncV;
00452                     // NB "-" sign for wrong convention +ve -> left 
00453                     uW = uWlast - WheelBase/V*(Kw->K1()*errW[0] + + Kw->K2()*errW[1] + Kw->K3()*errW[2]);
00454                     //   uW = uWlast - 1e3*WheelBase/speedDemand*(Kw->K1()*errW[0] + + Kw->K2()*errW[1] + Kw->K3()*errW[2]);
00455                   }
00456              else
00457                   {
00458                        // set it to zero (center)
00459                        uW = uW0;
00460                        uWlast = uW0;
00461                        for (int i=0;i<3;i++)
00462                             errW[i] = 0.;
00463                   } 
00464              if (uW > uWmax)
00465                   {
00466                        uW = uWmax;
00467                        printf("+W control saturated!\n");
00468                   }
00469              if (uW < uWmin)
00470                   {
00471                        uW = uWmin;
00472                        printf("-W control saturated!\n");
00473                   }
00474              printf("W loop err: %f, u = %f, r = %f, x = %f\n",errW[0], uW,turnRateDemand*M_PI/180.0,EncOmega);
00475              
00476              // Write actions to control board
00477              SetServo(SET_SERVO_THROTTLE,(unsigned char) uV);
00478              SetServo(SET_SERVO_FRONTSTEER,(unsigned char) uW);
00479              
00480              for (int i=1;i<3;i++)
00481                {
00482                  errW[i] = errW[i-1];
00483                  errV[i] = errV[i-1];
00484                }
00485              uVlast = uV;
00486              uWlast = uW;
00487 
00488         }
00489       usleep(200000);
00490     }
00491   pthread_exit(NULL);
00492 }
00493 
00494 void
00495 ClodBuster::ResetRawPositions()
00496 {
00497   encoder_offset=ReadEncoders();
00498 }
00499 
00500 clodbuster_encoder_data_t  ClodBuster::ReadEncoders()
00501 { GRASPPacket packet,rpacket;
00502  clodbuster_encoder_data_t temp;
00503 
00504  packet.Build(ECHO_ENCODER_COUNTS_TS);
00505  packet.Send(clodbuster_fd);
00506 
00507  rpacket.Receive(clodbuster_fd,ECHO_ENCODER_COUNTS_TS);
00508  rpacket.size=rpacket.retsize;
00509  rpacket.PrintHex();
00510 
00511  // make 24bit signed int a 32bit signed int
00512  temp.left = (rpacket.packet[0]<<16)|(rpacket.packet[1]<<8)|(rpacket.packet[2]);
00513  if (rpacket.packet[0] & 0x80)
00514    temp.left |= 0xff<<24;
00515  temp.right = (rpacket.packet[3]<<16)|(rpacket.packet[4]<<8)|(rpacket.packet[5]);
00516  if (rpacket.packet[3] & 0x80)
00517    temp.right |= 0xff<<24;
00518  // temp.left = (((char) rpacket.packet[0])<<16)|(rpacket.packet[1]<<8)|(rpacket.packet[2]);
00519  // temp.right = (((char) rpacket.packet[3])<<16)|(rpacket.packet[4]<<8)|(rpacket.packet[5]);
00520 
00521  // get timer count 32bit unsigned int
00522  temp.time_count = (rpacket.packet[6] << 24)|(rpacket.packet[7] <<16)|(rpacket.packet[8]<<8)|(rpacket.packet[9]);
00523 
00524  return temp;
00525 
00526 }
00527 
00528 void ClodBuster::GetGraspBoardParams()
00529 {
00530   GRASPPacket packet,rpacket;
00531   packet.Build(ECHO_MAX_SERVO_LIMITS);
00532   packet.Send(clodbuster_fd);
00533   printf("Servo Limit Enquiry: ");
00534 
00535   packet.PrintHex();
00536 
00537   rpacket.Receive(clodbuster_fd,ECHO_MAX_SERVO_LIMITS);
00538   
00539   memcpy(max_limits,rpacket.packet,8);
00540   rpacket.size = rpacket.retsize;
00541   
00542   rpacket.PrintHex();
00543 
00544   packet.Build(ECHO_MIN_SERVO_LIMITS);
00545   packet.Send(clodbuster_fd);
00546    printf("Servo Limit Enquiry: ");
00547 
00548   packet.PrintHex();
00549  
00550   rpacket.Receive(clodbuster_fd,ECHO_MIN_SERVO_LIMITS);
00551   rpacket.size = rpacket.retsize;
00552   
00553   rpacket.PrintHex();
00554   memcpy(min_limits,rpacket.packet,8);
00555   packet.Build(ECHO_CEN_SERVO_LIMITS);
00556   packet.Send(clodbuster_fd);
00557   printf("Servo Limit Enquiry: ");
00558 
00559   packet.PrintHex();
00560 
00561   rpacket.Receive(clodbuster_fd,ECHO_CEN_SERVO_LIMITS);
00562   
00563   memcpy(center_limits,rpacket.packet,8);
00564   rpacket.size = rpacket.retsize;
00565   
00566   rpacket.PrintHex();
00567 }
00568 
00569 unsigned char 
00570 ClodBuster::SetServo(unsigned char chan, int value)
00571 {
00572   GRASPPacket spacket;
00573   unsigned char cmd;
00574   int demanded = center_limits[chan]+value/10;
00575 
00576   if (demanded > max_limits[chan])
00577     cmd = (unsigned char) max_limits[chan];
00578   else if (demanded < min_limits[chan])
00579     cmd = (unsigned char) min_limits[chan];
00580   else
00581     cmd = (unsigned char)demanded;
00582   spacket.Build(chan,cmd);
00583   spacket.Send(clodbuster_fd);
00584   // spacket.PrintHex();
00585 
00586   return(cmd);
00587 }
00588 void 
00589 ClodBuster::SetServo(unsigned char chan, unsigned char cmd)
00590 {
00591   GRASPPacket spacket;
00592   spacket.Build(chan,cmd);
00593   spacket.Send(clodbuster_fd);
00594   // spacket.PrintHex();
00595 }
00596 
00597 void ClodBuster::IntegrateEncoders()
00598 {
00599   // assign something to xpos,ypos, theta
00600   float dEr = encoder_measurement.right-old_encoder_measurement.right; 
00601   float dEl = encoder_measurement.left-old_encoder_measurement.left; 
00602   float L = Kenc*(dEr+dEl)*.5;
00603   float D = Kenc*(dEr-dEl)/WheelSeparation;
00604   float Phi = this->position_data.pos.pa+0.5*D;
00605 
00606   this->position_data.pos.px += L*cos(Phi);
00607   this->position_data.pos.py += L*sin(Phi);
00608   this->position_data.pos.pa += D;
00609 }
00610 
00611 void ClodBuster::DifferenceEncoders()
00612 {
00613   float dEr = encoder_measurement.right-old_encoder_measurement.right; 
00614   float dEl = encoder_measurement.left-old_encoder_measurement.left; 
00615   int64_t dtc =  encoder_measurement.time_count-old_encoder_measurement.time_count;
00616   if (dtc< -2147483648LL) 
00617        {
00618             dtc += 4294967296LL; //(1<<32);
00619             printf("encoder timer rollover caught\n");
00620        }
00621   float dt = dtc*1.6e-6;
00622   if (dt < 20e-3)
00623        printf("dt way too short %f s\n",dt);
00624   else if  (dt > 2.0/LoopFreq)
00625        printf("dt way too long %f s\n",dt);
00626        
00627   EncV = Kenc*(dEr+dEl)*.5/dt;
00628   EncOmega = Kenc*(dEr-dEl)/WheelSeparation/dt;     
00629   EncVleft = dEl/dt;
00630   EncVright = dEr/dt;
00631   printf("EncV = %f, EncW = %f, dt = %f\n",EncV,EncOmega*180/M_PI,dt);
00632 
00633 }

Last updated 12 September 2005 21:38:45