Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

MotionSequenceEngine.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_MotionSequenceEngine_h_
00003 #define INCLUDED_MotionSequenceEngine_h_
00004 
00005 #include "Shared/LoadSave.h"
00006 #include "IPC/ListMemBuf.h"
00007 #include "PostureEngine.h"
00008 
00009 //! A handy class for storing a sequence of keyframed movements
00010 /*! Each outputs is handled independently.  It's easy to add keyframes
00011  *  which modify all of the outputs, but since each output is tracked
00012  *  individually, OutputCmd's with 0 weight can be used to not affect
00013  *  other motions.  For instance, if you want to pan the head left to right while
00014  *  moving the right leg up and down several times, you won't have to
00015  *  specify the intermediary position of the head in its motion at each of the leg
00016  *  motion keyframes.
00017  *
00018  *  Be aware that the 0 time frame will be replaced on a call to
00019  *  play() with the current body posture from ::state.  However, this only applies
00020  *  to outputs which have a non-zero weighted frame defined at some
00021  *  point.  The weights, of the 0 time frame will remain unchanged.  
00022  *  These weights are initially set to 0, so that it's
00023  *  possible to 'fade in' the first frame of the motion sequence from
00024  *  whereever the body happens to be (or already doing)
00025  *
00026  *  To fade out at the end, set a frame with 0 weight for everything.
00027  *  Otherwise it will simply die suddenly.  When a joint reaches its
00028  *  last keyframe, if #hold is set (the default) it will hold its last value
00029  *  until the MotionSequence is reset or stopped.  If #hold is false,
00030  *  then the joint is treated as 0 weight once it reaches its last frame.
00031  *
00032  *  Currently, MotionSequenceEngine is intended mainly for building, 
00033  *  not editing.  It's easy to add keyframes, but hard/impossible to
00034  *  delete them.
00035  *
00036  *  The MotionSequenceEngine base class is an abstract class so that you can
00037  *  create memory efficient motion sequences and simply refer to them
00038  *  by the common base class instead of having to worry about the
00039  *  actual size allocated in the template, MotionSequenceMC.
00040  *
00041  *  @see MotionSequenceEngine::SizeSmall, MotionSequenceEngine::SizeMedium, MotionSequenceEngine::SizeLarge, MotionSequenceEngine::SizeXLarge, 
00042  *  
00043  *  The file format used is as follows: ('<' and '>' are not meant literally)
00044  *  - First line: '<tt>\#MSq</tt>'
00045  *  - Followed by any series of:\n
00046  *    - '<tt>advanceTime </tt><i>time-delta</i>' - moves playhead forward, in milliseconds (synonym for <tt>delay</tt>)
00047  *    - '<tt>delay </tt><i>time-delta</i>' - moves playhead forward, in milliseconds (synonym for <tt>advanceTime</tt>)
00048  *    - '<tt>setTime </tt><i>time</i>' - sets play time to specified value, in ms
00049  *    - '<i>outputname</i><tt> </tt><i>value</i><tt> </tt>[<i>weight</i>]' - sets the specified output to the value - assumes 1 for weight; you can view the list of valid joint names in the outputNames array within the RobotInfo extension namespace for your model.  (e.g. ERS210Info::outputNames[])
00050  *    - '<tt>load </tt><i>filename</i>' - file can be a posture or another motion sequence (if MS, leaves playhead at end of the motion sequence); see setPose() and overlayMotion()
00051  *    - '<tt>loadExplicit </tt><i>filename</i>' - file must be a posture, sets position for all outputs, including zero-weighted ones; see setExplicitPose()
00052  *    - '<tt>degrees</tt>' - all following <i>value</i>s will be interpreted as degrees [default]
00053  *    - '<tt>radians</tt>' - all following <i>value</i>s will be interpreted as radians
00054  *    - '<tt>#</tt><i>comment</i>' - a comment line
00055  *  - Last line: '<tt>\#END</tt>'
00056  *  
00057  *  Lines beginning with '#' are ignored as comments.  Be aware if you
00058  *  load the file and then save it again, these comments will be lost.
00059  *
00060  *  Example 1: This motion sequence will straighten out the head, panning from right to left.\n
00061 <table align="center"><tr><td align=left><pre>
00062 \#MSq 
00063 degrees 
00064 
00065 <i>\# Straighten head</i>
00066 advanceTime  50 
00067 NECK:tilt       15 
00068 NECK:nod       0 
00069 
00070 <i>\# Pan right</i>
00071 advanceTime  850 
00072 NECK:pan~       -45 
00073 
00074 <i>\# Pan left</i>
00075 advanceTime  900 
00076 NECK:pan~       45 
00077 NECK:tilt       15 
00078 NECK:nod       0 
00079 
00080 \#END
00081 </pre></td></tr></table>
00082  *  
00083  *  This graph illustrates the motion of the tilt and pan joints in example 1:
00084  *  <img src="MotionSequenceGraph1.png">
00085  *  Notice how the joint will move from wherever it is initally to the first
00086  *  keyframe <i>for that joint</i>.  Specifying the tilt joint a second time
00087  *  at the end of the motion forces the tilt joint to be held at that position
00088  *  throughout the motion, regardless of the #hold setting at the time
00089  *  the sequence is run.
00090  *  
00091  *  Example 2: This example will straighten the head and the tail, pan the
00092  *  head left to right, and @e then pan the tail left to right.\n
00093 <table align="center"><tr><td align=left><pre>
00094 \#MSq
00095 degrees
00096 
00097 <i>\# Bring head and tail to neural positions</i>
00098 advanceTime 50
00099 NECK:pan~ 0
00100 NECK:tilt 0
00101 TAIL:pan~ 0
00102 TAIL:tilt 0
00103 
00104 <i>\# Pan left</i>
00105 advanceTime 1000
00106 NECK:pan~ 90
00107 <i>\# Pan right</i>
00108 advanceTime 1000
00109 NECK:pan~ -90
00110 
00111 <i>\# Center head</i>
00112 <i>\# Update tail time index</i>
00113 advanceTime 500
00114 NECK:pan~ 0
00115 <i>\# Note this respecification of TAIL:pan~ at 0 -- see graph below</i>
00116 TAIL:pan~ 0
00117 
00118 <i>\# Wag left</i>
00119 advanceTime 500
00120 TAIL:pan~ 90
00121 <i>\# Wag right</i>
00122 advanceTime 500
00123 TAIL:pan~ -90
00124 
00125 \#END 
00126 </pre></td></tr></table>
00127  *  
00128  *  These graphs illustrate the motion of the pan joint for the head and the tail:
00129  *  <img src="MotionSequenceGraph2-head.png">
00130  *  <img src="MotionSequenceGraph2-tail.png">
00131  *  The head's motion should be straightforward.  The thing to note in the tail's
00132  *  graph is why the second <code>TAIL:pan~ 0</code> is necessary at time
00133  *  2550.  If it were not specified, the tail would slowly move to the left over the
00134  *  course of the head's movement.  We want it to stay still until the head is done,
00135  *  so it must be respecified at the same position to hold it at that value in the
00136  *  intervening time.
00137  *  
00138  *  After loading a motion sequence, the playtime is left at the end.
00139  *  This is to make it easy to append/overlay motion sequences.
00140  *  However, the playhead will be reset to the beginning on the first
00141  *  call to updateOutputs() if isPlaying() returns true.
00142  *
00143  *  You can also create a motion sequence dynamically at run time:
00144  *  \code
00145  *  //This code sample will stand up, point the head forward and up 0.1 radians,
00146  *  //and then autoprune
00147  *
00148  *  //First declare the MotionSequence itself:
00149  *  SharedObject< MotionSequenceMC<MotionSequenceEngine::SizeSmall> > stand;
00150  *
00151  *  //Over the course of the first 700 milliseconds, go to standing posture:
00152  *  standSit->setTime(700);
00153  *  standSit->setPose(PostureEngine("stand.pos"));
00154  *  // could also use standSit->loadFile("stand.pos")
00155  *
00156  *  //Then take another 700 milliseconds to straighten out the head:
00157  *  standSit->advanceTime(700);
00158  *  //We'll set joints individually this time, instead of loading a posture file:
00159  *  standSit->setOutputCmd(HeadOffset+PanOffset,0);
00160  *  standSit->setOutputCmd(HeadOffset+RollOffset,0);
00161  *  standSit->setOutputCmd(HeadOffset+TiltOffset,0.1); //look up .1 radians
00162  *
00163  *  //Add to MotionManager:
00164  *  motman->addPrunableMotion(standSit);
00165  *  //Playback will now begin automatically, and region deallocated when done
00166  *  \endcode
00167  *  
00168  *  By default, #playing is true.  Thus, when you add a MotionSequenceMC
00169  *  to the MotionManager, it will begin executing automatically.  If
00170  *  you do \e not want this behavior, simply call pause() before
00171  *  adding the sequence.
00172  *
00173  *  When the sequence reaches the end, isAlive() will return false.
00174  *  If the motion was added with MotionManager::addPrunableMotion, the
00175  *  motion sequence will then autoprune itself from the MotionManager.
00176  *  However, you can either call MotionManager::addPersistentMotion()
00177  *  to add it, or call setAutoPrune(false), if you want to retain the
00178  *  same instantiation between executions.
00179  *
00180  *  @see PostureEngine for information on the posture files
00181  *  @see <a href="http://www.cs.cmu.edu/~dst/Tekkotsu/Tutorial/postures.shtml">David Touretzky's "Postures and Motion Sequences" Chapter</a>
00182  *  @see <a href="http://www.cs.cmu.edu/afs/cs/academic/class/15494-s06/www/lectures/postures.pdf">CMU's Cognitive Robotics posture slides</a>
00183  *  @todo We should also have an insertMotion()
00184  */
00185 class MotionSequenceEngine : public LoadSave {
00186 public:
00187   //!constructor, will start playing immediately
00188   MotionSequenceEngine();
00189   //!destructor
00190   virtual ~MotionSequenceEngine() {}
00191 
00192   //! similar to the MotionCommand::updateOutputs, although this isn't a motion command, and doesn't make any calls on MotionManager - merely updates #lasttime, expects subclasses to do the work of sending new commands to the system
00193   virtual int updateOutputs();
00194   
00195   //!@name LoadSave related
00196   virtual unsigned int getBinSize() const; //!< inherited, returns the size used to save the sequence
00197   virtual unsigned int loadBuffer(const char buf[], unsigned int len); //!< inherited, doesn't clear before loading - call clear yourself if you want to reset, otherwise it will overlay.  Leaves playtime at end of load.
00198   virtual unsigned int saveBuffer(char buf[], unsigned int len) const; //!< inherited, saves the motion sequence - will save a flat file - doesn't remember references to other files which were loaded
00199   virtual unsigned int loadFile(const char filename[]); //!< inherited, doesn't clear before loading - call clear yourself if you want to reset, otherwise it will overlay.  Leaves playtime at end of load.
00200   virtual unsigned int saveFile(const char filename[]) const; //!< inherited, saves the motion sequence - will save a flat file - doesn't remember references to other files which were loaded
00201   void setSaveDegrees() { loadSaveMode=M_PI/180; }       //!< will store angles as degrees on future saves
00202   bool isSaveDegrees() const { return loadSaveMode!=1; } //!< returns true if will store angles as degrees on future saves
00203   void setSaveRadians() { loadSaveMode=1; }              //!< will store angles as radians on future saves
00204   bool isSaveRadians() const { return loadSaveMode==1; } //!< returns true if will store angles as degrees on future saves
00205   //@}
00206   
00207   //!@name Sequence Construction
00208   virtual void clear()=0; //!< empties out the sequence (constant time operation - faster than a series of pops)
00209   void setTime(unsigned int x); //!< set the time for both playback and editing (in milliseconds)
00210   unsigned int advanceTime(unsigned int x) {setTime(playtime+x); return playtime; } //!< advance the play/edit index by @a x milliseconds, and then returns the new getTime()
00211   void setOutputCmd(unsigned int i, const OutputCmd& cmd); //!< will insert a keyframe for the given output, or change an existing one
00212   const OutputCmd& getOutputCmd(unsigned int i); //!< gets the value of output @a i at the playhead
00213   void setPose(const PostureEngine& pose); //!< calls setOutputCmd for all non-zero weighted OutputCmds in @a pose (if you wish to set from a file, use loadFile)
00214   void setExplicitPose(const PostureEngine& pose); //!< calls setOutputCmd on each of the OutputCmds in @a pose, even if they are zero-weight (can be used to fade joints in/out with other conflicting motions)
00215   PostureEngine getPose(); //!< returns the set of OutputCmd's at the current playhead as a PostureEngine
00216   void getPose(PostureEngine& pose); //!< stores the set of OutputCmd's at the current playhead into the specified PostureEngine
00217   void overlayPose(const PostureEngine& pose) __attribute__((__deprecated__)); //!< deprecated, use setPose instead (what setPose used to do is now setExplicitPose)
00218   unsigned int overlayMotion(const std::string& msFile); //!< loads @a msFile from disk and calls overlayMotion(const MotionSequenceEngine&) with it, returns the duration of @a msFile (0 if there was an error)
00219   void overlayMotion(const MotionSequenceEngine& ms); //!< applies each keyframe of @a ms to this, leaves playhead at the end (in other words, advances playhead to end of @a ms)
00220   void compress(); //!< compresses the sequence by eliminating sequences of moves which are identical
00221   virtual unsigned int getMaxFrames() const=0; //!< returns the maximum number of key frames (Move's) which can be stored, determined by the instantiating MotionSequenceMC's template parameter
00222   virtual unsigned int getUsedFrames() const=0; //!< returns the number of used key frames (Move's) which have been stored by the instantiation MotionSequenceEngine subclass
00223   void makeSafe(const float vels[NumOutputs], float margin); //!< will insert time into the motion where needed to keep the joint velocities at or below the speeds given in @a vels * @a margin
00224   //@}
00225 
00226   //!@name Playback Control
00227   bool isPlaying();                                     //! returns true if currently playing
00228   void play();                                          //!< restarts playback from beginning
00229   void pause() { playing=false; }                       //!< pauses playback until another call to play() or resume()
00230   void resume();                                        //!< begins playback from the current playtime
00231   unsigned int getTime() const { return playtime; }     //!< returns the current position of the playback (in milliseconds), see setTime()
00232   unsigned int getEndTime() const { return endtime; }   //!< returns the length of the motion sequence (in milliseconds)
00233   void setSpeed(float x) { playspeed=x; }               //!< sets the playback speed (e.g. 1=regular, 0.5=half speed, -1=@b backwards)
00234   float getSpeed() const { return playspeed; }          //!< returns the playback speed
00235   
00236   virtual void setHold(bool h=true) { hold=h; } //!< Sets #hold - if this is set to false, it will allow a persistent motion to behave the same as a pruned motion, without being pruned
00237   virtual bool getHold() { return hold; }       //!< return #hold
00238   
00239   //@}
00240 
00241 protected:
00242   // TYPES:
00243   typedef unsigned short Move_idx_t; //!< type for indexes to move structures in subclass's storage
00244   static Move_idx_t invalid_move; //!< used to mark the ends of the Move linked lists
00245 
00246   //! This struct holds all the information needed about a frame for a particular output
00247   struct Move {
00248     //!constructor
00249     Move() : cmd(), next(), prev(), starttime(0) {}
00250     OutputCmd cmd;           //!< the actual command to use
00251     Move_idx_t next;        //!< the next frame
00252     Move_idx_t prev;        //!< the previous frame
00253     unsigned int starttime; //!< the time (relative to first frame) this frame should be expressed at
00254   };
00255 
00256   // MEMBERS:
00257   Move_idx_t starts[NumOutputs]; //!< the beginning frame for each output animation
00258   Move_idx_t prevs[NumOutputs];  //!< the previous frame (the starttime for this frame will always be less than or equal to playtime)
00259   Move_idx_t nexts[NumOutputs];  //!< the upcoming frame (the starttime for this frame will always be greater than playtime)
00260   OutputCmd curs[NumOutputs];          //!< merely a cache of current values (if computed, see #curstamps)
00261   unsigned int curstamps[NumOutputs]; //!< timestamp of corresponding value in #curs
00262   unsigned int playtime;              //!< the current time of playback, 0 is start of sequence
00263   unsigned int lasttime;              //!< the time of the last update
00264   unsigned int endtime;               //!< max of moves's Move::starttime's
00265   float playspeed;                    //!< multiplies the difference between current time and starttime, negative will cause play backwards
00266   bool playing;                       //!< true if playing, false if paused
00267   bool hold;                          //!< if set to true, the posture will be kept active; otherwise joints will be marked unused after each posture is achieved (as if the posture was pruned); set through setHold()
00268   
00269   float loadSaveMode;                 //!< 1 to use radians, M_PI/180 for degrees during a save
00270 
00271   virtual Move& getKeyFrame(Move_idx_t x) =0;            //!< returns the Move struct corresponding to @a x in the subclass's actual data structure
00272   virtual const Move& getKeyFrame(Move_idx_t x) const=0; //!< returns the Move struct corresponding to @a x in the subclass's actual data structure
00273   virtual Move_idx_t newKeyFrame()=0;                    //!< causes subclass to create a new Move structure, returns its index
00274   virtual void eraseKeyFrame(Move_idx_t x)=0;            //!< causes subclass to mark the corresponding Move structure as free
00275 
00276   //!Does the actual calculation of position information.  Perhaps replace with a Bezier or spline or something?
00277   void calcOutput(OutputCmd& ans, unsigned int t, const Move& prev, const Move& next) const {
00278     float prevweight=(float)(next.starttime-t)/(float)(next.starttime-prev.starttime);
00279     ans.set(prev.cmd,next.cmd,prevweight);
00280   }
00281   
00282   //!Sets prev and next to the appropriate values for the given time and output index, return true if there was a change
00283   virtual bool setRange(unsigned int t,Move_idx_t& prev, Move_idx_t& next) const=0;
00284 
00285   //!sets playtime to next time for which any output has a keyframe, -1 if none exists
00286   unsigned int setNextFrameTime(Move_idx_t p[NumOutputs], Move_idx_t n[NumOutputs]) const;
00287   
00288   //!reads a line from a file, parsing it into variables, returns ending position
00289   static unsigned int readWord(const char buf[], const char * const buflen, char word[], const unsigned int wordlen);
00290 
00291   //!returns the index for the output named in the string or NumOutputs if not found, begins search through RobotInfo::outputName's at index @a i
00292   static unsigned int getOutputIndex(const char name[], unsigned int i);
00293 };
00294 
00295 /*! @file
00296  * @brief Describes MotionSequenceEngine, abstract code for smoothly transitioning between a sequence of postures
00297  * @author ejt (Creator)
00298  *
00299  * $Author: ejt $
00300  * $Name: tekkotsu-3_0 $
00301  * $Revision: 1.18 $
00302  * $State: Exp $
00303  * $Date: 2006/10/03 23:01:28 $
00304  */
00305 
00306 #endif

Tekkotsu v3.0
Generated Wed Oct 4 00:03:44 2006 by Doxygen 1.4.7