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

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Mon May 9 05:01:38 2016 by Doxygen 1.6.3