00001
00002 #ifndef INCLUDED_Dynamixel_h_
00003 #define INCLUDED_Dynamixel_h_
00004
00005 #include "local/DeviceDriver.h"
00006 #include "local/MotionHook.h"
00007 #include "local/DataSource.h"
00008 #include "local/CommPort.h"
00009 #include "Shared/plist.h"
00010 #include "Shared/plistSpecialty.h"
00011 #include "Shared/get_time.h"
00012 #include "IPC/Thread.h"
00013 #include "IPC/FailsafeThread.h"
00014 #include "IPC/DriverMessaging.h"
00015 #include "DynamixelProtocol.h"
00016 #include <iostream>
00017 #include <sstream>
00018 #include <iomanip>
00019
00020
00021 class DynamixelDriver : public virtual DeviceDriver, public virtual DriverMessaging::Listener, public MotionHook, public DataSource, public virtual plist::PrimitiveListener {
00022 public:
00023 static const int START_SERVO_ID = 1;
00024 static const unsigned int UNUSED = plist::OutputSelector::UNUSED;
00025
00026
00027 enum ServoParam_t {
00028 DYNAMIXEL_SLOPE=0,
00029 DYNAMIXEL_PUNCH,
00030 DYNAMIXEL_MARGIN
00031 };
00032
00033 explicit DynamixelDriver(const std::string& name)
00034 : DeviceDriver(autoRegisterDynamixelDriver,name), DriverMessaging::Listener(), MotionHook(), DataSource(),
00035 servos(), commName(), loadCompensation(28.f), commLatency(16), numPoll(3), responseTime(4000), servoIDSync(),
00036 commThread(servos,commName,*this), motionActive(false), sensorsActive(false), lastSensor()
00037 {
00038
00039 VOLTAGE_SENSOR_OFFSET = capabilities.findSensorOffset("PowerVoltage");
00040 TEMP_SENSOR_OFFSET = capabilities.findSensorOffset("PowerThermo");
00041
00042 addEntry("Servos",servos,"Maps servo IDs to Tekkotsu output offsets, use command line new/delete commands to add/remove mappings.");
00043 addEntry("CommPort",commName,"Name of the CommPort to use, generally a SerialCommPort with direct binary TTL communication with the servos");
00044 addEntry("LoadCompensation",loadCompensation,"Ratio of load to deflection, if non-zero will use LoadPrediction messages and attempt to counter anticipated loads");
00045 addEntry("CommLatency",commLatency,"The delay (in MILLIseconds, ms) to get a response from the servos due to buffering in the USB-to-TTL interface.");
00046 addEntry("NumPoll",numPoll,"Number of sensor queries to send before reading responses. This lets us front-load the queries so we can get multiple responses per read buffer flush period");
00047 addEntry("ResponseTime",responseTime,"The amount of time (in MICROseconds, µs) to wait between sending a sensor query and sending another when front-loading the buffer.\nIf this is too short, the next query will collide with the previous response.");
00048
00049 for(unsigned int i=0; i<NumPIDJoints; ++i) {
00050 std::stringstream bio_id;
00051 bio_id << std::setw(3) << std::setfill('0') << (i+START_SERVO_ID);
00052 servos[bio_id.str()]=ServoInfo(i+START_SERVO_ID);
00053 }
00054 servos.addCollectionListener(&servoIDSync);
00055 DriverMessaging::addListener(this,DriverMessaging::LoadPrediction::NAME);
00056 DriverMessaging::addListener(this,DriverMessaging::SensorPriority::NAME);
00057 }
00058 virtual ~DynamixelDriver() {
00059 servos.removeCollectionListener(&servoIDSync);
00060 DriverMessaging::removeListener(this,DriverMessaging::SensorPriority::NAME);
00061 DriverMessaging::removeListener(this,DriverMessaging::LoadPrediction::NAME);
00062 }
00063
00064 virtual std::string getClassName() const { return autoRegisterDynamixelDriver; }
00065
00066 virtual MotionHook* getMotionSink() { return dynamic_cast<MotionHook*>(this); }
00067 virtual void getSensorSources(std::map<std::string,DataSource*>& sources) {
00068 sources.clear();
00069 sources["Sensors"]=dynamic_cast<DataSource*>(this);
00070 }
00071
00072 virtual void motionStarting();
00073 virtual bool isConnected();
00074 virtual void motionStopping();
00075 virtual void motionCheck(const float outputs[][NumOutputs]);
00076 virtual void updatePIDs(const std::vector<MotionHook::PIDUpdate>& pids);
00077 virtual void registerSource();
00078 virtual void deregisterSource();
00079 virtual void enteringRealtime(const plist::Primitive<double>& simTimeScale) { DataSource::enteringRealtime(simTimeScale); }
00080 virtual void leavingRealtime(bool isFullSpeed) { DataSource::leavingRealtime(isFullSpeed); }
00081
00082 virtual unsigned int nextTimestamp();
00083 virtual const std::string& nextName() { return instanceName; }
00084 virtual bool advance();
00085
00086 virtual void plistValueChanged(const plist::PrimitiveBase& pl);
00087
00088 virtual void processDriverMessage(const DriverMessaging::Message& d);
00089
00090 struct ServoInfo : public virtual plist::Dictionary {
00091 ServoInfo(unsigned int i=DynamixelProtocol::INVALID_ID)
00092 : plist::Dictionary(false), detected(false), output(), led(), freeSpinOutput(), invertRotation(false), zeroAngle(0), maxTic(0), maxAngle(0),
00093 cmdSpeedSlopeP(0.6575f), cmdSpeedOffsetP(0.1557f), cmdSpeedSlopeN(0.6554f), cmdSpeedOffsetN(-0.1256f),
00094 repSpeedSlope(0.111f / 60 * 2 * (float)M_PI),
00095 leftIRDistOffset(), centerIRDistOffset(), rightIRDistOffset(),
00096 leftLuminosityOffset(), centerLuminosityOffset(), rightLuminosityOffset(),
00097 micVolumeOffset(), micSpikeCountOffset(),
00098 servoID(i), slope(0), punch(0), margin(0), curRotationMode(UNKNOWN), lastCmd(), recentCmdMotion(0), failures(0),
00099 predictedLoad(0), sensorPriority(-1), sensorActivation(1), micSpikeFrameNumber(-1U), model(DynamixelProtocol::MODEL_UNKNOWN), modelName()
00100 {
00101 i-=START_SERVO_ID;
00102 #ifdef TGT_HAS_WHEELS
00103 if(WheelOffset<PIDJointOffset || WheelOffset>=PIDJointOffset+NumPIDJoints) {
00104
00105 if(i<(int)NumWheels) {
00106 output = ( i+WheelOffset );
00107 freeSpinOutput = ( i+WheelOffset );
00108 } else {
00109 output = ( (i-NumWheels<(int)NumPIDJoints) ? i-NumWheels+PIDJointOffset : UNUSED );
00110 }
00111 } else {
00112
00113 output = ( (i<NumPIDJoints) ? i+PIDJointOffset : UNUSED);
00114 if(i>=(int)WheelOffset && i<(int)(WheelOffset+NumWheels)) {
00115 freeSpinOutput = ( i+PIDJointOffset );
00116 }
00117 }
00118 #else
00119 output = ( (i<NumPIDJoints) ? i+PIDJointOffset : UNUSED);
00120 #endif
00121 #ifdef TGT_HAS_LEDS
00122 led = (i<NumLEDs ? i+LEDOffset : UNUSED);
00123 #endif
00124
00125 plist::NamedEnumeration<SensorOffset_t>::setNames(sensorNames);
00126 plist::NamedEnumeration<SensorOffset_t>::addNameForVal("UNUSED",static_cast<SensorOffset_t>(-1));
00127 leftIRDistOffset.set(capabilities.findSensorOffset("LeftIRDist"));
00128 centerIRDistOffset.set(capabilities.findSensorOffset("CenterIRDist"));
00129 rightIRDistOffset.set(capabilities.findSensorOffset("RightIRDist"));
00130 leftLuminosityOffset.set(capabilities.findSensorOffset("LeftLuminosity"));
00131 centerLuminosityOffset.set(capabilities.findSensorOffset("CenterLuminosity"));
00132 rightLuminosityOffset.set(capabilities.findSensorOffset("RightLuminosity"));
00133 micVolumeOffset.set(capabilities.findSensorOffset("MicVolume"));
00134 micSpikeCountOffset.set(capabilities.findSensorOffset("MicSpikeCount"));
00135
00136 setLoadSavePolicy(FIXED,SYNC);
00137 initEntries();
00138 }
00139
00140 void setModel(DynamixelProtocol::ModelID_t m) { if(model!=m) { model=m; initEntries(); } }
00141 DynamixelProtocol::ModelID_t getModel() const { return model; }
00142 void setModelName(const std::string m) { modelName=m; }
00143 const std::string& getModelName() const { return modelName; }
00144
00145
00146 bool hasSensorOffset() const {
00147 const SensorOffset_t US = static_cast<SensorOffset_t>(-1);
00148 return leftIRDistOffset!=US || centerIRDistOffset!=US || rightIRDistOffset!=US
00149 || leftLuminosityOffset!=US || centerLuminosityOffset!=US || rightLuminosityOffset!=US
00150 || micVolumeOffset!=US || micSpikeCountOffset!=US;
00151 }
00152
00153 plist::Primitive<bool> detected;
00154
00155
00156 plist::OutputSelector output;
00157 plist::OutputSelector led;
00158 plist::OutputSelector freeSpinOutput;
00159
00160 plist::Primitive<bool> invertRotation;
00161 plist::Angle zeroAngle;
00162 plist::Primitive<unsigned int> maxTic;
00163 plist::Angle maxAngle;
00164 plist::Primitive<float> cmdSpeedSlopeP;
00165 plist::Primitive<float> cmdSpeedOffsetP;
00166 plist::Primitive<float> cmdSpeedSlopeN;
00167 plist::Primitive<float> cmdSpeedOffsetN;
00168 plist::Primitive<float> repSpeedSlope;
00169
00170
00171 plist::NamedEnumeration<SensorOffset_t> leftIRDistOffset;
00172 plist::NamedEnumeration<SensorOffset_t> centerIRDistOffset;
00173 plist::NamedEnumeration<SensorOffset_t> rightIRDistOffset;
00174 plist::NamedEnumeration<SensorOffset_t> leftLuminosityOffset;
00175 plist::NamedEnumeration<SensorOffset_t> centerLuminosityOffset;
00176 plist::NamedEnumeration<SensorOffset_t> rightLuminosityOffset;
00177 plist::NamedEnumeration<SensorOffset_t> micVolumeOffset;
00178 plist::NamedEnumeration<SensorOffset_t> micSpikeCountOffset;
00179
00180 unsigned int servoID;
00181 float slope;
00182 float punch;
00183 float margin;
00184 enum RotationMode { POSITION, CONTINUOUS, UNKNOWN } curRotationMode;
00185 unsigned short lastCmd;
00186 float recentCmdMotion;
00187 unsigned short failures;
00188 float predictedLoad;
00189 float sensorPriority;
00190 float sensorActivation;
00191 unsigned int micSpikeFrameNumber;
00192
00193 DynamixelProtocol::ModelID_t model;
00194 plist::Primitive<std::string> modelName;
00195 protected:
00196 void initEntries() {
00197 using namespace DynamixelProtocol;
00198 clear();
00199
00200 addEntry("Model",modelName,"Model name for this device; set automatically via pingServos");
00201 addEntry("Detected",detected,"If true, servo is detected and functioning. If a servo is reconnected, set back to true to attempt to re-enable communication.");
00202
00203 if(model!=MODEL_AXS1 || model==MODEL_UNKNOWN) {
00204 addEntry("Output",output,"Tekkotsu offset to pull servo positions from; -1 or empty string to mark unused\n"
00205 "(May still rotate via FreeSpinOutput if position control by Output is unused...)");
00206 addEntry("LED",led,"Tekkotsu offset to pull LED values from; -1 or empty string to mark unused");
00207 addEntry("FreeSpinOutput",freeSpinOutput,"Tekkotsu output to control the 'continuous rotation mode'. If specified output's\n"
00208 "value is non-zero, the value from Output is ignored, and the servo is rotated at the\n"
00209 "specified speed. If Output and FreeSpinOutput are equal, then the servo is permanently\n"
00210 "in continuous rotation mode.");
00211
00212 addEntry("InvertRotation",invertRotation,"If true, the servo moves in the reverse direction. Calibration parameters are always applied relative to the 'native' orientation.");
00213 addEntry("ZeroAngle",zeroAngle,"Specifies the angle to which the zero point of the output will be mapped.\n"
00214 "For an AX or RX servo, this must be in the range ±2.618 radians (i.e. ±150°), such that\n"
00215 "a value of zero is centered in the range of motion.");
00216 maxTic = IS_MXEX(model) ? 4095 : 1023 ;
00217 maxAngle = IS_MXEX(model) ? (360*(float)M_PI/180) : (300*(float)M_PI/180);
00218 addEntry("MaxTic",maxTic,"The maximum servo tic value (e.g. 1023 for AX and RX, 4095 for EX-106 and MX series)");
00219 addEntry("MaxAngle",maxAngle,"The maximum servo angle (e.g. 300° for AX and RX, 280.6° for EX-106, 360° for MX series )");
00220 addEntry("CmdSpeedSlopePos",cmdSpeedSlopeP,"Calibration parameter for converting desired physical positive speeds to speed command for the servo.");
00221 addEntry("CmdSpeedOffsetPos",cmdSpeedOffsetP,"Calibration parameter for converting desired physical positive speeds to speed command for the servo.");
00222 addEntry("CmdSpeedSlopeNeg",cmdSpeedSlopeN,"Calibration parameter for converting desired physical negative speeds to speed command for the servo.");
00223 addEntry("CmdSpeedOffsetNeg",cmdSpeedOffsetN,"Calibration parameter for converting desired physical negative speeds to speed command for the servo.");
00224 addEntry("ReportedSpeedSlope",repSpeedSlope,"Calibration parameter for converting speed feedback from servo to actual physical speed.");
00225 }
00226
00227 if(model==MODEL_AXS1 || model==MODEL_UNKNOWN) {
00228 addEntry("LeftIRDistOffset",leftIRDistOffset,"Tekkotsu sensor offset where the left IR distance reading should be stored.");
00229 addEntry("CenterIRDistOffset",centerIRDistOffset,"Tekkotsu sensor offset where the center IR distance reading should be stored.");
00230 addEntry("RightIRDistOffset",rightIRDistOffset,"Tekkotsu sensor offset where the right IR distance reading should be stored.");
00231 addEntry("LeftLuminosityOffset",leftLuminosityOffset,"Tekkotsu sensor offset where the left luminosity reading should be stored.");
00232 addEntry("CenterLuminosityOffset",centerLuminosityOffset,"Tekkotsu sensor offset where the center luminosity reading should be stored.");
00233 addEntry("RightLuminosityOffset",rightLuminosityOffset,"Tekkotsu sensor offset where the right luminosity reading should be stored.");
00234 addEntry("MicVolumeOffset",micVolumeOffset,"Tekkotsu sensor offset where the volume magnitude reading should be stored.");
00235 addEntry("MicSpikeCountOffset",micSpikeCountOffset,"Tekkotsu sensor offset where the volume spike count should be stored.");
00236 }
00237 }
00238
00239 };
00240
00241 plist::DictionaryOf< ServoInfo > servos;
00242 typedef plist::DictionaryOf< ServoInfo >::const_iterator servo_iterator;
00243
00244 plist::Primitive<std::string> commName;
00245 plist::Primitive<float> loadCompensation;
00246 plist::Primitive<unsigned int> commLatency;
00247 plist::Primitive<unsigned int> numPoll;
00248 plist::Primitive<unsigned int> responseTime;
00249
00250 protected:
00251 static unsigned int VOLTAGE_SENSOR_OFFSET;
00252 static unsigned int TEMP_SENSOR_OFFSET;
00253
00254 struct ServoInfoIDSync : plist::CollectionListener {
00255 virtual void plistCollectionEntryAdded(plist::Collection& col, ObjectBase&) { plistCollectionEntriesChanged(col); }
00256 virtual void plistCollectionEntriesChanged(plist::Collection& col) {
00257 plist::DictionaryOf< ServoInfo >& s = dynamic_cast<plist::DictionaryOf<ServoInfo>& > (col);
00258 for(servo_iterator it=s.begin(); it!=s.end(); ++it)
00259 it->second->servoID = atoi(it->first.c_str());
00260 }
00261 } servoIDSync;
00262
00263 class CommThread : public Thread, public plist::CollectionListener {
00264 public:
00265 CommThread(plist::DictionaryOf< ServoInfo >& servoList, const plist::Primitive<std::string>& comm, DynamixelDriver& parent)
00266 : Thread(), pidLock(), dirtyPIDs(0), commName(comm), servos(servoList), driver(parent), servoPollQueue(),
00267 failsafe(*this,FrameTime*NumFrames*3/2000.0,false), continuousUpdates(), updated(false), responsePending(false), lastSensorTime(0),
00268 servoDeflection(0), isFirstCheck(true), timestampBufA(0), timestampBufB(0), timestampBufC(0), curBuf(NULL)
00269 {
00270 failsafe.restartFlag=true;
00271 for(unsigned int i=0; i<NumLEDs; ++i)
00272 lastLEDState[i]=LED_UNKNOWN;
00273 servos.addCollectionListener(this);
00274 plistCollectionEntriesChanged(servos);
00275 }
00276 ~CommThread() { servos.removeCollectionListener(this); }
00277
00278 virtual void plistCollectionEntryAdded(Collection& , ObjectBase& primitive) {
00279 if(ServoInfo* si = dynamic_cast<ServoInfo*>(&primitive)) {
00280 servoPollQueue.push_back(si);
00281 si->detected=true;
00282 if(isStarted())
00283 driver.pingServos(true);
00284 }
00285 }
00286 virtual void plistCollectionEntryRemoved(Collection& , ObjectBase& primitive) {
00287 ServoInfo* si = dynamic_cast<ServoInfo*>(&primitive);
00288 std::vector<ServoInfo*>::iterator it = std::find(servoPollQueue.begin(),servoPollQueue.end(),si);
00289 if(it!=servoPollQueue.end())
00290 servoPollQueue.erase(it);
00291 }
00292 virtual void plistCollectionEntriesChanged(Collection& ) {
00293 servoPollQueue.resize(servos.size());
00294 unsigned int i=0;
00295 for(plist::DictionaryOf< ServoInfo >::const_iterator it=servos.begin(); it!=servos.end(); ++it) {
00296 servoPollQueue[i++] = it->second;
00297 if(isStarted())
00298 it->second->detected=true;
00299 }
00300 if(isStarted())
00301 driver.pingServos(true);
00302 }
00303
00304 virtual void start() { updated=false; continuousUpdates=true; Thread::start(); }
00305 virtual void startOneUpdate() { updated=false; continuousUpdates=false; Thread::start(); }
00306 virtual Thread& stop();
00307 virtual void waitForUpdate();
00308 virtual bool isStarted() const { return Thread::isStarted() || failsafe.isStarted(); }
00309 bool takeUpdate() { if(!updated) { return false; } else { updated=false; return true; } }
00310
00311
00312 float* getWriteBuffer();
00313
00314 void setWriteBufferTimestamp(float * buf);
00315
00316 unsigned int nextTimestamp() const { return lastSensorTime + driver.commLatency; }
00317
00318 Thread::Lock pidLock;
00319 PIDUpdate pidValues[NumOutputs];
00320 unsigned int dirtyPIDs;
00321
00322 protected:
00323 virtual bool launched();
00324 virtual void cancelled();
00325 void clearBuffer();
00326 virtual unsigned int runloop();
00327 template<class T> void readResponse(T& response, std::istream& is, ServoInfo& module);
00328 virtual void updateCommands(std::istream& is, std::ostream& os);
00329
00330
00331 DynamixelProtocol::SyncWritePosSpeedEntry setServo(const servo_iterator& servo, ServoInfo::RotationMode rm, float v, float speed);
00332
00333 const plist::Primitive<std::string>& commName;
00334 plist::DictionaryOf< ServoInfo >& servos;
00335 DynamixelDriver& driver;
00336 std::vector<ServoInfo*> servoPollQueue;
00337 FailsafeThread failsafe;
00338 bool continuousUpdates;
00339 bool updated;
00340 bool responsePending;
00341 unsigned int lastSensorTime;
00342 float servoDeflection;
00343
00344 bool isFirstCheck;
00345 float outputBufA[NumOutputs];
00346 unsigned int timestampBufA;
00347 float outputBufB[NumOutputs];
00348 unsigned int timestampBufB;
00349 float outputBufC[NumOutputs];
00350 unsigned int timestampBufC;
00351 float lastOutputs[NumOutputs];
00352 float * curBuf;
00353
00354
00355 inline bool calcLEDValue(unsigned int i,float x) {
00356 if(x<=0.0) {
00357 ledActivation[i]*=.9f;
00358 return false;
00359 } else if(x>=1.0) {
00360 return true;
00361 } else {
00362 float x3 = x*x*x;
00363 x = .25f*x3*x3 + .75f*x;
00364 ledActivation[i]+=x;
00365 if(ledActivation[i]>=1) {
00366 ledActivation[i]-=1;
00367 return true;
00368 } else {
00369 return false;
00370 }
00371 }
00372 }
00373 float ledActivation[NumLEDs];
00374 enum LedState { LED_OFF=0, LED_ON, LED_UNKNOWN } lastLEDState[NumLEDs];
00375
00376 private:
00377 CommThread(const CommThread&);
00378 CommThread& operator=(const CommThread&);
00379 } commThread;
00380
00381 void doFreeze();
00382 void doUnfreeze();
00383
00384
00385 static void provideValues(const ServoInfo& info, const DynamixelProtocol::ServoSensorsResponse& response);
00386
00387 static void provideValues(ServoInfo& info, const DynamixelProtocol::AXS1SensorsResponse& response);
00388
00389
00390 void provideOutput(unsigned int idx) { if(idx<NumOutputs) providingOutput(idx); }
00391
00392 void ignoreOutput(unsigned int idx) { if(idx<NumOutputs) ignoringOutput(idx); }
00393
00394
00395 void pingServos(bool detectedOnly=false);
00396
00397
00398 void sendZeroTorqueCmd(CommPort& comm);
00399
00400
00401 template<class T> static void writeSyncEntries(std::ostream& os, const std::vector<T>& entries) {
00402 if(entries.size()==0)
00403 return;
00404 unsigned char checksum=0;
00405 DynamixelProtocol::write(os, DynamixelProtocol::SyncWriteHeader<T>(entries.size()), checksum);
00406 os.write(entries[0],entries.size()*sizeof(T));
00407 checksum+=DynamixelProtocol::nchecksum(entries[0],entries.size()*sizeof(T));
00408 os.put(~checksum);
00409 }
00410
00411 bool motionActive;
00412 bool sensorsActive;
00413 std::string lastSensor;
00414
00415 private:
00416
00417 static const std::string autoRegisterDynamixelDriver;
00418 };
00419
00420 template<class T> void DynamixelDriver::CommThread::readResponse(T& response, std::istream& is, ServoInfo& module) {
00421 do {
00422 testCancel();
00423 while(!DynamixelProtocol::readResponse(is,response,module.output)) {
00424
00425
00426
00427 failsafe.progressFlag=true;
00428 testCancel();
00429 }
00430 } while(response.servoid!=module.servoID);
00431
00432 module.failures=0;
00433 module.sensorActivation=0;
00434
00435
00436
00437
00438 provideValues(module, response);
00439 }
00440
00441
00442
00443
00444
00445
00446
00447 #endif