Tekkotsu Homepage | Demos | Overview | Downloads | Dev. Resources | Reference | Credits |
MotionCommand.hGo to the documentation of this file.00001 //-*-c++-*- 00002 #ifndef INCLUDED_MotionCommand_h 00003 #define INCLUDED_MotionCommand_h 00004 00005 #include "Shared/RobotInfo.h" 00006 #include "MotionManagerMsg.h" 00007 #include "Shared/WorldState.h" 00008 #include "OutputCmd.h" 00009 #include <stddef.h> 00010 00011 class EventTranslator; 00012 class EventBase; 00013 00014 //! The abstract base class for motions, provides common interface. All motions should inherit from this 00015 /*! For instructions on how to create: 00016 * - <b>an instantiation</b> of an existing MotionCommand, see MotionManager 00017 * - <b>a new subclass</b> of MotionCommand, read on. Also see the step-by-step tutorials: 00018 * - <a href="../FirstMotionCommand.html">Tekkotsu's "First MotionCommand" Tutorial</a> 00019 * - <a href="http://www.cs.cmu.edu/~dst/Tekkotsu/Tutorial/motion.shtml">David Touretzky's Motion Commands chaper</a> 00020 * - <a href="http://www.cs.cmu.edu/afs/cs/academic/class/15494-s06/www/lectures/motion_commands.pdf">CMU's Cognitive Robotics slides</a> 00021 * 00022 * To create a new type of motion, you'll want to subclass this. You 00023 * don't need to do anything fancy, but just be sure to override the 3 00024 * abstract functions, updateOutputs(), isAlive(), and isDirty(). 00025 * 00026 * There is a quick-start boilerplate included in the distribution: <a href="http://cvs.tekkotsu.org/cgi/viewcvs.cgi/Tekkotsu/project/templates/motioncommand.h?rev=HEAD&content-type=text/vnd.viewcvs-markup"><i>project</i><tt>/templates/motioncommand.h</tt></a>: 00027 * 00028 * When an output is set to a value, that output is held at that value 00029 * until it is set to a new value, even if the MotionCommand that set 00030 * it is pruned or stops using the output. Outputs never "reset" to 0 00031 * or some other relatively arbitrary base value if all the 00032 * MotionCommands are removed. 00033 * 00034 * However, PID values will reset to the default values if pruned or 00035 * not set since these values <i>do</i> have a base value which you 00036 * will want to use 99% of the time. 00037 * 00038 * Be aware that there is a delay between when you set a joint to a 00039 * value and that actually is taken into account by the system - it's 00040 * on the order of FrameTime*NumFrames (currently 8*4 = 32 ms, so 00041 * worse case 2*8*4 = 64 ms) This is because the commands are double 00042 * buffered. PIDs, on the other hand, seem to take effect more 00043 * quickly. This un-synchronization can sometimes cause a bit of 00044 * jerkiness (mainly on startup, where there's a large difference 00045 * between desired and target values.) 00046 * 00047 * Here is the cycle of calls made by MotionManager to your command: 00048 * -# shouldPrune() (by default, this will return !isAlive() iff #autoprune==true) 00049 * -# updateOutputs() (assuming the MC wasn't pruned after the previous step) 00050 * 00051 * So, if you want to hold a joint at a value, each time your 00052 * updateOutputs function is called, you should tell the 00053 * MotionManager to keep the joint there (using one of 00054 * MotionManager::setOutput()'s). If you do not set a joint after a 00055 * call to updateOutputs, the MotionManager will assume you are no 00056 * longer using that joint and a lower priority MotionCommand may 00057 * inherit it. 00058 * 00059 * MotionCommands which generate events should use the inherited 00060 * MotionCommand::postEvent() instead of trying to access a global ::erouter - the 00061 * inherited version will properly handle sending the events 00062 * regardless of the current process context, but trying to access a 00063 * non-shared global like ::erouter could cause problems otherwise. 00064 * 00065 * @warning <b>Be careful what you call in MotionManager</b> \n 00066 * Some functions are marked MotionCommand-safe - this is another 00067 * issue due to our "fake" fork. In short, when a function is called 00068 * on a MotionCommand, it uses the context of whatever process created 00069 * it, not the process that actually made the function call. Thus, 00070 * when Motion calls updateOutputs(), calls that the MotionCommand 00071 * makes are indistinguishable from concurrent calls from Main. This 00072 * can cause deadlock if a function is called which locks the 00073 * MotionManager. To get around this, we need to pass the 'this' 00074 * parameter on functions that require a lock, namely 00075 * MotionManager::setOutputs(). This allows the MotionManager to 00076 * figure out that it's the same MotionCommand it previously called 00077 * updateOutputs() on, and thus avoid trying to lock itself again. 00078 * 00079 * @warning <b>Don't store pointers in motion commands!</b> \n 00080 * Since motion commands are in shared memory, and these shared memory 00081 * regions can have different base pointers in each process, pointers 00082 * will only be valid in the process from which they were assigned. 00083 * In other processes, that address may point to something else, 00084 * especially if it was pointing outside of the shared memory 00085 * regions.\n 00086 * There are convoluted ways of getting around this. If needed, 00087 * MotionManager could be modified to hand out shared memory regions 00088 * upon request. Let's try to avoid this for now. Keep 00089 * MotionCommands simple, without dynamic memory. Do more complicated 00090 * stuff with behaviors, which only have to worry about running in Main. 00091 */ 00092 class MotionCommand : public MotionManagerMsg { 00093 //!@nosubgrouping 00094 public: 00095 00096 // ***************** 00097 //! @name *** ABSTRACT: *** (must be defined by subclasses) 00098 // ***************** 00099 00100 //! is called once per update cycle, can do any processing you need to change your priorities or set output commands on the MotionManager 00101 /*! @return zero if no changes were made, non-zero otherwise 00102 * @see RobotInfo::NumFrames @see RobotInfo::FrameTime */ 00103 virtual int updateOutputs()=0; 00104 00105 //! not used by MotionManager at the moment, but could be used to reduce recomputation, and you may find it useful 00106 /*! @return zero if none of the commands have changed since last 00107 * getJointCmd(), else non-zero */ 00108 virtual int isDirty()=0; 00109 00110 //! used to prune "dead" motions from the MotionManager 00111 /*! note that a motion could be "paused" or inactive and therefore 00112 * not dirty, but still alive, biding its time to "strike" ;) 00113 * @return zero if the motion is still processing, non-zero otherwise */ 00114 virtual int isAlive()=0; 00115 00116 //@} 00117 00118 00119 // ****************** 00120 //! @name *** INHERITED: *** 00121 // ****************** 00122 00123 //! Constructor: Defaults to kStdPriority and autoprune==true 00124 MotionCommand() : MotionManagerMsg(), queue(NULL), autoprune(true), started(false) 00125 #ifdef PLATFORM_APERIOS 00126 , state() 00127 #endif 00128 { } 00129 //! Destructor 00130 virtual ~MotionCommand() {} 00131 00132 //! called after this is added to MotionManager 00133 virtual void DoStart() { started=true; } 00134 00135 //! called after this is removed from MotionManager 00136 virtual void DoStop() { started=false; clearID(); resetWheels(); } 00137 00138 //! returns true if the MotionCommand is currently running (although it may be overridden by a higher priority MotionCommand) 00139 virtual bool isActive() const { return started; } 00140 00141 /*! @return current setting of autopruning - used to remove motion from groups when !isAlive() */ 00142 virtual bool getAutoPrune() { return autoprune; } 00143 00144 /*! @param ap bool representing requested autopruning setting */ 00145 virtual void setAutoPrune(bool ap) { autoprune=ap; } 00146 00147 //! whether this motion should be removed from its motion group automatically ( MotionCommand::autoprune && !isAlive()) 00148 /*! @return (MotionCommand::autoprune && !isAlive())*/ 00149 virtual bool shouldPrune() { return (autoprune && !isAlive()); } 00150 00151 //! only called from MMCombo during process setup, allows MotionCommands to send events 00152 void setTranslator(EventTranslator * q) { queue=q; } 00153 00154 #ifdef PLATFORM_APERIOS 00155 //! called by MotionManager each time a process checks out the motion, makes sure #state is set for the calling process 00156 void setWorldState(WorldState* s) { state.cur=s; } 00157 #endif 00158 00159 protected: 00160 //! this utility function will probably be of use to a lot of MotionCommand's 00161 /*! Does a weighted average of a and b, favoring b by x percent (so x==0 results in a, x==1 results in b) 00162 * @param a first value 00163 * @param b second value 00164 * @param x weight of second value as opposed to first 00165 * @return @f$ a*(1.0-x)+b*x @f$ 00166 * @todo - replace with a more fancy spline based thing? */ 00167 static inline double interpolate(double a, double b, double x) { 00168 return a*(1.0-x)+b*x; 00169 } 00170 //! this utility function will probably be of use to a lot of MotionCommand's 00171 /*! Does a weighted average of a and b, favoring b by x percent (so x==0 results in a, x==1 results in b) 00172 * @param a first value 00173 * @param b second value 00174 * @param x weight of second value as opposed to first 00175 * @return @f$ a*(1.0-x)+b*x @f$ 00176 * @todo - replace with a more fancy spline based thing? */ 00177 static inline float interpolate(float a, float b, float x) { 00178 return a*(1.0-x)+b*x; 00179 } 00180 //! this utility function will probably be of use to a lot of MotionCommand's, see interpolate(double a,double b,double r) 00181 /*! interpolates both value and weights of JointCmd's 00182 * @param a first joint cmd 00183 * @param b second joint cmd 00184 * @param x weight to favor b's value and weight 00185 * @param r joint cmd to store the result */ 00186 static inline void interpolate(const OutputCmd& a, const OutputCmd& b, float x, OutputCmd& r) { 00187 r.set(interpolate(a.value,b.value,x),interpolate(a.weight,b.weight,x)); 00188 } 00189 //@} 00190 00191 //! calls EventTranslator::trapEvent() directly (avoids needing erouter, which is a non-shared global, causes problems with context, grr, silly OS) 00192 void postEvent(const EventBase& event); 00193 00194 //! This function sets all wheel values to 0 00195 void resetWheels(); 00196 00197 EventTranslator * queue; //!< send events using this, instead of posting to the erouter 00198 00199 int autoprune; //!< default true, autoprune setting, if this is true and isAlive() returns false, MotionManager will attempt to remove the MC automatically 00200 bool started; //!< true if the MotionCommand is currently running (although it may be overridden by a higher priority MotionCommand) 00201 #ifdef PLATFORM_APERIOS 00202 //! purposely shadows global ::state as a member of MotionCommand, so subclasses will be guaranteed to use the current process's instance instead of that of the process they were created in 00203 struct StateRedirect { 00204 WorldState * operator->() const { return cur==NULL ? WorldState::getCurrent() : cur; } 00205 WorldState * cur; 00206 } state; 00207 #endif 00208 00209 private: 00210 MotionCommand(const MotionCommand&); //!< don't call 00211 MotionCommand& operator=(const MotionCommand&); //!< don't call 00212 }; 00213 00214 /*! @file 00215 * @brief Defines the MotionCommand class, used for creating motions of arbitrary complexity 00216 * @author ejt (Creator) 00217 * 00218 * $Author: kcomer $ 00219 * $Name: tekkotsu-4_0 $ 00220 * $Revision: 1.25 $ 00221 * $State: Exp $ 00222 * $Date: 2007/11/15 21:31:18 $ 00223 */ 00224 00225 #endif 00226 |
Tekkotsu v4.0 |
Generated Thu Nov 22 00:54:54 2007 by Doxygen 1.5.4 |