MOOS 0.2375
|
00001 00002 // 00003 // MOOS - Mission Oriented Operating Suite 00004 // 00005 // A suit of Applications and Libraries for Mobile Robotics Research 00006 // Copyright (C) 2001-2005 Massachusetts Institute of Technology and 00007 // Oxford University. 00008 // 00009 // This software was written by Paul Newman and others 00010 // at MIT 2001-2002 and Oxford University 2003-2005. 00011 // email: pnewman@robots.ox.ac.uk. 00012 // 00013 // This file is part of a MOOS Instrument. 00014 // 00015 // This program is free software; you can redistribute it and/or 00016 // modify it under the terms of the GNU General Public License as 00017 // published by the Free Software Foundation; either version 2 of the 00018 // License, or (at your option) any later version. 00019 // 00020 // This program is distributed in the hope that it will be useful, 00021 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00023 // General Public License for more details. 00024 // 00025 // You should have received a copy of the GNU General Public License 00026 // along with this program; if not, write to the Free Software 00027 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 00028 // 02111-1307, USA. 00029 // 00031 // MOOSJRKerrDriver.cpp: implementation of the CMOOSJRKerrDriver class. 00032 // 00033 // Driver for three JR Kerr / Kerr Automation Engineering 00034 // PIC-SERVO-3PH motor controllers in a tailcone for an Odyssey II class vehicle 00035 // 00036 // This code does some slightly non-portable things like assume ints 00037 // are always 32 bits and chars always 8 bits, but we'll live with it 00038 // for now. 00039 // 00040 // 2/2002 - jmorash@alum.mit.edu 00041 // 00043 #include <MOOSLIB/MOOSLib.h> 00044 #include <iostream> 00045 #include <fstream> 00046 #include <math.h> 00047 #include "MOOSJRKerrDriver.h" 00048 00049 #define JRKERR_UNKNOWN -1; 00050 #define JRKERR_ADDR_INITIAL 0x00 00051 #define JRKERR_ADDR_GROUP 0xFF 00052 //these addresses reflect the wiring in the aft sphere 00053 #define JRKERR_ADDR_THRUSTER 0x01 00054 #define JRKERR_ADDR_RUDDER 0x02 00055 #define JRKERR_ADDR_ELEVATOR 0x03 00056 00057 //Command bytes are: upper nibble = number of following Data bytes, lower nibble = command number 00058 #define JRKERR_RESET_POSITION 0x00 00059 #define JRKERR_SET_ADDRESS 0x21 00060 #define JRKERR_DEFINE_STATUS 0x12 00061 #define JRKERR_READ_STATUS 0x13 00062 #define JRKERR_LOAD_TRAJECTORY_POS 0xD4 00063 #define JRKERR_LOAD_TRAJECTORY_PWM 0x24 00064 #define JRKERR_START_MOTION 0x05 00065 #define JRKERR_SET_GAINS 0xD6 00066 #define JRKERR_STOP_MOTOR 0x17 00067 #define JRKERR_IO_CONTROL 0x18 00068 #define JRKERR_SET_HOME_MODE 0x19 00069 #define JRKERR_SET_BAUD_RATE 0x1A 00070 #define JRKERR_CLEAR_BITS 0x0B 00071 #define JRKERR_SAVE_AS_HOME 0x0C 00072 #define JRKERR_NOP 0x0E 00073 #define JRKERR_HARD_RESET 0x0F 00074 00075 #define JRKERR_MAX_ELEVATOR_ANGLE 45 00076 #define JRKERR_MAX_RUDDER_ANGLE 45 00077 00078 #define JRKERR_ENCODER_TICKS_PER_DEGREE 13.194 00079 00080 #define JRKERR_FS_THRUST 255 00081 #define JRKERR_ZERO_THRUST 0 00082 00083 #define HARDWARE_FAILURE_TO 4.0 00084 00085 // Construction/Destruction 00087 00088 CMOOSJRKerrDriver::CMOOSJRKerrDriver() 00089 { 00090 m_bMoveDone = true; 00091 m_sLogFileName="JRKerrLog.txt"; 00092 00093 //these will be changed by SyncLog() 00094 m_dfRudder = 0; 00095 m_dfElevator = 0; 00096 m_dfRudderOffset = 0; 00097 m_dfElevatorOffset = 0; 00098 } 00099 00100 CMOOSJRKerrDriver::~CMOOSJRKerrDriver() 00101 { 00102 00103 } 00104 00105 void CMOOSJRKerrDriver::TraceKerrMessage(const char * pMsg,int nLen) 00106 { 00107 00108 00109 //print out command sent (hex values) 00110 if(m_pPort->IsVerbose()) 00111 { 00112 00113 MOOSTrace("Sent %02X: %02X", (unsigned char)pMsg[1], (unsigned char)pMsg[2]); 00114 //print Data bytes, but not checksum 00115 for (int i=3; i < (nLen-1); i++) 00116 { 00117 MOOSTrace(" %02X", (unsigned char)pMsg[i]); 00118 } 00119 MOOSTrace("\n"); 00120 } 00121 } 00122 00123 bool CMOOSJRKerrDriver::SendCmd(int addr, int nCmd, const char *Data) 00124 { 00125 //This function assembles, checksums and sends commands, then 00126 //deals with the returned status byte(s) 00127 00128 int nDataLen=0; 00129 int nRead=0; 00130 char nStatByte=0; 00131 char str[32]=""; 00132 00133 00134 char status[5]=""; 00135 00136 //Commands always start with 0xAA, followed by the module address, 00137 //then the command byte, then the associated Data bytes, then the 00138 //checksum. Data length is implicit in the upper nibble of the 00139 //command byte. Checksum does not include the preamble (0xAA). 00140 00141 nDataLen = (((unsigned char)nCmd & 0xF0) >> 4) & 0x0F; 00142 00143 char Tx[100]; 00144 int nNdx = 0; 00145 Tx[nNdx++] = (char)0xAA; 00146 Tx[nNdx++] = addr; 00147 Tx[nNdx++] = nCmd; 00148 int i=0; 00149 for (i=0;i<nDataLen;i++) 00150 { 00151 //send Data bytes 00152 Tx[nNdx++] = Data[i]; 00153 } 00154 00155 //now figure checksum. it's an 8bit wraparound 2's complement sum 00156 char cCheckSum = 0; 00157 for(i = 1;i<nNdx;i++) 00158 { 00159 cCheckSum+=Tx[i]; 00160 } 00161 00162 //append 00163 Tx[nNdx++] = cCheckSum; 00164 00165 //write 00166 m_pPort->Write(Tx,nNdx); 00167 TraceKerrMessage(Tx,nNdx); 00168 00169 //now deal with the returned Data. 00170 //first handle the initial status byte, which is always there. 00171 00172 nRead = m_pPort->ReadNWithTimeOut(status,1); 00173 if (nRead > 0) 00174 { 00175 nStatByte = status[0]; 00176 } 00177 else if (nCmd != JRKERR_HARD_RESET) 00178 { 00179 MOOSTrace("No response from JRKerr controller.\n"); 00180 return false; 00181 } 00182 00183 //then, if we're reading extra info, 00184 00185 if (nCmd==JRKERR_READ_STATUS) 00186 { 00187 if (Data[0] & 0x20) 00188 { 00189 nRead = m_pPort->ReadNWithTimeOut(status,2); 00190 00191 if (nRead > 0) 00192 { 00193 MOOSTrace("%02X: found device type %2d, version %2d\n", addr, status[0], status[1]); 00194 } 00195 else 00196 { 00197 MOOSTrace("Error reading device type and version\n"); 00198 } 00199 } 00200 } 00201 00202 //finally, read the status cksum 00203 nRead = m_pPort->ReadNWithTimeOut(status,1); 00204 00205 if ((nRead <= 0) && (nCmd != JRKERR_HARD_RESET)) 00206 { 00207 MOOSTrace("Error reading status checksum\n"); 00208 } 00209 if (m_pPort->IsVerbose() && (nCmd!=JRKERR_HARD_RESET)) 00210 { 00211 MOOSTrace("%02X replies: status 0x%02X\n", addr, (unsigned char)nStatByte); 00212 } 00213 00214 if (nStatByte & 0x02) 00215 { 00216 MOOSTrace("Checksum error in last command\n"); 00217 } 00218 00219 if (nStatByte & 0x01) 00220 { 00221 m_bMoveDone = true; 00222 } 00223 else 00224 { 00225 m_bMoveDone = false; 00226 } 00227 00228 return true; 00229 } 00230 00231 00232 bool CMOOSJRKerrDriver::LogPosition() 00233 { 00234 //clobber open 00235 ofstream JRKerrLogFile; 00236 00237 JRKerrLogFile.open(m_sLogFileName.c_str()); 00238 00239 if(JRKerrLogFile.is_open()) 00240 { 00241 JRKerrLogFile<<"Rudder="<<m_dfRudder<<",Elevator="<<m_dfElevator<<endl; 00242 00243 JRKerrLogFile.close(); 00244 } 00245 return true; 00246 } 00247 00248 00249 bool CMOOSJRKerrDriver::SyncLog() 00250 { 00251 //gentle open 00252 ifstream JRKerrLogFile; 00253 JRKerrLogFile.open(m_sLogFileName.c_str()); 00254 00255 if(JRKerrLogFile.is_open()) 00256 { 00257 string sLine; 00258 char Tmp[1000]; 00259 JRKerrLogFile.getline(Tmp,sizeof(Tmp)); 00260 sLine = string(Tmp); 00261 if(!sLine.empty()) 00262 { 00263 MOOSRemoveChars(sLine," \t"); 00264 string sRudder = MOOSChomp(sLine,","); 00265 MOOSChomp(sRudder,"Rudder="); 00266 00267 m_dfKerrRudderOffset = atof(sRudder.c_str()); 00268 m_dfRudder = m_dfKerrRudderOffset; 00269 00270 string sElevator = sLine; 00271 MOOSChomp(sElevator,"Elevator="); 00272 m_dfKerrElevatorOffset = atof(sElevator.c_str()); 00273 00274 m_dfElevator = m_dfKerrElevatorOffset; 00275 } 00276 JRKerrLogFile.close(); 00277 } 00278 return true; 00279 } 00280 00281 00282 00283 bool CMOOSJRKerrDriver::Initialise() 00284 { 00285 00286 if(m_pPort==NULL) 00287 return false; 00288 00289 //we keep a log file so fin position 00290 //is known if the driver crashes. 00291 SyncLog(); 00292 00293 if(m_pPort->IsVerbose()) 00294 { 00295 MOOSTrace("iActuation Init() : Sending JRKerr init commands\n"); 00296 } 00297 00298 //First we send 16 null bytes, wait 'at least 1 ms' and flush the rx buffer 00299 //on the device 00300 for (int i=0; i<16; i++) 00301 { 00302 m_pPort->Write("\x00", 1); 00303 } 00304 MOOSPause(1); 00305 00306 m_pPort->Flush(); 00307 00309 // now send the commands 00311 char Data[14]=""; 00312 bool bInitOK = true; 00313 00314 //first, reset to powerup state 00315 bInitOK = bInitOK && SendCmd(JRKERR_ADDR_GROUP, JRKERR_HARD_RESET, "\x00"); 00316 00317 //set addresses 00318 Data[0]=JRKERR_ADDR_THRUSTER; 00319 Data[1]=(char) 0xFF; 00320 bInitOK = bInitOK && SendCmd(JRKERR_ADDR_INITIAL, JRKERR_SET_ADDRESS, Data); 00321 00322 Data[0]=JRKERR_ADDR_RUDDER; 00323 Data[1]=(char)0xFF; 00324 bInitOK = bInitOK && SendCmd(JRKERR_ADDR_INITIAL, JRKERR_SET_ADDRESS, Data); 00325 00326 Data[0]=JRKERR_ADDR_ELEVATOR; 00327 Data[1]=(char)0xFF; 00328 bInitOK = bInitOK && SendCmd(JRKERR_ADDR_INITIAL, JRKERR_SET_ADDRESS, Data); 00329 00330 bInitOK = bInitOK && SendCmd(JRKERR_ADDR_THRUSTER, JRKERR_READ_STATUS, "\x20"); 00331 00332 bInitOK = bInitOK && SendCmd(JRKERR_ADDR_RUDDER, JRKERR_READ_STATUS, "\x20"); 00333 00334 bInitOK = bInitOK && SendCmd(JRKERR_ADDR_ELEVATOR, JRKERR_READ_STATUS, "\x20"); 00335 00336 //Set PID gains. Kp=5000, Kd=1000, Ki=0, IntLimit=0, 00337 //OutputLimit=255, CurrentLimit=0, ErrorLimit=10000, ServoRate=1 00338 //are fine values for all three actuators. 00339 //Multi-byte values are sent LSB first. 00340 00341 bInitOK = bInitOK && SendCmd(JRKERR_ADDR_THRUSTER, JRKERR_SET_GAINS, "\x88\x13" 00342 "\xE8\x03" 00343 "\x00\x00" 00344 "\x00\x00" 00345 "\xFF" 00346 "\x00" 00347 "\x10\x27" 00348 "\x01"); 00349 00350 bInitOK = bInitOK && SendCmd(JRKERR_ADDR_RUDDER, JRKERR_SET_GAINS, 00351 "\x88\x13\xE8\x03\x00\x00\x00\x00\xFF\x00\x10\x27\x01"); 00352 00353 bInitOK = bInitOK && SendCmd(JRKERR_ADDR_ELEVATOR, JRKERR_SET_GAINS, 00354 "\x88\x13\xE8\x03\x00\x00\x00\x00\xFF\x00\x10\x27\x01"); 00355 00356 bInitOK = bInitOK && SendCmd(JRKERR_ADDR_THRUSTER, JRKERR_STOP_MOTOR, "\x01"); 00357 00358 bInitOK = bInitOK && SendCmd(JRKERR_ADDR_RUDDER, JRKERR_STOP_MOTOR, "\x01"); 00359 00360 bInitOK = bInitOK && SendCmd(JRKERR_ADDR_ELEVATOR, JRKERR_STOP_MOTOR, "\x01"); 00361 00362 00363 00364 if (!bInitOK) 00365 { 00366 MOOSTrace("JRKerr init failed!\n"); 00367 return false; 00368 } 00369 else 00370 { 00371 MOOSTrace("JRKerr init OK\n"); 00372 return true; 00373 } 00374 } 00375 00376 00377 bool CMOOSJRKerrDriver::DoPositionMove(int position, int velocity, int acceleration, int address) 00378 { 00379 char nCmd[13]=""; 00380 00381 //wait until the previous move is complete. 00382 double dfTimeWaited = 0; 00383 while(!m_bMoveDone) 00384 { 00385 //this will set the m_bMoveDone flag when it's done moving - NOP just returns a status byte 00386 SendCmd(address, JRKERR_NOP, "\x00"); 00387 MOOSPause(10); 00388 dfTimeWaited+=0.01; 00389 00390 if( dfTimeWaited>HARDWARE_FAILURE_TO) 00391 { 00392 MOOSTrace("JRKerr HW not responding! FAILED MOVE ASSUMED\n"); 00393 return false; 00394 } 00395 } 00396 00397 //command byte, then 4bytes pos, 4b vel, 4b accel 00398 //command byte = 10010111: start now, pos mode, load p,v,a 00399 nCmd[0]=(char)0x97; 00400 //now for some crazy fun byte-noodling. Send LSB first. 00401 nCmd[1]=(position & 0x000000FF); 00402 nCmd[2]=(position & 0x0000FF00)>>8; 00403 nCmd[3]=(position & 0x00FF0000)>>16; 00404 nCmd[4]=(position & 0xFF000000)>>24; 00405 nCmd[5]=(velocity & 0x000000FF); 00406 nCmd[6]=(velocity & 0x0000FF00)>>8; 00407 nCmd[7]=(velocity & 0x00FF0000)>>16; 00408 nCmd[8]=(velocity & 0xFF000000)>>24; 00409 nCmd[9]=(acceleration & 0x000000FF); 00410 nCmd[10]=(acceleration & 0x0000FF00)>>8; 00411 nCmd[11]=(acceleration & 0x00FF0000)>>16; 00412 nCmd[12]=(acceleration & 0xFF000000)>>24; 00413 00414 return SendCmd(address, JRKERR_LOAD_TRAJECTORY_POS, nCmd); 00415 } 00416 00417 bool CMOOSJRKerrDriver::SetElevator(double dfAng) 00418 { 00419 00420 00421 //velocity and acceleration found by experiment, seem to work OK 00422 int position=0, velocity=100000, acceleration=100; 00423 00424 if (dfAng > JRKERR_MAX_ELEVATOR_ANGLE) 00425 { 00426 dfAng = JRKERR_MAX_ELEVATOR_ANGLE; 00427 } 00428 if (dfAng < -JRKERR_MAX_ELEVATOR_ANGLE) 00429 { 00430 dfAng = -JRKERR_MAX_ELEVATOR_ANGLE; 00431 } 00432 position = int((dfAng - m_dfKerrElevatorOffset) * JRKERR_ENCODER_TICKS_PER_DEGREE); 00433 m_dfElevator = dfAng; 00434 LogPosition(); 00435 00436 return DoPositionMove(position, velocity, acceleration, JRKERR_ADDR_ELEVATOR); 00437 } 00438 00439 bool CMOOSJRKerrDriver::SetRudder(double dfAng) 00440 { 00441 00442 00443 00444 //velocity and acceleration found by experiment, seem to work OK 00445 int position=0, velocity=100000, acceleration=100; 00446 00447 if (dfAng > JRKERR_MAX_RUDDER_ANGLE) 00448 { 00449 dfAng = JRKERR_MAX_RUDDER_ANGLE; 00450 } 00451 if (dfAng < -JRKERR_MAX_RUDDER_ANGLE) 00452 { 00453 dfAng = -JRKERR_MAX_RUDDER_ANGLE; 00454 } 00455 position = int((dfAng - m_dfKerrRudderOffset) * JRKERR_ENCODER_TICKS_PER_DEGREE); 00456 m_dfRudder = dfAng; 00457 LogPosition(); 00458 return DoPositionMove(position, velocity, acceleration, JRKERR_ADDR_RUDDER); 00459 } 00460 00461 bool CMOOSJRKerrDriver::SetZeroElevator() 00462 { 00463 m_dfElevator = 0; 00464 m_dfKerrElevatorOffset = 0; 00465 LogPosition(); 00466 return SendCmd(JRKERR_ADDR_ELEVATOR, JRKERR_RESET_POSITION, ""); 00467 } 00468 00469 bool CMOOSJRKerrDriver::SetZeroRudder() 00470 { 00471 00472 m_dfRudder = 0; 00473 m_dfKerrRudderOffset = 0; 00474 LogPosition(); 00475 return SendCmd(JRKERR_ADDR_RUDDER, JRKERR_RESET_POSITION, ""); 00476 } 00477 00478 bool CMOOSJRKerrDriver::SetThrust(double dfPercent) 00479 { 00480 00481 int nThrust; 00482 int nDirection=1; //1=fwd, 0=rev 00483 char nCmd[2]=""; 00484 00485 if(dfPercent>100) 00486 { 00487 dfPercent = 100.0; 00488 } 00489 if(dfPercent<-100) 00490 { 00491 dfPercent = -100.0; 00492 } 00493 00494 nThrust = (int)((dfPercent/100.0) * JRKERR_FS_THRUST + JRKERR_ZERO_THRUST); 00495 00496 if(nThrust<0) { 00497 nDirection=0; 00498 nThrust=-nThrust; 00499 } 00500 00501 //control byte, then PWM byte 00502 //control byte = 0b1?001000 = start move now, PWM mode, ? = direction 00503 00504 nCmd[0] = 0x88 | ((unsigned char)nDirection << 6); 00505 nCmd[1] = (unsigned char)nThrust; 00506 00507 return SendCmd(JRKERR_ADDR_THRUSTER, JRKERR_LOAD_TRAJECTORY_PWM, nCmd); 00508 00509 } 00510 00511 00512 double CMOOSJRKerrDriver::GetRPM() 00513 { 00514 return 0; 00515 } 00516 00517