Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

robot/ManipulatorJointTrajectory.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002   Copyright (C)2003 David Jung <opensim@pobox.com>
00003 
00004   This program/file is free software; you can redistribute it and/or modify
00005   it under the terms of the GNU General Public License as published by
00006   the Free Software Foundation; either version 2 of the License, or
00007   (at your option) any later version.
00008   
00009   This program is distributed in the hope that it will be useful,
00010   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012   GNU General Public License for more details. (http://www.gnu.org)
00013   
00014   You should have received a copy of the GNU General Public License
00015   along with this program; if not, write to the Free Software
00016   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017   
00018   $Id: ManipulatorJointTrajectory.cpp 1039 2004-02-11 20:50:52Z jungd $
00019   $Revision: 1.4 $
00020   $Date: 2004-02-11 15:50:52 -0500 (Wed, 11 Feb 2004) $
00021   $Author: jungd $
00022  
00023 ****************************************************************************/
00024 
00025 #include <robot/ManipulatorJointTrajectory>
00026 
00027 #include <sstream>
00028 
00029 #include <base/Math>
00030 #include <base/Externalizer>
00031 #include <base/Serializer>
00032 #include <base/externalization_error>
00033 
00034 
00035 using robot::ManipulatorJointTrajectory;
00036 
00037 using base::Vector;
00038 using base::vectorRange;
00039 using base::Range;
00040 using base::Time;
00041 using base::Serializer;
00042 using base::Externalizer;
00043 using base::externalization_error;
00044 using base::XS;
00045 using base::dom::DOMNode;
00046 using base::dom::DOMElement;
00047 
00048 
00049 ManipulatorJointTrajectory::ManipulatorJointTrajectory(Int numJoints, AngularUnits units)
00050   : angUnits(units)
00051 {
00052   const Int dof = numJoints;
00053   qs.resize(2);
00054   qs[0].reset( zeroVector(dof) );
00055   qs[1].reset( zeroVector(dof) );
00056   
00057   times.resize(2);
00058   times[0] = 0;
00059   times[1] = 1;
00060 
00061   computeSis(dof);
00062 }
00063 
00064 
00065 ManipulatorJointTrajectory::ManipulatorJointTrajectory(const ManipulatorJointTrajectory& t)
00066   : qs(t.qs), times(t.times), si(t.si), angUnits(t.angUnits)
00067 {
00068 }
00069 
00070 
00071 ManipulatorJointTrajectory::ManipulatorJointTrajectory(const Vector& sq, const Time& st, 
00072                                                        const Vector& eq, const Time& et,
00073                                                        AngularUnits units)
00074   : angUnits(units)
00075 {
00076   Int dof = sq.size();
00077   Assert( dof > 0 );
00078 
00079   qs.resize(2);
00080   qs[0].reset(sq);
00081   qs[1].reset(eq);
00082 
00083   times.resize(2);
00084   times[0] = st;
00085   times[1] = et;
00086 
00087   computeSis(dof);
00088 }
00089 
00090 
00091 ManipulatorJointTrajectory::ManipulatorJointTrajectory(const array<Vector>& qs, 
00092                                                        const array<Time>& times, 
00093                                                        bool deltas, AngularUnits units)
00094   : angUnits(units)
00095 {
00096   init(qs, times, deltas);
00097 }
00098 
00099 
00100 void ManipulatorJointTrajectory::convertComponentUnits(array<Int> components, AngularUnits units)
00101 {
00102   if (getAngularUnits() == units) return; // nothing to do
00103 
00104   const Int dof = qs[0].size();
00105   for(Int i=0; i<qs.size(); i++) {
00106     for(Int ci=0; ci<components.size(); ci++) {
00107       Int c(components[ci]);
00108       if (c<dof) {
00109         qs[i][c] = (units==Radians)?Math::degToRad(qs[i][c]):Math::radToDeg(qs[i][c]);
00110       }
00111       else
00112         throw std::invalid_argument(Exception("component index out of range"));
00113     }
00114   }
00115 }
00116 
00117 
00118 void ManipulatorJointTrajectory::setNumJoints(Int numJoints, bool truncateInitial)
00119 {
00120   Int currsize = qs[0].size();
00121   
00122   if (numJoints < currsize) { // discard elements from end (or start if truncateInitial is true)
00123     for(Int i=0; i<qs.size(); i++) {
00124       Vector nq( numJoints );
00125       if (!truncateInitial)
00126         nq = vectorRange(qs[i], Range(0, numJoints) );
00127       else
00128         nq = vectorRange(qs[i], Range(currsize-numJoints, currsize));
00129       qs[i].reset(nq);
00130     }
00131   }
00132   else if (numJoints > currsize) { // add zero elements on end
00133     const Int currentNum = qs[0].size();
00134     for(Int i=0; i<qs.size(); i++) {
00135       Vector nq( zeroVector(numJoints) );
00136       vectorRange(nq, Range(0, currentNum)) = qs[i];
00137       qs[i].reset(nq);
00138     }
00139   }
00140 
00141 }
00142 
00143 
00144 
00145 Vector ManipulatorJointTrajectory::q(const Time& t) const 
00146 {
00147   Real s = gets(t);
00148   Int i = findIndex(s);
00149   if (Math::equals(s,si[i])) // fast path if s corresponds to a waypoint
00150     return qs[i];
00151 
00152   Vector v( qs[i+1] - qs[i] );
00153   Real ts( si[i+1] - si[i] ); // change in s between qs[i] to qs[i+1]
00154   
00155   Real ds = s-si[i];
00156   Vector dv( (ds/ts)*v );
00157 
00158   return qs[i] + dv;
00159 }
00160 
00161 
00162 Time ManipulatorJointTrajectory::time(Real s) const
00163 {
00164   Math::bound<Real>(s,0,1);
00165   return Time( times[0].seconds() +  ( (times[times.size()-1] - times[0]).seconds() * s ) );
00166 }
00167 
00168 
00169 void ManipulatorJointTrajectory::shiftTime(const Time& dt)
00170 {
00171   for(Int i=0; i<times.size(); i++)
00172     times[i] += dt;
00173 }
00174 
00175 
00176 void ManipulatorJointTrajectory::scaleTime(Real s)
00177 {
00178   for(Int i=0; i<times.size(); i++)
00179     times[i] *= s;
00180 }
00181 
00182 
00183 Real ManipulatorJointTrajectory::gets(const Time& t) const
00184 {
00185   const Time& st(times[0]);
00186   const Time& et(times[times.size()-1]);
00187   Time bt(t);
00188   Math::bound<Time>(bt,st,et);
00189   return (bt-st).seconds() / (et-st).seconds();
00190 }
00191 
00192 void ManipulatorJointTrajectory::serialize(Serializer& s)
00193 {
00194   s(qs,"qs");
00195   s(times,"times");
00196   s(si,"si");
00197 }
00198 
00199 
00200 bool ManipulatorJointTrajectory::formatSupported(String format, Real version, ExternalizationType type) const
00201 {
00202   return    (format == "txt") && (version==1.0) 
00203          || (format == "xml") && (version==1.0);
00204 }
00205 
00206 
00207 void ManipulatorJointTrajectory::externalize(Externalizer& e, String format, Real version)
00208 {
00209   if (format=="") format = String("xml");
00210 
00211   if (!formatSupported(format,version,e.ioType()))
00212     throw std::invalid_argument(Exception(String("format ")+format+" "+base::realToString(version)+" unsupported"));
00213 
00214   if (format == "txt") {
00215 
00216     if (e.isInput()) {
00217       array<Vector> qs;
00218       array<Time> times;
00219       bool first = true;
00220       bool deltas = false;
00221       
00222       while (e.moreInput()) {
00223         
00224         String line(e.readLine());
00225         if (line.empty()) break; // empty line or eof indicates end of list
00226         while (line[0] == '#') line = e.readLine(); // skip comment lines
00227         
00228         if (first) {
00229           if (!( (line.substr(0,8) == "absolute") || (line.substr(0,8) == "relative")))
00230             throw std::invalid_argument(Exception(String("joint waypoint text file must indicate 'absolute' or 'relative'")));
00231           
00232           deltas = (line.substr(0,8) == String("relative"));
00233           first = false;
00234         }
00235         else {
00236           // count separating spaces to determine dof
00237           Int spaces=0;
00238           for(Int i=0; i<line.size(); i++) 
00239             if (line[i]==' ') { 
00240               spaces++;
00241               while ( (i<line.size()-1) && (line[i]==' ')) i++;
00242             }
00243           if (line[0]==' ') spaces--; // don't count leading space(s)
00244           
00245           std::istringstream iss(line);
00246           iss.setf(std::ios_base::skipws | std::ios_base::dec);
00247           
00248           const Int dof = spaces + 1 - 1; // don't count time at end as a dof
00249           
00250           Vector q(dof);
00251           
00252           for(Int i=0; i<dof; i++)
00253             iss >> q[i];
00254           
00255           Real t;
00256           iss >> t;
00257           
00258           qs.push_back(q);
00259           
00260           times.push_back(Time(t));
00261         }
00262       }
00263       
00264       if (first)
00265         throw std::invalid_argument(Exception(String("joint waypoint text file contains no data.")));
00266       
00267       if (qs.size() < 2)
00268         throw std::invalid_argument(Exception(String("joint waypoint text file must contain at least 2 'waypoint' vectors")));
00269       
00270       init(qs,times,deltas);
00271       
00272     }
00273     else { // output
00274       std::ostringstream out;
00275       
00276       // comment header
00277       out << "# Joint waypoint trajectory [joint positions & times (at-end)]" << std::endl;
00278       out << "# the following line should be 'absolute' for absolute qi's & t values or 'relative' if the vectors represent inter-waypoint deltas dqi's & dt" << std::endl;
00279       out << "absolute" << std::endl; // not "relative"
00280       out << "# q0 q1 ... qn t" << std::endl;
00281       std::ostream::fmtflags savedFlags = out.setf(std::ios_base::dec | std::ios_base::right);
00282       Int savedPrec = out.precision(10);
00283       Int savedWidth = out.width(13);
00284       const Int dof=qs[0].size();
00285 
00286 // this is a hack, as under gcc the flags don't appear to stay set after an output op (!?)
00287 #define setflags \
00288       out.setf(std::ios_base::dec | std::ios_base::right); \
00289       out.precision(10); \
00290       out.width(13); 
00291 
00292       setflags;
00293       for(Int i=0; i<qs.size(); i++) {
00294         Vector q(qs[i]);
00295         Time t(times[i]);
00296         
00297         for(Int e=0; e<dof; e++)
00298           out << q[e] << " "; setflags;
00299         
00300         out << t.seconds(); setflags;
00301         out << std::endl;
00302         
00303       }
00304       
00305       out.setf(savedFlags);
00306       out.precision(savedPrec);
00307       out.width(savedWidth);
00308       
00309       e.writeString(out.str());
00310     }
00311     
00312   } else if (format == "xml") {
00313 
00314     if (e.isOutput()) {
00315 
00316       DOMElement* trajElem = e.createElement("jointtrajectory");
00317 
00318       e.setElementAttribute(trajElem,"pointtype","absolute");
00319       e.setElementAttribute(trajElem,"angunit", (angUnits==Radians)?String("rad"):String("deg"));
00320 
00321       e.appendComment(trajElem, "space separated joint parameter values and time (secs) at end");
00322 
00323       const Int dof=qs[0].size();
00324       
00325       for(Int i=0; i<qs.size(); i++) {
00326         Vector v(dof + 1);
00327         vectorRange(v, Range(0,dof)) = qs[i];
00328         v[dof] = times[i].seconds();
00329 
00330         e.appendText( trajElem, e.toString(v) );
00331         e.appendBreak( trajElem );
00332       }
00333 
00334       e.appendElement(trajElem);
00335 
00336     }
00337     else { // input
00338       
00339       DOMNode* context = e.context();
00340       
00341       DOMElement* trajElem = e.getFirstElement(context, "jointtrajectory");
00342 
00343       bool absolute = ( e.getDefaultedElementAttribute(trajElem, "pointtype", "absolute") == "absolute" );
00344       angUnits = ( e.getDefaultedElementAttribute(trajElem, "angunit", "rad") == "deg" )?Degrees:Radians;
00345 
00346       array<Vector> qs;
00347       array<Time> times;
00348 
00349       String trajText = e.getContainedText(trajElem,true);
00350       array<String> trajLines = e.splitIntoLines(trajText);
00351 
00352       if (trajLines.size() < 2)
00353         throw externalization_error(Exception("trajectory must contain at least two points"));
00354 
00355       Int dof;
00356       for(Int i=0; i<trajLines.size(); i++) {
00357 
00358         array<String> elts = e.splitAtDelimiter(trajLines[i], ' ');
00359         if (elts.size() < 2)
00360           throw externalization_error(Exception("trajectory point with no joints encountered"));
00361         else {
00362           if (i==0)
00363             dof=elts.size()-1;
00364           else
00365             if (elts.size()-1 != dof)
00366               throw externalization_error(Exception("trajectory point of incorrect dimension encountered"));
00367         }
00368 
00369         Vector v( e.stringsToReals(elts) );
00370 
00371         qs.push_back( Vector( vectorRange(v, Range(0, v.size()-1)) ) );
00372         times.push_back( Time(v[v.size()-1]) );
00373       }
00374 
00375       e.removeElement(trajElem);
00376 
00377       init(qs,times,!absolute);
00378       
00379     }
00380     
00381     
00382   } // format xml
00383   
00384 }
00385 
00386 
00387 
00388 void ManipulatorJointTrajectory::init(const array<base::Vector>& qs, const array<base::Time>& times, bool deltas)
00389 {
00390   Assert(qs.size() > 0);
00391   Assert(qs.size() == times.size());
00392   Int dof=qs[0].size();
00393 
00394   if (!deltas) {
00395     this->qs=qs;
00396     this->times = times;
00397 #ifdef DEBUG
00398   // make sure all the Vectors are the same size
00399   for(Int i=0; i<qs.size(); i++) {
00400     Assert(qs[i].size() == dof);
00401   }    
00402 #endif
00403   }
00404   else {
00405     // convert sequence of deltas into absolute joint-configurations and times
00406     array<Vector> aqs(qs.size()+1);
00407     aqs[0].reset( zeroVector(dof) );
00408     for(Int i=0; i<qs.size(); i++) {
00409       Assert(qs[i].size() == dof);
00410       aqs[i+1].reset( aqs[i]+qs[i] );
00411     }
00412 
00413     array<Time> atimes(times.size()+1);
00414     atimes[0] = 0;
00415     for(Int i=0; i<times.size(); i++)
00416       atimes[i+1] = atimes[i]+times[i];
00417 
00418     this->qs = aqs;
00419     this->times = atimes;
00420   }
00421 
00422   computeSis(dof);
00423 }
00424 
00425 
00426 void ManipulatorJointTrajectory::computeSis(Int dof)
00427 {
00428   // ensure at least two points
00429   if (qs.size() == 0) { qs.at(0) = qs.at(1) = Vector(dof); }
00430   if (times.size() == 0) { times.at(0) = 0; times.at(1) = 1; }
00431 
00432   // s's map [0..1] to the range [start-time..end-time]
00433   Real T = (times[times.size()-1] - times[0]).seconds();
00434   Int i = 1;
00435   si.resize(qs.size());
00436   si[0] = 0;
00437   while (i<qs.size()) {
00438     if (times[i-1] > times[i])
00439       throw std::invalid_argument(Exception("times must be monotonically increasing"));
00440     si[i] = (times[i] - times[0]).seconds() / T;
00441     i++;
00442   }
00443 }
00444 
00445 
00446 Int ManipulatorJointTrajectory::findIndex(Real s) const
00447 {
00448   Int i;
00449   for(i=0; i<si.size(); i++)
00450     if (si[i] > s) break;
00451   return i-1;
00452 }

Generated on Thu Jul 29 15:56:37 2004 for OpenSim by doxygen 1.3.6