class DiscreteDMP(object): def __init__(self, transf_system, reg_model=None): self.ts = transf_system # state variables # ----------------------------- self.start = 0.0 self.goal = 1.0 self.tau = 1.0 # Cannonical System # ----------------------------- # s_min self.cutoff = 0.001 # alpha (canonical system parameter) self.alpha = abs(math.log(self.cutoff)) # Canonical System self.s = 1.0 # canonical system is starting with 1.0 self.s_time = 0.0 # Attractor # ----------------------------- # spring term self.K = 50.0 # damping term self.D = self.K / 4 # time steps (for the run) self.delta_t = 0.001 # Transformation System # ---------------------------- # current position, velocity, acceleration self.x = 0.0 self.xd = 0.0 self.xdd = 0.0 # internal variables ''' xd not scaled by tau aka v''' self._raw_xd = 0.0 self.f = 0 # target function input (x) and output (y) self.target_function_input = None self.Fd = None # debugging (y values predicted by fitted lwr model) self.Fp = None # do not predict f by approximated function, # use values of ft directly only works if duration is 1.0!! self.use_ft = False # create LWR model and set parameters # ----------------------------- if not reg_model: # default regression model self.lwr_model = LWR(activation=0.1, exponentially_spaced=True, n_rfs=20) else: # custom regression model self.lwr_model = reg_model # is the DMP initialized? self._is_learned = False def _predict_f(self, x): # if nothing is learned we assume f=0.0 if not self._is_learned: print "WARNING: NO TARGET FUNCTION LEARNED assuming f = 0.0" return 0.0 #return self.lwr_model.predict(np.asarray([x]))[0] return self.lwr_model.predict(x) def _create_training_set(self, trajectory, frequency): ''' Prepares the data set for the supervised learning @param trajectory: list of 3-Tuples with (pos,vel,acc) ''' # number of training samples n_samples = len(trajectory) # duration of the movement duration = float(n_samples) / float(frequency) # set tau to duration for learning tau = duration # initial goal and start obtained from trajectory start = trajectory[0][0] goal = trajectory[-1][0] print "create training set of movement from trajectory with %i entries (%i hz) \ with duration: %f, start: %f, goal: %f" % (n_samples, frequency, duration, start, goal) # compute target function input (canonical system) [rollout] # ----------------------------- # compute alpha_x such that the canonical system drops # below the cutoff when the trajectory has finished alpha = -(math.log(self.cutoff)) # time steps dt = 1.0 / n_samples # delta_t for learning time = 0.0 time_steps = np.zeros(n_samples) for i in xrange(len(time_steps)): time_steps[i] = math.exp(-(alpha / tau) * time) time += dt # compute values of target function # ----------------------------- # the target function (transformation system solved for f, and plugged in y for x) ft = lambda y, yd, ydd, s: self.ts.fs(self.K, self.D, y, yd, ydd, start, goal, tau, s) # evaluate function to get the target values for given training data F = [] for i, d in enumerate(trajectory): # compute f_target(y, yd, ydd) * s #print "s ", target_function_input[i], "y ", d[0], "yd ", d[1], "ydd", d[2], " ft:", ft(d[0], d[1], d[2]) F.append(ft(d[0], d[1], d[2], time_steps[i])) return time_steps, np.asarray(F) def _compute_derivatives(self, pos_trajectory, frequency): # ported from trajectory.cpp # no f*****g idea why doing it this way - but hey, the results are better ^^ add_pos_points = 4 #add points to start for _ in range(add_pos_points): first_point = pos_trajectory[0] pos_trajectory.insert(0, first_point) # add points to the end for _ in range(add_pos_points): first_point = pos_trajectory[-1] pos_trajectory.append(first_point) # derive positions vel_trajectory = [] for i in range(len(pos_trajectory) - 4): vel = (pos_trajectory[i] - (8.0 * pos_trajectory[i + 1]) + (8.0 * pos_trajectory[i + 3]) - pos_trajectory[i + 4]) / 12.0 vel *= frequency vel_trajectory.append(vel) # derive velocities acc_trajectory = [] for i in range(len(vel_trajectory) - 4): acc = (vel_trajectory[i] - (8.0 * vel_trajectory[i + 1]) + (8.0 * vel_trajectory[i + 3]) - vel_trajectory[i + 4]) / 12.0 acc *= frequency acc_trajectory.append(acc) results = zip(pos_trajectory[4:], vel_trajectory[2:], acc_trajectory) return results def _step(self): ''' runs a integration step - updates variables self.x, self.xd, self.xdd ''' dt = self.delta_t # integrate transformation system # ----------------------------- # update f(s) # debugging: use raw function output (time must be 1.0) if self.use_ft: print "DEBUG: using ft without approximation" ftinp = list(self.target_function_input) self.f = self.Fd[ftinp.index(self.s)] else: self.f = self._predict_f(self.s) # calculate xdd (vd) according to the transformation system equation 1 self.xdd = self.ts.transformation(self.K, self.D, self.x, self._raw_xd, self.start, self.goal, self.tau, self.f, self.s) # calculate xd using the raw_xd (scale by tau) self.xd = (self._raw_xd / self.tau) # integrate (update xd with xdd) self._raw_xd += self.xdd * dt # integrate (update x with xd) self.x += self.xd * dt # integrate canonical system # ----------------------------- self.s = math.exp(-(self.alpha / self.tau) * self.s_time) self.s_time += dt return self.x, self.xd, self.xdd def learn(self, trajectory, time): ''' Learns the DMP by a given sample trajectory @param sample_trajectory: list of tuples (pos,vel,acc) ''' assert len(trajectory) > 0 totalSteps = int(time / self.delta_t) # frequency if isinstance(trajectory[0], float): trajectory = self._compute_derivatives(trajectory, totalSteps) if len(trajectory[0]) != 3: raise Exception( "trajectory must be a list with 3-tuples [(1,2,3),(4,5,6)]") # get input and output of desired target function time_steps, Fd = self._create_training_set(trajectory, totalSteps) # save input/output of f_target self.target_function_input = time_steps self.Fd = Fd # learn LWR Model for this transformation system self.lwr_model.learn(time_steps, Fd) self._is_learned = True # debugging: compute learned ft(x) self.Fp = [] for x in time_steps: self.Fp.append(self._predict_f(x)) def setup(self, start, goal, duration): self.start = start self.goal = goal self.tau = duration def plan(self, time): trajectory = [] self.x = self.start stepNumber = 0 totalSteps = int(time / self.delta_t) while stepNumber < totalSteps: x, xd, xdd = self._step() trajectory.append([x, xd, xdd]) stepNumber += 1 pos = np.squeeze(np.array(np.matrix(trajectory)[:, 0])) vel = np.squeeze(np.array(np.matrix(trajectory)[:, 1])) acc = np.squeeze(np.array(np.matrix(trajectory)[:, 2])) return pos, vel, acc
class DiscreteDMP: def __init__(self, improved_version=False, reg_model=None): ## time constants choosen for critical damping # s_min self.cutoff = 0.001 # alpha (canonical system parameter) self.alpha = abs(math.log(self.cutoff)) # spring term self.k_gain = 50.0 # damping term self.d_gain = self.k_gain / 4 # time steps (for the run) self.delta_t = 0.001 ## state variables '''start position aka $x_0$''' self.start = 0.0 '''goal position aka $g$''' self.goal = 1.0 '''movement duration: temporal scaling factor $\tau$''' self.tau = 1.0 ## Transformation System # current position, velocity, acceleration '''$x$ (position)''' self.x = 0.0 '''$\dot x$ aka v (velocity)''' self.xd = 0.0 '''$\ddot x$ aka \dot v (acceleration)''' self.xdd = 0.0 # internal variables ''' xd not scaled by tau aka v''' self._raw_xd = 0.0 '''current value of function f (perturbation function)''' self.f = 0.0 # target function input (x) and output (y) self.target_function_input = None self.target_function_ouput = None # debugging (y values predicted by fitted lwr model) self.target_function_predicted = None # do not predict f by approximated function, use values of ft directly - only works if duration is 1.0!! self.use_ft = False # create LWR model and set parameters if not reg_model: # default regression model self.lwr_model = LWR(activation=0.1, exponentially_spaced=True, n_rfs=20) else: # custom regression model self.lwr_model = reg_model # Canonical System self.s = 1.0 # canonical system is starting with 1.0 self.s_time = 0.0 # is the DMP initialized? self._initialized = False self._is_learned = False # set the correct transformation and ftarget functions if improved_version: self._transformation_func = self._transformation_func_improved self._ftarget_func = self._ftarget_func_improved else: self._transformation_func = self._transformation_func_original self._ftarget_func = self._ftarget_func_original # original formulation @staticmethod def _transformation_func_original(k_gain, d_gain, x, raw_xd, start, goal, tau, f, s): return (k_gain * (goal - x) - d_gain * raw_xd + (goal - start) * f) / tau @staticmethod def _ftarget_func_original(k_gain, d_gain, y, yd, ydd, goal, start, tau, s): return ((-1 * k_gain * (goal - y) + d_gain * yd + tau * ydd) / (goal - start)) # improved version of formulation @staticmethod def _transformation_func_improved(k_gain, d_gain, x, raw_xd, start, goal, tau, f, s): return (k_gain * (goal - x) - d_gain * raw_xd - k_gain * (goal - start) * s + k_gain * f) / tau @staticmethod def _ftarget_func_improved(k_gain, d_gain, y, yd, ydd, goal, start, tau, s): # published formula #return ((tau * ydd - d_gain * yd) / k_gain ) + (goal - y) - ((goal - start) * s) # version from dmp_lib/transformation_systm.cpp return ((tau**2 * ydd + d_gain * yd * tau) / k_gain ) - (goal - y) + ((goal - start) * s) # predict f def predict_f(self, x): # if nothing is learned we assume f=0.0 if not self._is_learned: print "WARNING: NO TARGET FUNCTION LEARNED assuming f = 0.0" return 0.0 #return self.lwr_model.predict(np.asarray([x]))[0] return self.lwr_model.predict(x) def setup(self, start, goal, duration): assert not self._initialized # set start position self.start = start # set current x to start (this is only a good idea if we not already started the dmp, which is the case for setup) self.x = start # set goal self.goal = goal self.tau = duration self._initialized = True def reset(self): self.x = 0 self.xd = 0 self.xdd = 0 self._raw_xd = 0 self.f = 0 self.s = 1.0 self.s_time = 0.0 def _create_training_set(self, trajectory, frequency): ''' Prepares the data set for the supervised learning @param trajectory: list of 3-Tuples with (pos,vel,acc) ''' # number of training samples n_samples = len(trajectory) # duration of the movement duration = float(n_samples) / float(frequency) # set tau to duration for learning tau = duration # initial goal and start obtained from trajectory start = trajectory[0][0] goal = trajectory[-1][0] print "create training set of movement from trajectory with %i entries (%i hz) with duration: %f, start: %f, goal: %f" % (n_samples, frequency, duration, start, goal) ##### compute target function input (canonical system) [rollout] # compute alpha_x such that the canonical system drops # below the cutoff when the trajectory has finished alpha = -(math.log(self.cutoff)) # time steps dt = 1.0 / n_samples # delta_t for learning time = 0.0 target_function_input = np.zeros(n_samples) for i in xrange(len(target_function_input)): target_function_input[i] = math.exp(-(alpha / tau) * time) time += dt # import pylab as plt # plt.figure() # plt.plot(target_function_input) # plt.show() # vectorized: #time_steps = np.arange(0, 1.0, dt) #target_function_input2 = np.exp(-(alpha) * time_steps) #if (target_function_input == target_function_input2).all(): # print "same!" # print "target_function_input",len(target_function_input) ##### compute values of target function # the target function (transformation system solved for f, and plugged in y for x) #ft = lambda y, yd, ydd, s: ((-(self.k_gain) * (goal - y) + self.d_gain * yd + tau * ydd) / (goal - start)) ft = lambda y, yd, ydd, s: self._ftarget_func(self.k_gain, self.d_gain, y, yd, ydd, goal, start, tau, s) # evaluate function to get the target values for given training data target_function_ouput = [] for i, d in enumerate(trajectory): # compute f_target(y, yd, ydd) * s #print "s ", target_function_input[i], "y ", d[0], "yd ", d[1], "ydd", d[2], " ft:", ft(d[0], d[1], d[2]) target_function_ouput.append(ft(d[0], d[1], d[2], target_function_input[i])) return target_function_input, np.asarray(target_function_ouput) @staticmethod def compute_derivatives(pos_trajectory, frequency): # ported from trajectory.cpp # no f*****g idea why doing it this way - but hey, the results are better ^^ add_pos_points = 4 #add points to start for _ in range(add_pos_points): first_point = pos_trajectory[0] pos_trajectory.insert(0, first_point) # add points to the end for _ in range(add_pos_points): first_point = pos_trajectory[-1] pos_trajectory.append(first_point) # derive positions vel_trajectory = [] for i in range(len(pos_trajectory) - 4): vel = (pos_trajectory[i] - (8.0 * pos_trajectory[i + 1]) + (8.0 * pos_trajectory[i + 3]) - pos_trajectory[i + 4]) / 12.0 vel *= frequency vel_trajectory.append(vel) # derive velocities acc_trajectory = [] for i in range(len(vel_trajectory) - 4): acc = (vel_trajectory[i] - (8.0 * vel_trajectory[i + 1]) + (8.0 * vel_trajectory[i + 3]) - vel_trajectory[i + 4]) / 12.0 acc *= frequency acc_trajectory.append(acc) result_traj = zip(pos_trajectory[4:], vel_trajectory[2:], acc_trajectory) return result_traj def learn_batch(self, sample_trajectory, frequency): ''' Learns the DMP by a given sample trajectory @param sample_trajectory: list of tuples (pos,vel,acc) ''' assert len(sample_trajectory) > 0 if isinstance(sample_trajectory[0], float): # calculate yd and ydd if sample_trajectory does not contain it print "automatic derivation of yd and ydd" sample_trajectory = self.compute_derivatives(sample_trajectory, frequency) if len(sample_trajectory[0]) != 3: raise Exception("malformed trajectory, has to be a list with 3-tuples [(1,2,3),(4,5,6)]") # get input and output of desired target function target_function_input, target_function_ouput = self._create_training_set(sample_trajectory, frequency) # save input/output of f_target self.target_function_input = target_function_input self.target_function_ouput = target_function_ouput print "target_function_ouput len: ", len(target_function_ouput) # learn LWR Model for this transformation system self.lwr_model.learn(target_function_input, target_function_ouput) # inM = np.asmatrix(target_function_input).T # outM = np.asmatrix(target_function_ouput).T # # learn lwpr model # for i in range(len(target_function_input)): # #print "value", outM[i] # self.lwr_model.update(inM[i], outM[i]) # self._is_learned = True # debugging: compute learned ft(x) self.target_function_predicted = [] for x in target_function_input: self.target_function_predicted.append(self.predict_f(x)) def run_step(self): ''' runs a integration step - updates variables self.x, self.xd, self.xdd ''' assert self._initialized dt = self.delta_t ### integrate transformation system # update f(s) # debugging: use raw function output (time must be 1.0) if self.use_ft: print "DEBUG: using ft without approximation" ftinp = list(self.target_function_input) ft = self.target_function_ouput[ftinp.index(self.s)] self.f = ft else: f = self.predict_f(self.s) self.f = f # calculate xdd (vd) according to the transformation system equation 1 #self.xdd = (self.k_gain * (self.goal - self.x) - self.d_gain * self._raw_xd + (self.goal - self.start) * self.f) / self.tau self.xdd = self._transformation_func(self.k_gain, self.d_gain, self.x, self._raw_xd, self.start, self.goal, self.tau, self.f, self.s) # calculate xd using the raw_xd (scale by tau) self.xd = (self._raw_xd / self.tau) # integrate (update xd with xdd) self._raw_xd += self.xdd * dt # integrate (update x with xd) self.x += self.xd * dt # integrate canonical system self.s = math.exp(-(self.alpha / self.tau) * self.s_time) self.s_time += dt