Libbarrett
1.2.4
|
00001 00026 /* 00027 * @file low_level_wam-inl.h 00028 * @date 11/02/2010 00029 * @author Chris Dellin 00030 * @author Dan Cody 00031 * 00032 */ 00033 00034 00035 #include <stdexcept> 00036 #include <vector> 00037 #include <limits> 00038 #include <cmath> 00039 #include <cassert> 00040 00041 #include <boost/static_assert.hpp> 00042 #include <Eigen/LU> 00043 #include <libconfig.h++> 00044 00045 #include <barrett/os.h> 00046 #include <barrett/detail/stl_utils.h> 00047 #include <barrett/units.h> 00048 #include <barrett/products/puck.h> 00049 #include <barrett/products/motor_puck.h> 00050 #include <barrett/products/safety_module.h> 00051 #include <barrett/products/puck_group.h> 00052 #include <barrett/products/abstract/multi_puck_product.h> 00053 00054 #include <barrett/products/low_level_wam.h> 00055 00056 00057 namespace barrett { 00058 00059 00060 template<size_t DOF> 00061 const enum Puck::Property LowLevelWam<DOF>::props[] = { Puck::P, Puck::T }; 00062 00063 00064 template<size_t DOF> 00065 LowLevelWam<DOF>::LowLevelWam(const std::vector<Puck*>& _pucks, SafetyModule* _safetyModule, const libconfig::Setting& setting, std::vector<int> torqueGroupIds) : 00066 MultiPuckProduct(DOF, _pucks, PuckGroup::BGRP_WAM, props, sizeof(props)/sizeof(props[0]), "LowLevelWam::LowLevelWam()"), 00067 safetyModule(_safetyModule), torqueGroups(), 00068 home(setting["home"]), j2mp(setting["j2mp"]), 00069 noJointEncoders(true), positionSensor(PS_MOTOR_ENCODER), 00070 lastUpdate(0.0), torquePropId(group.getPropertyId(Puck::T)) 00071 { 00072 logMessage(" Config setting: %s => \"%s\"") % setting.getSourceFile() % setting.getPath(); 00073 00074 00075 group.setProperty(Puck::MODE, MotorPuck::MODE_IDLE); // Make sure the Pucks are IDLE 00076 00077 00078 // Zero-compensation? 00079 bool zeroCompensation = setting.exists("zeroangle"); 00080 if (!zeroCompensation) { 00081 logMessage(" Missing \"zeroangle\" vector: no zero-compensation"); 00082 } 00083 00084 00085 // Compute motor/joint transforms 00086 Eigen::LU<typename sqm_type::Base> lu(j2mp); 00087 if (!lu.isInvertible()) { 00088 (logMessage("LowLevelWam::%s(): j2mp matrix is not invertible.") 00089 % __func__).template raise<std::runtime_error>(); 00090 } 00091 lu.computeInverse(&m2jp); 00092 j2mt = m2jp.transpose(); 00093 00094 00095 // Setup torque groups 00096 // If no IDs are provided, use the defaults 00097 if (torqueGroupIds.size() == 0) { 00098 torqueGroupIds.push_back(PuckGroup::BGRP_LOWER_WAM); 00099 torqueGroupIds.push_back(PuckGroup::BGRP_UPPER_WAM); 00100 } 00101 size_t numTorqueGroups = ceil(static_cast<double>(DOF)/MotorPuck::PUCKS_PER_TORQUE_GROUP); 00102 if (numTorqueGroups > torqueGroupIds.size()) { 00103 (logMessage("LowLevelWam::%s(): Too few torque group IDs. " 00104 "Need %d torque groups, only %d IDs provided.") 00105 % __func__ % numTorqueGroups % torqueGroupIds.size()) 00106 .template raise<std::logic_error>(); 00107 } 00108 00109 size_t i = 0; 00110 for (size_t g = 0; g < numTorqueGroups; ++g) { 00111 std::vector<Puck*> tgPucks; 00112 while (tgPucks.size() < 4 && i < DOF) { 00113 tgPucks.push_back(pucks[i]); 00114 ++i; 00115 } 00116 torqueGroups.push_back(new PuckGroup(torqueGroupIds[g], tgPucks)); 00117 } 00118 00119 00120 // Compute puck/joint transforms 00121 // (See DC notebook 1p25) 00122 v_type cpr; 00123 for (size_t i = 0; i < DOF; ++i) { 00124 cpr[i] = motorPucks[i].getCountsPerRad(); 00125 } 00126 j2pp = cpr.asDiagonal() * j2mp; 00127 00128 v_type rpc; 00129 for (size_t i = 0; i < DOF; ++i) { 00130 rpc[i] = motorPucks[i].getRadsPerCount(); 00131 } 00132 p2jp = m2jp * rpc.asDiagonal(); 00133 00134 v_type ipnm; 00135 for (size_t i = 0; i < DOF; ++i) { 00136 ipnm[i] = motorPucks[i].getIpnm(); 00137 } 00138 j2pt = ipnm.asDiagonal() * j2mt; 00139 00140 00141 // Zero the WAM? 00142 if (safetyModule == NULL) { 00143 logMessage(" No safetyModule: WAM might not be zeroed"); 00144 } else if (safetyModule->wamIsZeroed()) { 00145 logMessage(" WAM was already zeroed"); 00146 } else if (zeroCompensation) { 00147 v_type zeroAngle(setting["zeroangle"]); 00148 00149 v_type currentAngle; 00150 for (size_t i = 0; i < DOF; ++i) { 00151 currentAngle[i] = motorPucks[i].counts2rad(pucks[i]->getProperty(Puck::MECH)); 00152 } 00153 00154 v_type errorAngle = (j2mp*home + zeroAngle) - currentAngle; 00155 for (size_t i = 0; i < DOF; ++i) { 00156 while (errorAngle[i] > M_PI) { 00157 errorAngle[i] -= 2*M_PI; 00158 } 00159 while (errorAngle[i] < -M_PI) { 00160 errorAngle[i] += 2*M_PI; 00161 } 00162 } 00163 00164 // Check for exclusions 00165 for (size_t i = 0; i < DOF; ++i) { 00166 // If VERS < 118, then nothing useful is exposed on MECH; don't compensate 00167 if (pucks[i]->getVers() < 118) { 00168 logMessage(" No zero-compensation for Puck %d: old firmware") % pucks[i]->getId(); 00169 errorAngle[i] = 0; 00170 continue; 00171 } 00172 00173 // If not ROLE & 256, then it's not an absolute encoder; don't compensate 00174 if ( !(pucks[i]->hasOption(Puck::RO_MagEncOnSerial)) ) { 00175 logMessage(" No zero-compensation for Puck %d: no absolute encoder") % pucks[i]->getId(); 00176 errorAngle[i] = 0; 00177 continue; 00178 } 00179 00180 // If the calibration data is out of range, don't compensate 00181 if (zeroAngle[i] > 2*M_PI || zeroAngle[i] < 0) { 00182 logMessage(" No zero-compensation for Puck %d: bad calibration data") % pucks[i]->getId(); 00183 errorAngle[i] = 0; 00184 continue; 00185 } 00186 } 00187 00188 definePosition(home - m2jp*errorAngle); 00189 logMessage(" WAM zeroed with zero-compensation"); 00190 } else { 00191 definePosition(home); 00192 logMessage(" WAM zeroed without zero-compensation"); 00193 } 00194 00195 00196 // Joint encoders 00197 setPositionSensor(PS_MOTOR_ENCODER); // Use motor encoders by default 00198 int numJe = 0; 00199 int numInitializedJe = 0; 00200 00201 for (size_t i = 0; i < DOF; ++i) { 00202 if (pucks[i]->hasOption(Puck::RO_OpticalEncOnEnc)) { 00203 ++numJe; 00204 if (motorPucks[i].foundIndexPulse()) { 00205 ++numInitializedJe; 00206 } 00207 } 00208 } 00209 00210 // If there are joint encoders, look up counts per revolution from the config file 00211 if (numJe != 0) { 00212 noJointEncoders = false; 00213 logMessage(" Found %d joint encoders (%d are initialized)") % numJe % numInitializedJe; 00214 00215 v_type jointEncoderCpr(setting["joint_encoder_counts"]); 00216 for (size_t i = 0; i < DOF; ++i) { 00217 if (jointEncoderCpr[i] == 0.0) { 00218 jointEncoder2jp[i] = 0.0; 00219 } else { 00220 jointEncoder2jp[i] = 2*M_PI / jointEncoderCpr[i]; 00221 } 00222 } 00223 } 00224 00225 00226 // Prevent velocity faults on startup due to WAM motion while no program was running. 00227 if (safetyModule != NULL) { 00228 safetyModule->ignoreNextVelocityFault(); 00229 } 00230 00231 00232 // Get good initial values for jp_1 and lastUpdate 00233 update(); 00234 jv_best.setZero(); 00235 } 00236 00237 template<size_t DOF> 00238 LowLevelWam<DOF>::~LowLevelWam() 00239 { 00240 detail::purge(torqueGroups); 00241 } 00242 00243 00244 template<size_t DOF> 00245 inline const typename LowLevelWam<DOF>::jp_type& LowLevelWam<DOF>::getJointPositions(enum PositionSensor sensor) const { 00246 switch (sensor) { 00247 case PS_MOTOR_ENCODER: 00248 return jp_motorEncoder; 00249 break; 00250 case PS_JOINT_ENCODER: 00251 return jp_jointEncoder; 00252 break; 00253 default: 00254 return jp_best; 00255 } 00256 } 00257 00258 template<size_t DOF> 00259 void LowLevelWam<DOF>::setPositionSensor(enum PositionSensor sensor) 00260 { 00261 switch (sensor) { 00262 case PS_MOTOR_ENCODER: 00263 useJointEncoder.assign(false); 00264 break; 00265 case PS_JOINT_ENCODER: 00266 if ( !hasJointEncoders() ) { 00267 (logMessage("LowLevelWam::%s: This WAM is not equipped with joint encoders.") 00268 % __func__).template raise<std::logic_error>(); 00269 } 00270 for (size_t i = 0; i < DOF; ++i) { 00271 if (pucks[i]->hasOption(Puck::RO_OpticalEncOnEnc) && motorPucks[i].foundIndexPulse()) { 00272 useJointEncoder[i] = true; 00273 } else { 00274 useJointEncoder[i] = false; 00275 } 00276 } 00277 break; 00278 default: 00279 (logMessage("LowLevelWam::%s: Bad sensor value: %d") 00280 % __func__ % sensor).template raise<std::logic_error>(); 00281 break; 00282 } 00283 00284 positionSensor = sensor; 00285 } 00286 00287 00288 template<size_t DOF> 00289 void LowLevelWam<DOF>::update() 00290 { 00291 double now = highResolutionSystemTime(); 00292 00293 if (noJointEncoders) { 00294 group.getProperty<MotorPuck::MotorPositionParser<double> >(Puck::P, pp.data(), true); 00295 jp_motorEncoder = p2jp * pp; // Convert from Puck positions to joint positions 00296 jp_best = jp_motorEncoder; 00297 } else { 00298 // Make sure the reinterpret_cast below makes sense. 00299 BOOST_STATIC_ASSERT(sizeof(MotorPuck::CombinedPositionParser<double>::result_type) == 2*sizeof(double)); 00300 00301 // PuckGroup::getProperty() will fill pp_jep.data() with 2*DOF doubles: 00302 // Primary Encoder 1, Secondary Encoder 1, Primary Encoder 2, Secondary Encoder 2, ... 00303 group.getProperty<MotorPuck::CombinedPositionParser<double> >( 00304 Puck::P, 00305 reinterpret_cast<MotorPuck::CombinedPositionParser<double>::result_type*>(pp_jep.data()), 00306 true); 00307 jp_motorEncoder = p2jp * pp_jep.col(0); 00308 00309 for (size_t i = 0; i < DOF; ++i) { 00310 if (pp_jep(i,1) == std::numeric_limits<double>::max()) { 00311 jp_jointEncoder[i] = 0.0; 00312 jp_best[i] = jp_motorEncoder[i]; 00313 } else { 00314 jp_jointEncoder[i] = jointEncoder2jp[i] * pp_jep(i,1); 00315 if (useJointEncoder[i]) { 00316 jp_best[i] = jp_jointEncoder[i]; 00317 } else { 00318 jp_best[i] = jp_motorEncoder[i]; 00319 } 00320 } 00321 } 00322 } 00323 00324 jv_best = (jp_best - jp_best_1) / (now - lastUpdate); 00325 // TODO(dc): Detect unreasonably large velocities 00326 00327 jp_best_1 = jp_best; 00328 lastUpdate = now; 00329 } 00330 00331 template<size_t DOF> 00332 void LowLevelWam<DOF>::setTorques(const jt_type& jt) 00333 { 00334 // Get around C++ address-of-static-member weirdness... 00335 static const size_t PUCKS_PER_TORQUE_GROUP = MotorPuck::PUCKS_PER_TORQUE_GROUP; 00336 00337 pt = j2pt * jt; // Convert from joint torques to Puck torques 00338 00339 size_t i = 0; 00340 for (size_t g = 0; g < torqueGroups.size(); ++g) { 00341 MotorPuck::sendPackedTorques(bus, torqueGroups[g]->getId(), torquePropId, pt.data()+i, std::min(PUCKS_PER_TORQUE_GROUP, DOF-i)); 00342 i += PUCKS_PER_TORQUE_GROUP; 00343 } 00344 } 00345 00346 template<size_t DOF> 00347 void LowLevelWam<DOF>::definePosition(const jp_type& jp) 00348 { 00349 // Tell the safety logic to ignore the next velocity fault 00350 // (the position will appear to change rapidly) 00351 if (safetyModule != NULL) { 00352 safetyModule->ignoreNextVelocityFault(); 00353 } 00354 00355 pp = j2pp * jp; // Convert from joint positions to Puck positions 00356 00357 { 00358 // Synchronize with execution-cycle 00359 BARRETT_SCOPED_LOCK(bus.getMutex()); 00360 for (size_t i = 0; i < DOF; ++i) { 00361 pucks[i]->setProperty(Puck::P, floor(pp[i])); 00362 } 00363 } 00364 00365 // Record the fact that the WAM has been zeroed 00366 if (safetyModule != NULL) { 00367 safetyModule->setWamZeroed(); 00368 } 00369 } 00370 00371 00372 }