Libbarrett  1.2.4
include/barrett/products/detail/low_level_wam-inl.h
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Defines