Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

Dynamixel.cc

Go to the documentation of this file.
00001 #include "Dynamixel.h"
00002 #include "Shared/MarkScope.h"
00003 #include "Shared/debuget.h"
00004 #include "Shared/WorldState.h"
00005 #include "Shared/BioloidInfo.h"
00006 #include "Shared/TimeET.h"
00007 #include "IPC/CallbackThread.h"
00008 #include <algorithm>
00009 
00010 using namespace std; 
00011 using namespace DynamixelProtocol;
00012 
00013 INSTANTIATE_NAMEDENUMERATION_STATICS(SensorOffset_t);
00014 enum SeenState { LED_UNSEEN, LED_SAME, LED_DIFF };
00015 
00016 unsigned int DynamixelDriver::VOLTAGE_SENSOR_OFFSET = capabilities.findSensorOffset("PowerVoltage");
00017 unsigned int DynamixelDriver::TEMP_SENSOR_OFFSET = capabilities.findSensorOffset("PowerThermo");
00018 
00019 const std::string DynamixelDriver::autoRegisterDynamixelDriver = DeviceDriver::getRegistry().registerType<DynamixelDriver>("Dynamixel");
00020 
00021 void DynamixelDriver::motionStarting() {
00022   ASSERTRET(!motionActive,"DynamixelDriver::motionStarting, but motionActive is true");
00023   MotionHook::motionStarting();
00024   
00025   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00026   if(comm==NULL) {
00027     if(commName.size()>0)
00028       std::cerr << "DynamixelDriver \"" << instanceName << "\": initialization failed, CommPort \"" << commName << "\" not found" << std::endl;
00029   } else if(!comm->open() || !comm->isWriteable()) {
00030     std::cerr << "DynamixelDriver \"" << instanceName << "\": initialization failed, CommPort disconnected" << std::endl;
00031   } else {
00032     bool restartComm=false;
00033     if(commThread.isStarted()) { // commThread hogs the comm lock, stop it so we don't starve
00034       commThread.stop().join();
00035       restartComm=true;
00036     }
00037     MarkScope autolock(*comm);
00038     std::ostream os(&comm->getWriteStreambuf());
00039     // we'll reset these individually on the first motion update
00040     for(servo_iterator it=servos.begin(); it!=servos.end(); ++it)
00041       it->second->punch = it->second->margin = it->second->slope = 0;
00042     write(os, BroadcastFullComplianceCmd()).flush();
00043     write(os, BroadcastNoPunchCmd()).flush();
00044     write(os, BroadcastZeroSpeedCmd()).flush();
00045     // we use compliance slope to disable torque, so make sure it's enabled here
00046     write(os, BroadcastTorqueCmd(true)).flush();
00047     
00048     if(!sensorsActive) // first to become active, ping servos
00049       pingServos();
00050     
00051     if(restartComm) // we might have stopped commThread
00052       commThread.start();
00053   }
00054   
00055   motionActive=true;
00056   commName.addPrimitiveListener(this);
00057 }
00058 
00059 bool DynamixelDriver::isConnected() {
00060   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00061   return (comm!=NULL && comm->isReadable() && comm->isWriteable());
00062 }
00063 
00064 void DynamixelDriver::motionStopping() {
00065   ASSERTRET(motionActive,"DynamixelDriver::motionStopping, but motionActive is false");
00066   motionActive=false;
00067   if(!sensorsActive) { // last one to stay active...
00068     if(commThread.isStarted())
00069       commThread.stop().join();
00070     // listener count is not recursive, so only remove if we're the last one
00071     commName.removePrimitiveListener(this);
00072   }
00073   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00074   if(comm!=NULL) {
00075     sendZeroTorqueCmd(*comm);
00076     comm->close(); // this *is* recursive, so we always close it to match our open() in motionStarting()
00077   }
00078   MotionHook::motionStopping();
00079 }
00080 
00081 void DynamixelDriver::motionCheck(const float outputs[][NumOutputs]) {
00082   float * buf = commThread.getWriteBuffer();
00083   for(unsigned int i=NumOutputs; i!=0; ) {
00084     --i;
00085     buf[i] = outputs[NumFrames-1][i];
00086   }
00087   commThread.setWriteBufferTimestamp(buf);
00088 }
00089 
00090 void DynamixelDriver::updatePIDs(const std::vector<MotionHook::PIDUpdate>& pids) {
00091   MarkScope autolock(commThread.pidLock);
00092   for(std::vector<MotionHook::PIDUpdate>::const_iterator it=pids.begin(); it!=pids.end(); ++it)
00093     commThread.pidValues[it->idx]=*it;
00094   commThread.dirtyPIDs+=pids.size();
00095 }
00096 
00097 void DynamixelDriver::registerSource() {
00098   ASSERTRET(!sensorsActive,"DynamixelDriver::registerSource, but sensorsActive is true");
00099   sensorsActive=true;
00100   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00101   if(comm!=NULL)
00102     comm->open();
00103   for(servo_iterator it=servos.begin(); it!=servos.end(); ++it) {
00104     if(it->second->detected) {
00105       provideOutput(it->second->output);
00106       provideOutput(it->second->freeSpinOutput);
00107     }
00108     it->second->output.addPrimitiveListener(this);
00109     it->second->freeSpinOutput.addPrimitiveListener(this);
00110     it->second->detected.addPrimitiveListener(this);
00111   }
00112   if(!motionActive && comm!=NULL && comm->isWriteable()) // first to become active, ping servos
00113     pingServos();
00114   commName.addPrimitiveListener(this);
00115   commLatency.addPrimitiveListener(this);
00116   numPoll.addPrimitiveListener(this);
00117   responseTime.addPrimitiveListener(this);
00118   plistValueChanged(numPoll); // just to trigger sanity check on these values
00119 }
00120 
00121 void DynamixelDriver::deregisterSource() {
00122   ASSERTRET(sensorsActive,"DynamixelDriver::deregisterSource, but sensorsActive is false");
00123   sensorsActive=false;
00124   if(!motionActive) { // last to stay active...
00125     if(commThread.isStarted())
00126       commThread.stop().join();
00127     // listener count is not recursive, so only remove if we're the last one
00128     commName.removePrimitiveListener(this);
00129   }
00130   commLatency.removePrimitiveListener(this);
00131   numPoll.removePrimitiveListener(this);
00132   responseTime.removePrimitiveListener(this);
00133   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00134   if(comm!=NULL)
00135     comm->close();
00136   for(servo_iterator it=servos.begin(); it!=servos.end(); ++it) {
00137     it->second->detected.removePrimitiveListener(this);
00138     it->second->freeSpinOutput.removePrimitiveListener(this);
00139     it->second->output.removePrimitiveListener(this);
00140     if(it->second->detected)
00141       ignoreOutput(it->second->output);
00142   }
00143 }
00144 
00145 void DynamixelDriver::doUnfreeze() {
00146   MarkScope sl(commThread.getStartLock());
00147   if(!commThread.isStarted()) {
00148     commThread.start();
00149   }
00150 }
00151 
00152 void DynamixelDriver::doFreeze() {
00153   MarkScope sl(commThread.getStartLock());
00154   if(commThread.isStarted()) {
00155     commThread.stop().join();
00156     ASSERT(!commThread.isStarted(),"DynamixelDriver::CommThread ended, but still running?");
00157   }
00158 }
00159 
00160 unsigned int DynamixelDriver::nextTimestamp() {
00161   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00162   if(comm==NULL || !comm->isReadable() || !comm->isWriteable())
00163     return -1U;
00164   return commThread.nextTimestamp();
00165 }
00166 
00167 bool DynamixelDriver::advance() {
00168   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00169   if(comm==NULL || !comm->isReadable() || !comm->isWriteable())
00170     return false;
00171   
00172   if(!commThread.isStarted()) {
00173     // indicates we're in non-realtime mode
00174     pingServos(true);
00175   }
00176   static unsigned int lockeduptest=0;
00177   if(!commThread.takeUpdate()) {
00178     if(++lockeduptest==8)
00179       std::cerr << "WARNING: DynamixelDriver appears to be locked up, not getting new sensor readings" << std::endl;
00180     return false;
00181   }
00182   if(lockeduptest>=8)
00183     std::cerr << "DynamixelDriver has gotten unwedged, sending sensor updates again." << std::endl;
00184   lockeduptest=0;
00185   
00186   return true;
00187 }
00188 
00189 void DynamixelDriver::plistValueChanged(const plist::PrimitiveBase& pl) {
00190   if(&pl==&commName) {
00191     // if here, then motionStarted or registerSource has been called, thus when commName changes,
00192     // need to close old one and reopen new one
00193     if(commThread.isStarted())
00194       commThread.stop().join();
00195     
00196     CommPort * comm = CommPort::getRegistry().getInstance(commName.getPreviousValue());
00197     if(comm!=NULL) {
00198       // close each of our old references
00199       if(sensorsActive)
00200         comm->close();
00201       if(motionActive) {
00202         sendZeroTorqueCmd(*comm);
00203         comm->close();
00204       }
00205     }
00206     bool motionWasActive=motionActive, sensorsWereActive=sensorsActive;
00207     sensorsActive=motionActive=false;
00208     
00209     // open each of our new references
00210     if(motionWasActive)
00211       motionStarting();
00212     if(sensorsWereActive)
00213       registerSource();
00214     
00215     if(getTimeScale()>0)
00216       commThread.start();
00217   } else if(&pl==&commLatency || &pl==&numPoll || &pl==&responseTime) {
00218     if(numPoll==0) {
00219       std::cerr << "NumPoll must be at least 1, remove " << instanceName << " from the Sensors.Sources list if you want to disable sensor polling." << std::endl;
00220       numPoll=1;
00221     } else if(numPoll>1 && (numPoll * responseTime)/1000 > commLatency) {
00222       std::cerr << "WARNING: NumPoll * ResponseTime (" << numPoll << "·" << responseTime << "µs=" << (numPoll*responseTime/1000) << "ms) exceeds BufferLatency (" << commLatency << "ms)\n"
00223       "You may be missing synchronization with buffer flushes" << std::endl;
00224     }
00225   } else {
00226     // check if it's one of the individual servos... if it is, means we're providing servo feedback,
00227     // need to call providingOutput/ignoringOutput as appropriate
00228     for(servo_iterator it=servos.begin(); it!=servos.end(); ++it) {
00229       if(&pl==&it->second->detected) {
00230         if(it->second->detected) {
00231           provideOutput(it->second->output);
00232           provideOutput(it->second->freeSpinOutput);
00233         } else {
00234           ignoreOutput(it->second->freeSpinOutput);
00235           ignoreOutput(it->second->output);
00236         }
00237         return; // found it, DON'T fall through to error message below...
00238       } else if(it->second->detected) {
00239         if(&pl==&it->second->output) {
00240           ignoreOutput(it->second->output.getPreviousValue());
00241           provideOutput(it->second->output);
00242           return; // found it, DON'T fall through to error message below...
00243         } else if(&pl==&it->second->freeSpinOutput) {
00244           ignoreOutput(it->second->freeSpinOutput.getPreviousValue());
00245           provideOutput(it->second->freeSpinOutput);
00246           return; // found it, DON'T fall through to error message below...
00247         }
00248       } else if(&pl==&it->second->output || &pl==&it->second->freeSpinOutput) {
00249         return; // found it, DON'T fall through to error message below...
00250       }
00251     }
00252     std::cerr << "Unhandled value change in " << getClassName() << ": " << pl.get() << std::endl;
00253   }
00254 }
00255 
00256 void DynamixelDriver::processDriverMessage(const DriverMessaging::Message& d) {
00257   if(d.CLASS_NAME==DriverMessaging::LoadPrediction::NAME) {
00258     const DriverMessaging::LoadPrediction& loads = dynamic_cast<const DriverMessaging::LoadPrediction&>(d);
00259     for(servo_iterator it=servos.begin(); it!=servos.end(); ++it) {
00260       plist::DictionaryOf<plist::Primitive<float> >::const_iterator dit = loads.loads.findEntry(it->second->output.get());
00261       if(dit!=loads.loads.end())
00262         it->second->predictedLoad=*dit->second;
00263     }
00264   } else if(d.CLASS_NAME==DriverMessaging::SensorPriority::NAME) {
00265     const DriverMessaging::SensorPriority& pri = dynamic_cast<const DriverMessaging::SensorPriority&>(d);
00266     for(servo_iterator it=servos.begin(); it!=servos.end(); ++it) {
00267       plist::DictionaryOf<plist::Primitive<float> >::const_iterator dit = pri.outputs.findEntry(it->second->output.get());
00268       if(dit!=pri.outputs.end())
00269         it->second->sensorPriority=*dit->second;
00270     }
00271   }
00272 }
00273 
00274 void DynamixelDriver::pingServos(bool detectedOnly) {
00275   if(!detectedOnly)
00276     for(servo_iterator it=servos.begin(); it!=servos.end(); ++it)
00277       it->second->detected=false;
00278 
00279   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00280   if(comm==NULL)
00281     return;
00282   
00283   bool restartComm=false;
00284   if(commThread.isStarted()) { // commThread hogs the comm lock, stop it so we don't starve
00285     commThread.stop().join();
00286     restartComm=true;
00287   }
00288   
00289   MarkScope autolock(comm->getLock());
00290   if(!comm->isWriteable() || !comm->isReadable()) {
00291     std::cerr << "DynamixelDriver \"" << instanceName << "\": cannot ping servos, CommPort disconnected." << std::endl;
00292     ASSERT(!restartComm,"How was the comm thread still running?  Not restarting it...");
00293     return;
00294   }
00295   std::istream is(&comm->getReadStreambuf());
00296   std::ostream os(&comm->getWriteStreambuf());
00297   if(!is || !os) {
00298     std::cerr << "DynamixelDriver \"" << instanceName << "\": cannot ping servos, CommPort gave bad iostreams." << std::endl;
00299     ASSERT(!restartComm,"How was the comm thread still running?  Not restarting it...");
00300     return;
00301   }
00302   
00303   // request exception on badbit so we can thread_cancel read under linux
00304   // (otherwise we get FATAL: exception not rethrown / Abort error)
00305   is.exceptions(ios_base::badbit);
00306   
00307   MarkScope writeLock(getSensorWriteLock());
00308   ServoSensorsResponse servoSensors;
00309   AXS1SensorsResponse axs1Sensors;
00310   for(servo_iterator it=servos.begin(); it!=servos.end(); ++it) {
00311     if( (it->second->getModel()==MODEL_UNKNOWN && !it->second->hasSensorOffset() && it->second->output==UNUSED)
00312        || ( it->second->getModel()==MODEL_AXS1 && !it->second->hasSensorOffset() )
00313        || ( it->second->getModel()!=MODEL_UNKNOWN && it->second->getModel()!=MODEL_AXS1 && it->second->output==UNUSED ))
00314       continue; // don't poll units we're not using
00315     if(!detectedOnly) {
00316       it->second->setModel(MODEL_UNKNOWN);
00317       it->second->detected=false;
00318     } else if(!it->second->detected) {
00319       continue;
00320     }
00321     unsigned int servoid = it->second->servoID;
00322     PingThread ping(is, os, servoid, it->second->output, &servoSensors, &axs1Sensors);
00323     const char* model = static_cast<const char*>(ping.join());
00324     is.clear();
00325     os.clear();
00326     if(model==Thread::CANCELLED || model==NULL) {
00327       unsigned int idx = it->second->output;
00328       if(static_cast<unsigned int>(idx)<NumOutputs) {
00329         cerr << "Warning: Dynamixel servo " << it->first
00330          << " (mapped to output offset " << outputNames[idx] << ")"
00331          << " was disconnected or not found" << endl;
00332       } else if(idx!=UNUSED) {
00333         cerr << "Warning: Dynamixel servo " << it->first
00334          << " (mapped to invalid output " << idx << "))"
00335          << " was disconnected or not found" << endl;
00336       } else if(servoid>=NumOutputs+START_SERVO_ID && (it->second->leftIRDistOffset!=-1 || it->second->centerIRDistOffset!=-1 || it->second->rightIRDistOffset!=-1
00337           || it->second->leftLuminosityOffset!=-1 || it->second->centerLuminosityOffset!=-1 || it->second->rightLuminosityOffset!=-1
00338           || it->second->micVolumeOffset!=-1 || it->second->micSpikeCountOffset!=-1))
00339       {
00340         cerr << "Warning: Dynamixel sensor module " << it->first
00341         << " was disconnected or not found" << endl;
00342       }
00343       it->second->sensorActivation=0;
00344       continue;
00345     }
00346     it->second->setModelName(model);
00347     for(std::map<DynamixelProtocol::ModelID_t, const std::string>::const_iterator nit=DynamixelProtocol::dynamixelModels.begin(); nit!=DynamixelProtocol::dynamixelModels.end(); ++nit) {
00348       if(nit->second==model) {
00349         it->second->setModel(nit->first);
00350         break;
00351       }
00352     }
00353     //std::cerr << "Ping " << it->second->servoID << " detected model " << it->second->getModel() << std::endl;
00354     it->second->detected=true;
00355     if(it->second->sensorActivation==0)
00356       it->second->sensorActivation=1;
00357     if(it->second->getModel()==MODEL_AXS1) {
00358       provideValues(*it->second,axs1Sensors);
00359     } else if(it->second->getModel()!=MODEL_UNKNOWN) {
00360       it->second->lastCmd=servoSensors.getPosition();
00361       provideValues(*it->second,servoSensors);
00362     }
00363   }
00364   
00365   if(restartComm)
00366     commThread.start();
00367 }
00368 
00369 void DynamixelDriver::sendZeroTorqueCmd(CommPort& comm) {
00370   if(!comm.isWriteable()) {
00371     std::cerr << "DynamixelDriver \"" << instanceName << "\": unable to send shutdown, CommPort disconnected" << std::endl;
00372   } else {
00373     MarkScope autolock(comm);
00374     std::ostream os(&comm.getWriteStreambuf());
00375     //printf("\nHEADER: ");
00376     //debuget::hexout(BroadcastTorqueCmd(true),sizeof(BroadcastTorqueCmd));
00377     write(os, BroadcastTorqueCmd(false)).flush();
00378   }
00379 }
00380 
00381 /// @cond INTERNAL
00382 static bool sensorActivationCompare(const DynamixelDriver::ServoInfo* a, const DynamixelDriver::ServoInfo* b) { return a->sensorActivation > b->sensorActivation; }
00383 /// @endcond
00384 
00385 Thread& DynamixelDriver::CommThread::stop() {
00386   // we might be stopped by the user while failsafe is also engaged
00387   if(getCurrent()!=&failsafe) {
00388     // not stopped by failsafe, so stop the failsafe first
00389     failsafe.restartFlag=false;
00390     failsafe.stop().join();
00391   }
00392   // failsafe may have already stopped this thread
00393   if(isStarted())
00394     Thread::stop();
00395   return *this;
00396 }
00397 
00398 void DynamixelDriver::CommThread::waitForUpdate() {
00399   if(!isStarted()) {
00400     if(!updated)
00401       std::cerr << "DynamixelDriver::CommThread::waitForUpdate: thread not running!" << std::endl;
00402     return;
00403   }
00404   if(updated)
00405     return;
00406   join();
00407   ASSERT(!failsafe.isStarted(),"DynamixelDriver::CommThread ended, but failsafe still running?");
00408 }
00409 
00410 float * DynamixelDriver::CommThread::getWriteBuffer() {
00411   float * bufs[3];
00412   if(timestampBufA<timestampBufB) {
00413     if(timestampBufA<timestampBufC) {
00414       bufs[0]=outputBufA;
00415       if(timestampBufB<timestampBufC) {
00416         bufs[1]=outputBufB;
00417         bufs[2]=outputBufC;
00418       } else {
00419         bufs[1]=outputBufC;
00420         bufs[2]=outputBufB;
00421       }
00422     }  else {
00423       bufs[0]=outputBufC;
00424       bufs[1]=outputBufA;
00425       bufs[2]=outputBufB;
00426     }
00427   } else if(timestampBufB<timestampBufC) {
00428     bufs[0]=outputBufB;
00429     if(timestampBufA<timestampBufC) {
00430       bufs[1]=outputBufA;
00431       bufs[2]=outputBufC;
00432     }  else {
00433       bufs[1]=outputBufC;
00434       bufs[2]=outputBufA;
00435     }
00436   } else {
00437     bufs[0]=outputBufC;
00438     bufs[1]=outputBufB;
00439     bufs[2]=outputBufA;
00440   }
00441   return (bufs[0]==curBuf) ? bufs[1] : bufs[0];
00442 }
00443 
00444 void DynamixelDriver::CommThread::setWriteBufferTimestamp(float * buf) {
00445   if(buf==outputBufA)
00446     timestampBufA=get_time();
00447   else if(buf==outputBufB)
00448     timestampBufB=get_time();
00449   else if(buf==outputBufC)
00450     timestampBufC=get_time();
00451   else
00452     std::cerr << "DynamixelDriver::CommThread::setWriteBufferTimestamp was passed a unknown buffer" << std::endl;
00453 }
00454 
00455 bool DynamixelDriver::CommThread::launched() {
00456   isFirstCheck=true;
00457   if(!failsafe.isRunning())
00458     failsafe.start();
00459   return true;
00460 }
00461 
00462 void DynamixelDriver::CommThread::cancelled() {
00463   if(!failsafe.isEngaged()) {
00464     ASSERT(!failsafe.isStarted(),"Failsafe is still running!  How was CommThread cancelled?");
00465     if(responsePending) {
00466       // clear read buffer so it doesn't interfere with whatever we do next
00467       CallbackThread cb(std::mem_fun(&CommThread::clearBuffer), this, true);
00468       usleep(50*1000);
00469       if(cb.isStarted())
00470         cb.stop().join();
00471     }
00472   } else {
00473     if(servoPollQueue.empty())
00474       return;
00475     ServoInfo* cur=NULL;
00476     // find first with some activation energy, that was the one in progress
00477     for(std::vector<ServoInfo*>::const_iterator it=servoPollQueue.begin(); it!=servoPollQueue.end(); ++it) {
00478       cur=*it;
00479       if(cur->sensorActivation>0)
00480         break;
00481     }
00482     cur->sensorActivation=0;
00483     //std::cerr << "DynamixelDriver: comm timeout reading from servo #" << cur->servoID << ", restarting thread" << std::endl;
00484     if(++(cur->failures) >= 20) {
00485       // consistently failing, mark servo as missing so we'll ignore it from now on
00486       std::cerr << "DynamixelDriver: too many failures on #" << cur->servoID << ", disabling 'Detected' flag" << std::endl;
00487       cur->detected=false;
00488       cur->sensorActivation=0;
00489     }
00490   }
00491 }
00492 
00493 void DynamixelDriver::CommThread::clearBuffer() {
00494   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00495   if(comm==NULL || !comm->isReadable() || !comm->isWriteable())
00496     return;
00497   
00498   MarkScope autolock(comm->getLock());
00499   std::istream is(&comm->getReadStreambuf());
00500   
00501   // request exception on badbit so we can thread_cancel read under linux
00502   // (otherwise we get FATAL: exception not rethrown / Abort error)
00503   is.exceptions(ios_base::badbit);
00504   
00505   while(is) {
00506     is.get();
00507     Thread::testCurrentCancel();
00508   }
00509 }
00510 
00511 unsigned int DynamixelDriver::CommThread::runloop() {
00512   testCancel();
00513   
00514   failsafe.progressFlag=true;
00515   
00516   // if there aren't any servos detected, just return now
00517   if(servoPollQueue.empty())
00518     return FrameTime*NumFrames*1000;
00519   
00520   // get the comm port
00521   CommPort * comm = CommPort::getRegistry().getInstance(commName);
00522   if(comm==NULL || !comm->isReadable() || !comm->isWriteable())
00523     return FrameTime*NumFrames*1000;
00524     
00525   MarkScope autolock(comm->getLock());
00526   std::ostream os(&comm->getWriteStreambuf());
00527   std::istream is(&comm->getReadStreambuf());
00528   
00529   // request exception on badbit so we can thread_cancel read under linux
00530   // (otherwise we get FATAL: exception not rethrown / Abort error)
00531   os.exceptions(ios_base::badbit);
00532   is.exceptions(ios_base::badbit);
00533   
00534   if(!driver.sensorsActive && continuousUpdates)
00535     updateCommands(is,os);
00536   
00537   unsigned int NUM_AVAIL_POLL = std::min<unsigned int>(driver.numPoll,servoPollQueue.size());
00538   std::partial_sort(servoPollQueue.begin(),servoPollQueue.begin()+NUM_AVAIL_POLL,servoPollQueue.end(),sensorActivationCompare);
00539   
00540   for(unsigned int i=0; driver.sensorsActive && i<NUM_AVAIL_POLL; ++i) {
00541     failsafe.progressFlag=true;
00542     if(servoPollQueue[i]->sensorActivation<=0) {
00543       NUM_AVAIL_POLL=i;
00544       break;
00545     }
00546     if(i>0) {
00547       // it's important we wait for at least the specified response time so the next query doesn't collide with a response in progress
00548       struct timespec st, rt;
00549       st.tv_sec = 0;
00550       st.tv_nsec = driver.responseTime*1000;
00551       while(nanosleep(&st,&rt)!=0) {
00552         testCancel();
00553         st=rt;
00554       }
00555     }
00556     failsafe.progressFlag=true;
00557     if(continuousUpdates)
00558       updateCommands(is,os);
00559     
00560     //cout << get_time() << " requesting from " << servoPollQueue[i]->servoID << " model " << servoPollQueue[i]->getModel() << endl;
00561     
00562     // send request for sensor values...
00563     //TimeET resptime;
00564     if(servoPollQueue[i]->getModel()==MODEL_AXS1) {
00565       //std::cout << "Query AX-S1 " << servoPollQueue[i]->servoID << std::endl;
00566       write(os, ReadAXS1SensorsCmd(servoPollQueue[i]->servoID)).flush();
00567       responsePending=true;
00568     } else if(servoPollQueue[i]->getModel()!=MODEL_UNKNOWN) {
00569       //std::cout << "Query Servo " << servoPollQueue[i]->servoID << std::endl;
00570       write(os, ReadServoSensorsCmd(servoPollQueue[i]->servoID)).flush();
00571       responsePending=true;
00572     } else {
00573       std::cerr << "Dynamixel Driver, attempting to poll unknown model '" << servoPollQueue[i]->getModelName() << "' (" << servoPollQueue[i]->getModel() << ") at servo ID " << servoPollQueue[i]->servoID << " (ignoring)" << std::endl;
00574       // drop it so we don't keep repeating the message
00575       std::vector<ServoInfo*>::iterator it=servoPollQueue.begin();
00576       std::advance(it,i);
00577       servoPollQueue.erase(it);
00578       --i;
00579       os.flush(); // flush any pending data (e.g. updateCommands) before the sleep on next iteration...
00580     }
00581     if(!os) {
00582       std::cerr << "DynamixelDriver " << driver.instanceName << " unable to send sensor poll request, lost comm?" << std::endl;
00583       return false;
00584     }
00585   }
00586   
00587   //TimeET latency;
00588   
00589   //std::cout << "READ " << get_time() << '\t' << (get_time()-lastSensorTime) << std::endl;
00590   lastSensorTime=get_time(); // actually should be set to time of first successful read... will be reset if that goes through
00591   
00592   ServoSensorsResponse servoresponse;
00593   AXS1SensorsResponse axs1response;
00594   for(unsigned int i=0; driver.sensorsActive && i<NUM_AVAIL_POLL; ++i) {
00595     failsafe.progressFlag=true;
00596     
00597     //cout << "reading from " << servoPollQueue[i]->servoID << endl;
00598     
00599     if(servoPollQueue[i]->getModel()==MODEL_AXS1) {
00600       readResponse(axs1response, is, *servoPollQueue[i]);
00601       
00602       // if we clear the count every poll, it disrupts count in progress, so count would always stay 0.  So only clear after a count has been registered.
00603       // buffer the clear commands, we don't want to interfere with incoming reads.  Will wait to flush after reads are complete.
00604       unsigned char checksum = 0;
00605       if(axs1response.sndCount>0) {
00606         write(os, SyncWriteHeader<SyncWriteSoundHoldAndCountEntry>(1), checksum);
00607         write(os, SyncWriteSoundHoldAndCountEntry(servoPollQueue[i]->servoID), checksum);
00608       } else {
00609         write(os, SyncWriteHeader<SyncWriteSoundHoldEntry>(1), checksum);
00610         write(os, SyncWriteSoundHoldEntry(servoPollQueue[i]->servoID), checksum);
00611       }
00612       os.put(~checksum);
00613       // note there is no flush... ok to wait and let it go out with the next write.
00614     
00615     } else if(servoPollQueue[i]->getModel()!=MODEL_UNKNOWN) {
00616       readResponse(servoresponse, is, *servoPollQueue[i]);
00617     }
00618     
00619     if(i==0)
00620       lastSensorTime=get_time(); // the rest should be coming in short order, but this syncs us to the comm buffer clear period
00621     
00622     /*
00623      // adaptive load compensation not working... using loadCompensation directly as value instead of flag to enable servoDeflection
00624     unsigned short pos = response.getPosition();
00625     //cout << "Polling " << servoPollQueue[i]->servoID << " pos " << pos << " predicted " << servoPollQueue[i]->predictedLoad << endl;
00626     if(std::abs(servoPollQueue[i]->predictedLoad)>0.25 && pos>0 && pos<1024) {
00627       float defl = pos - servoPollQueue[i]->lastCmd;
00628       float f = defl / servoPollQueue[i]->predictedLoad;
00629       servoDeflection = servoDeflection*.999 + f*.001;
00630       //cout << servoPollQueue[i]->servoID << " at " << pos << " deflection " << defl << " prediction " << servoPollQueue[i]->predictedLoad << " = " << servoDeflection << endl;
00631     }
00632     */
00633   }
00634   responsePending=false;
00635   
00636   // update activations to indicate which servos to poll next in CommThread...
00637   // Increment activation so the end of the list should have an activation of 20
00638   // (just a heuristic to be on par with a motion of 20 servo tics per update)
00639   const float actInc = (driver.numPoll>servoPollQueue.size()) ? 20 : 20.f*driver.numPoll/servoPollQueue.size();
00640   for(servo_iterator it=servos.begin(); it!=servos.end(); ++it) {
00641     ServoInfo * s = it->second;
00642     if(s->detected) {
00643       if(s->sensorPriority>=0) {
00644         s->sensorActivation += s->sensorPriority;
00645       } else { // auto mode
00646         // we add more activation for movement too...
00647         s->sensorActivation += actInc + s->recentCmdMotion;
00648       }
00649     }
00650     /*if(s->sensorActivation>0)
00651      cout << "Servo " << s->servoID << " has activation " << s->sensorActivation << endl;*/
00652   }
00653   
00654   //std::cout << "Latency: " << latency.Age() << std::endl;
00655   
00656   updated=true;
00657   //std::cout << "updated " /*<< (driver.thread==NULL) << ' ' << continuousUpdates << ' ' << NUM_AVAIL_POLL<< ' ' << servoPollQueue.size() << ' '*/ << get_time() << std::endl;
00658   
00659   if(!continuousUpdates) {
00660     failsafe.stop();
00661     return -1U;
00662   }
00663   
00664   return (driver.sensorsActive && NUM_AVAIL_POLL>0) ? 0 : FrameTime*NumFrames*1000;
00665 }
00666 
00667 void DynamixelDriver::provideValues(const ServoInfo& info, const DynamixelProtocol::ServoSensorsResponse& response) {
00668   MarkScope writeLock(getSensorWriteLock());
00669   
00670   if(info.freeSpinOutput<NumOutputs) {
00671     float x=response.getSpeed();
00672     if(info.invertRotation)
00673       x=1023-x;
00674     unsigned int idx=info.freeSpinOutput;
00675     float outputValue = x * info.repSpeedSlope; // apply calibration
00676     setOutputValue(idx,outputValue);
00677     if(idx-PIDJointOffset<NumPIDJoints) {
00678       float dutyValue = (info.curRotationMode!=ServoInfo::CONTINUOUS) ? 0 : response.getLoad()/1023.f;
00679       setPIDDutyValue(idx-PIDJointOffset, (info.invertRotation) ? -dutyValue : dutyValue);
00680     }
00681   }
00682   if(info.output<NumOutputs && info.output!=info.freeSpinOutput) {
00683     float x = response.getPosition();
00684     if(info.invertRotation)
00685       x=info.maxTic-x;
00686     x = x/info.maxTic*info.maxAngle;
00687     x -= info.zeroAngle + info.maxAngle/2;
00688     unsigned int idx=info.output;
00689     setOutputValue(idx,x);
00690     if(idx-PIDJointOffset<NumPIDJoints) {
00691       float dutyValue = (info.curRotationMode!=ServoInfo::POSITION) ? 0 : response.getLoad()/1023.f;
00692       setPIDDutyValue(idx-PIDJointOffset, (info.invertRotation) ? -dutyValue : dutyValue);
00693     }
00694   }
00695   ASSERT(info.output!=info.freeSpinOutput || info.curRotationMode==ServoInfo::CONTINUOUS,"Permanent free-spin, but curRotationMode not CONTINUOUS");
00696   
00697   if(VOLTAGE_SENSOR_OFFSET<NumSensors)
00698     setSensorValue(VOLTAGE_SENSOR_OFFSET, response.voltage/10.f);
00699   if(TEMP_SENSOR_OFFSET<NumSensors)
00700     setSensorValue(TEMP_SENSOR_OFFSET, response.temp);
00701 }
00702 
00703 /*!
00704  TODO: let sndCount be mapped to a button instead of sensor to provide events?
00705  */
00706 void DynamixelDriver::provideValues(ServoInfo& info, const DynamixelProtocol::AXS1SensorsResponse& response) {
00707   if(info.leftIRDistOffset!=-1)
00708     setSensorValue(info.leftIRDistOffset, response.leftIR/255.f);
00709   if(info.centerIRDistOffset!=-1)
00710     setSensorValue(info.centerIRDistOffset, response.centerIR/255.f);
00711   if(info.rightIRDistOffset!=-1)
00712     setSensorValue(info.rightIRDistOffset, response.rightIR/255.f);
00713   
00714   if(info.leftLuminosityOffset!=-1)
00715     setSensorValue(info.leftLuminosityOffset, response.leftLum/255.f);
00716   if(info.centerLuminosityOffset!=-1)
00717     setSensorValue(info.centerLuminosityOffset, response.centerLum/255.f);
00718   if(info.rightLuminosityOffset!=-1)
00719     setSensorValue(info.rightLuminosityOffset, response.rightLum/255.f);
00720   
00721   if(info.micVolumeOffset!=-1 && response.sndMaxHold>0) // if zero, didn't get a reading; shouldn't see any values<127
00722     setSensorValue(info.micVolumeOffset, std::abs(response.sndMaxHold-127)/128.f);
00723   if(info.micSpikeCountOffset!=-1) {
00724     if(info.micSpikeFrameNumber != getSensorFrameNumber()) {
00725       // count has been read since last update, reset count to current value
00726       setSensorValue(info.micSpikeCountOffset, response.sndCount);
00727       info.micSpikeFrameNumber = getSensorFrameNumber();
00728     } else if(response.sndCount>0) {
00729       // previous spikes haven't been picked up yet - if new spikes, add on to the count
00730       setSensorValue(info.micSpikeCountOffset, getSensorValue(info.micSpikeCountOffset) + response.sndCount);
00731     }
00732   }
00733 
00734   /*
00735   if(VOLTAGE_SENSOR_OFFSET<NumSensors)
00736     setSensorValue(VOLTAGE_SENSOR_OFFSET, response.voltage/10.f);
00737   if(TEMP_SENSOR_OFFSET<NumSensors)
00738     setSensorValue(TEMP_SENSOR_OFFSET, response.temp);
00739    */
00740 }
00741 
00742 void DynamixelDriver::CommThread::updateCommands(std::istream& is, std::ostream& os) {
00743   if(timestampBufA>timestampBufB) {
00744     if(timestampBufA>timestampBufC) {
00745       if(curBuf == outputBufA)
00746         return;
00747       curBuf = outputBufA;
00748     }  else {
00749       if(curBuf == outputBufC)
00750         return;
00751       curBuf = outputBufC;
00752     }
00753   } else if(timestampBufB>timestampBufC) {
00754     if(curBuf == outputBufB)
00755       return;
00756     curBuf = outputBufB;
00757   } else {
00758     if(timestampBufC==0)
00759       return; // no data yet, don't send commands to servos
00760     if(curBuf == outputBufC)
00761       return;
00762     curBuf = outputBufC;
00763   }
00764   
00765   float period = NumFrames*FrameTime;
00766   if(getTimeScale()>0)
00767     period /= ::getTimeScale();
00768   
00769   std::vector<SyncWritePosSpeedEntry> packets;
00770   packets.reserve(servos.size());
00771   
00772   std::vector<SyncWriteContinuousRotationEntry> modes;
00773   modes.reserve(servos.size());
00774   
00775   typedef std::vector<std::pair<std::string,ServoInfo::RotationMode> > modeUpdates_t;
00776   modeUpdates_t modeUpdates;
00777   modeUpdates.reserve(servos.size());
00778   
00779   std::vector<SyncWriteLEDEntry> leds;
00780   std::vector<SeenState> seenLEDs(NumLEDs,LED_UNSEEN);
00781   leds.reserve(servos.size());
00782   
00783   std::vector<SyncWriteComplianceEntry> compliances;
00784   compliances.reserve(servos.size());
00785   
00786   std::vector<SyncWritePunchEntry> punches;
00787   punches.reserve(servos.size());
00788   
00789   std::vector<SyncWriteTorqueEntry> torqueToggles;
00790   torqueToggles.reserve(servos.size());
00791   
00792   const bool havePIDUpdates = (dirtyPIDs>0);
00793   if(havePIDUpdates)
00794     pidLock.lock();
00795   
00796   for(servo_iterator it=servos.begin(); it!=servos.end(); ++it) {
00797     if(!it->second->detected || it->second->getModel()==MODEL_AXS1)
00798       continue;
00799     unsigned int servoid = it->second->servoID;
00800     
00801     unsigned int ledidx = it->second->led;
00802 #ifndef TGT_HAS_LEDS
00803     if(ledidx!=UNUSED)
00804       std::cerr << "Warning: Dynamixel driver mapping servo " << it->first << " to invalid led output index " << ledidx << std::endl;
00805 #else
00806     unsigned int ledsubidx = ledidx - LEDOffset;
00807     if(ledidx>=NumOutputs) {
00808       if(ledidx!=UNUSED)
00809         std::cerr << "Warning: Dynamixel driver mapping servo " << it->first << " to invalid led output index " << ledidx << std::endl;
00810     } else if(static_cast<unsigned int>(ledsubidx)>=NumLEDs) {
00811       std::cerr << "Warning: Dynamixel driver mapping servo " << it->first << " to invalid led index " << ledsubidx << std::endl;
00812     } else if(lastOutputs[ledidx]!=curBuf[ledidx] || lastLEDState[ledsubidx]==LED_UNKNOWN || (curBuf[ledidx]>0 && curBuf[ledidx]<1)) {
00813       if(seenLEDs[ledsubidx]==LED_UNSEEN) {
00814         // ensures we only calculate the led activation value once per LED signal
00815         // (even if it might be mapped to several servo LEDs...)
00816         LedState cur = calcLEDValue(ledsubidx,curBuf[ledidx]) ? LED_ON : LED_OFF;
00817         seenLEDs[ledsubidx] = (cur==lastLEDState[ledsubidx]) ? LED_SAME : LED_DIFF;
00818         lastLEDState[ledsubidx]=cur;
00819       }
00820       if(seenLEDs[ledsubidx]==LED_DIFF) {
00821         //cout << "\t\t" << it->first << " led " << lastLEDState[ledsubidx] << " @ " << get_time() << endl;
00822         leds.push_back(SyncWriteLEDEntry(servoid,lastLEDState[ledsubidx]==LED_ON));
00823       }
00824     }
00825 #endif
00826     
00827     unsigned int idx = it->second->freeSpinOutput;
00828     ServoInfo::RotationMode rm = ServoInfo::CONTINUOUS;
00829     if(it->second->output!=it->second->freeSpinOutput) {
00830       if(it->second->freeSpinOutput>=NumOutputs) {
00831         if(it->second->freeSpinOutput!=UNUSED)
00832           std::cerr << "Warning: Dynamixel driver mapping servo " << it->first << " to invalid free spin output index " << it->second->freeSpinOutput << std::endl;
00833         idx = it->second->output;
00834         rm = ServoInfo::POSITION;
00835       } else if(curBuf[it->second->freeSpinOutput]==0) {
00836         idx = it->second->output;
00837         rm = ServoInfo::POSITION;
00838       }
00839     }
00840     if(idx>=NumOutputs) {
00841       if(idx!=UNUSED)
00842         std::cerr << "Warning: Dynamixel driver mapping servo " << it->first << " to invalid output index " << idx << std::endl;
00843       continue; // invalid/unused servo
00844     }
00845     /*if(it->second->predictedLoad==0) {
00846       // send position if first starting, if changed value, or if rotation mode changed
00847       if(isFirstCheck || lastOutputs[idx]!=curBuf[idx] || rm!=it->second->curRotationMode) {
00848         float speed;
00849         if(rm==ServoInfo::CONTINUOUS) {
00850           // continuous rotation mode, speed specified directly
00851           speed = curBuf[idx];
00852         } else if(lastOutputs[idx]!=curBuf[idx]) {
00853           // here because value changed, speed based on distance to travel
00854           speed = (curBuf[idx] - lastOutputs[idx]) / (period/1000);
00855         } else {
00856           // initial start or change of mode, choose a reasonable speed
00857           speed = .35;
00858         }
00859         packets.push_back(setServo(it, rm, curBuf[idx], speed));
00860       }
00861     } else*/ 
00862     { // subject to load prediction, may need to adjust command
00863       // find current position in servo units
00864       float speed;
00865       if(rm==ServoInfo::CONTINUOUS) {
00866         // continuous rotation mode, speed specified directly
00867         speed = curBuf[idx];
00868       } else if(lastOutputs[idx]!=curBuf[idx]) {
00869         // here because value changed, speed based on distance to travel
00870         speed = (curBuf[idx] - lastOutputs[idx]) / (period/1000);
00871       } else {
00872         // initial start or change of mode, choose a reasonable speed
00873         speed = .35f;
00874       }
00875       unsigned short oldLastCmd = it->second->lastCmd;
00876       DynamixelProtocol::SyncWritePosSpeedEntry packet = setServo(it, rm, curBuf[idx], speed);
00877       // send position if first starting, if changed value, or if rotation mode changed
00878       if(isFirstCheck || oldLastCmd!=it->second->lastCmd || rm!=it->second->curRotationMode) {
00879         packets.push_back(packet);
00880       }
00881     }
00882     
00883     if(rm!=it->second->curRotationMode) {
00884       modes.push_back(SyncWriteContinuousRotationEntry(servoid,rm==ServoInfo::CONTINUOUS));
00885       modeUpdates.push_back(std::make_pair(it->first,rm));
00886     }
00887     
00888     if(havePIDUpdates) {
00889       if(it->second->slope!=pidValues[idx].pids[BioloidInfo::DYNAMIXEL_SLOPE] || it->second->margin!=pidValues[idx].pids[BioloidInfo::DYNAMIXEL_MARGIN]) {
00890         if(it->second->slope!=pidValues[idx].pids[BioloidInfo::DYNAMIXEL_SLOPE]) {
00891           it->second->slope = pidValues[idx].pids[BioloidInfo::DYNAMIXEL_SLOPE];
00892           unsigned short torque = (it->second->slope>0) ? 0x3FF : 0;
00893           // Have to set max torque to zero when we set slope to 0, otherwise it still fights a little.
00894           // Setting torque enable instead doesn't cut it because it apparently gets re-enabled on sensor query
00895           //std::cout << "Max torque " << idx << " set to " << torque << std::endl;
00896           torqueToggles.push_back(SyncWriteTorqueEntry(servoid,torque));
00897         }
00898         it->second->margin = pidValues[idx].pids[BioloidInfo::DYNAMIXEL_MARGIN];
00899         //std::cout << "Slope and margin " << idx << " set to " << pidValues[idx][BioloidInfo::DYNAMIXEL_SLOPE] << ' ' << pidValues[idx][BioloidInfo::DYNAMIXEL_MARGIN] << std::endl;
00900         compliances.push_back(SyncWriteComplianceEntry(servoid,(unsigned char)it->second->slope,(unsigned char)it->second->margin));
00901       }
00902       if(it->second->punch!=pidValues[idx].pids[BioloidInfo::DYNAMIXEL_PUNCH]) {
00903         it->second->punch = pidValues[idx].pids[BioloidInfo::DYNAMIXEL_PUNCH];
00904         //std::cout << "Punch " << idx << " set to " << pidValues[idx][BioloidInfo::DYNAMIXEL_PUNCH] << std::endl;
00905         punches.push_back(SyncWritePunchEntry(servoid,(unsigned char)it->second->punch));
00906       }
00907     }
00908   }
00909   
00910   if(havePIDUpdates) {
00911     dirtyPIDs=0;
00912     pidLock.unlock();
00913   }
00914   
00915   // if no changes, skip update altogether -- but if anything has changed, have to take the lock and send the updates
00916   if(packets.size()>0 || modes.size()>0 || leds.size()>0 || compliances.size()>0 || punches.size()>0) { 
00917     // send rotation mode changes, need to go before corresponding position command
00918     writeSyncEntries(os,modes);
00919     // send positions and speeds
00920     writeSyncEntries(os,packets);
00921     // send LED values
00922     writeSyncEntries(os,leds);
00923     // send servo controller parameters
00924     writeSyncEntries(os,torqueToggles);
00925     writeSyncEntries(os,compliances);
00926     writeSyncEntries(os,punches);
00927     // now dump it on the wire
00928     os.flush();
00929     if(os) {
00930       for(modeUpdates_t::const_iterator it=modeUpdates.begin(); it!=modeUpdates.end(); ++it) {
00931         //printf("\t\t%s Mode switch (%d -> %d)\n",it->first.c_str(),servos[it->first].curRotationMode,it->second);
00932         servos[it->first].curRotationMode=it->second;
00933       }
00934     } else {
00935       std::cerr << "WARNING: DynamixelDriver couldn't write update, bad output stream" << std::endl;
00936     }
00937   }
00938   memcpy(lastOutputs,curBuf,sizeof(lastOutputs));
00939   isFirstCheck=false;
00940 }
00941 
00942 DynamixelProtocol::SyncWritePosSpeedEntry DynamixelDriver::CommThread::setServo(const servo_iterator& servo, ServoInfo::RotationMode rm, float v, float speed) {
00943   const int MAX_CMD = servo->second->maxTic;
00944   const int MIN_CMD = 0;
00945   const int MAX_SPDCMD = 1023;
00946   unsigned int servoIdx = servo->second->servoID;
00947   if(servoIdx>255)
00948     throw std::runtime_error("DynamixelDriver::setServo, bad servo index!");
00949   //unsigned int outputIdx = servo->second->output;
00950   // get output's range in radians
00951   float outRange = servo->second->maxAngle;
00952   // get servo's range
00953   unsigned int servoRange = MAX_CMD - MIN_CMD;
00954   // get commanded position as percent of range of motion
00955   float cmd = (v+servo->second->zeroAngle+outRange/2)/outRange;
00956   // do conversion from radians (output domain) to pulse width (servo domain)
00957   float pw = cmd*servoRange;
00958   // account for servo load deflection
00959   pw -= servo->second->predictedLoad*driver.loadCompensation;
00960   
00961   // round to int
00962   int bpw = static_cast<int>(pw+0.5);
00963   // check bounds
00964   if(bpw<MIN_CMD)
00965     bpw = MIN_CMD;
00966   else if(bpw>MAX_CMD)
00967     bpw = MAX_CMD;
00968   if(servo->second->invertRotation)
00969     bpw = MAX_CMD - (bpw-MIN_CMD);
00970   
00971   // same for speed
00972   int bpwSpeed;
00973   if(rm==ServoInfo::CONTINUOUS) {
00974     if(speed>0)
00975       speed = speed*servo->second->cmdSpeedSlopeP + servo->second->cmdSpeedOffsetP; // apply calibration
00976     else if(speed<0)
00977       speed = speed*servo->second->cmdSpeedSlopeN + servo->second->cmdSpeedOffsetN; // apply calibration
00978     bpwSpeed = static_cast<int>(speed/outRange*servoRange+.5f);
00979     if(bpwSpeed<-MAX_SPDCMD)
00980       bpwSpeed=-MAX_SPDCMD;
00981     if(bpwSpeed>MAX_SPDCMD)
00982       bpwSpeed=MAX_SPDCMD;
00983     if(servo->second->invertRotation)
00984       bpwSpeed=-bpwSpeed;
00985     // 10th bit is turn direction
00986     if(bpwSpeed<0)
00987       bpwSpeed = -bpwSpeed + 1024;
00988   } else {
00989     bpwSpeed = 0; // speed control seems to make it jumpier than just running at maximum... :(
00990     /*if(speed>0)
00991      speed = speed*servo->second->cmdSpeedSlopeP + servo->second->cmdSpeedOffsetP; // apply calibration
00992      else if(speed<0)
00993      speed = speed*servo->second->cmdSpeedSlopeN + servo->second->cmdSpeedOffsetN; // apply calibration
00994      bpwSpeed = abs(static_cast<int>(speed/outRange*servoRange+.5f));
00995      if(bpwSpeed<1)
00996      bpwSpeed=1;
00997      if(bpwSpeed>1023)
00998      bpwSpeed=0; // max speed */
00999   }
01000   // LOW LEVEL LOGGING:
01001   //std::cout << get_time() << "\t" << servoIdx << " POS: " << bpw << " Speed: " << bpwSpeed << std::endl;
01002   
01003   const float GAMMA = 0.9f;
01004   float motion = std::abs(servo->second->lastCmd-bpw);
01005   servo->second->recentCmdMotion*=GAMMA;
01006   if(motion>servo->second->recentCmdMotion)
01007     servo->second->recentCmdMotion=motion;
01008   
01009   servo->second->lastCmd = bpw;
01010   return SyncWritePosSpeedEntry(servoIdx,bpw,bpwSpeed);
01011 }
01012 
01013 /*! @file
01014  * @brief 
01015  * @author Ethan Tira-Thompson (ejt) (Creator)
01016  */

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Tue Jan 31 04:35:31 2012 by Doxygen 1.6.3