<style> .reveal { font-size: 24px; } </style> # Trajectory planning Summary from the book of [Professor Peter Corke](https://link.springer.com/book/10.1007%2F978-3-642-20144-8) and [John J. Craig](http://www.mech.sharif.ir/c/document_library/get_file?uuid=5a4bb247-1430-4e46-942c-d692dead831f&groupId=14040) For better look check this note in [HackMD.io](https://hackmd.io/@libernormous/trajectory-planning) by: [Liber Normous](https://hackmd.io/@libernormous) --- # Intro + it is explained on Professor Peter Corke [[1]](https://link.springer.com/book/10.1007%2F978-3-642-20144-8) book page 44 and John J. Craig [[2]](http://www.mech.sharif.ir/c/document_library/get_file?uuid=5a4bb247-1430-4e46-942c-d692dead831f&groupId=14040) page 207 + Path is a way from one point to another point + Trajectory is a path with a specific timing --- # Smooth One-Dimensional Trajectories Smoothness in this context means that its first few temporal derivatives are **continuous**. Typically velocity and acceleration are required to be continuous and sometimes also the derivative of acceleration or jerk ---- ## Ultimate matrix $$ \left(\begin{matrix} s_0\\ s_T\\ \dot{s_0}\\ \dot{s_T}\\ \ddot{s_0}\\ \ddot{s_T}\\ \end{matrix}\right)= \left(\begin{matrix} 0 & 0 & 0 & 0 & 0 & 1\\ T^5 & T^4 & T^3 & T^2 & T & 1\\ 0 & 0 & 0 & 0 & 1 & 0\\ 5T^4 & 4T^3 & 3T^2 & 2T & 1 & 0\\ 0 & 0 & 0 & 2 & 0 & 0\\ 20T^3 & 12T^2 & 6T & 2 & 0 & 0\\ \end{matrix}\right) \left(\begin{matrix} A\\ B\\ C\\ D\\ E\\ F\\ \end{matrix}\right) $$ ---- ## Design steps 1. Decide your initial position $s_0$ and final position $s_T$ 2. Decide your initial velocity $\dot{s_0}$ and final velocity $\dot{s_T}$ 3. Decide your initial acceleration $\ddot{s_0}$ and final acceleration $\ddot{s_T}$ 4. Find coefficient $A\ B\ C\ D\ E\ F$ 5. How? inverse the matrix of course! 6. Note: this step is only for 1 axis, repeat the process for another axis 7. Note: $s_0$ is only for when $t=0$, and $s_t$ is only for when $t=T$ ---- ## Actuation step $$ \left(\begin{matrix} s(t)\\ \dot{s(t)}\\ \ddot{s(t)}\\ \end{matrix}\right)= \left(\begin{matrix} t^5 & t^4 & t^3 & t^2 & t & 1\\ 5t^4 & 4t^3 & 3t^2 & 2t & 1 & 0\\ 20t^3 & 12t^2 & 6t & 2 & 0 & 0\\ \end{matrix}\right) \left(\begin{matrix} A\\ B\\ C\\ D\\ E\\ F\\ \end{matrix}\right) $$ Depends on what you want. Do you want to actuate using 1. position $s(t)$ (for direct animation) 2. speed $\dot{s(t)}$ (for motor) 3. acceleration $\ddot{s(t)}$ (IDK) --- # example $$ T=10 $$ $$ \left(\begin{matrix} s(0)\\ s(10)\\ \dot{s}(0)\\ \dot{s}(10)\\ \ddot{s}(0)\\ \ddot{s}(10)\\ \end{matrix}\right)=\left(\begin{matrix} 0\\ 1\\ 0\\ 0\\ 0\\ 0\\ \end{matrix}\right) $$ $$ \left(\begin{matrix} 0 & 0 & 0 & 0 & 0 & 1\\ T^5 & T^4 & T^3 & T^2 & T & 1\\ 0 & 0 & 0 & 0 & 1 & 0\\ 5T^4 & 4T^3 & 3T^2 & 2T & 1 & 0\\ 0 & 0 & 0 & 2 & 0 & 0\\ 20T^3 & 12T^2 & 6T & 2 & 0 & 0\\ \end{matrix}\right)^{-1} \left(\begin{matrix} 0\\ 1\\ 0\\ 0\\ 0\\ 0\\ \end{matrix}\right)= \left(\begin{matrix} A\\ B\\ C\\ D\\ E\\ F\\ \end{matrix}\right) $$ --- ## CODE ```python=0 import numpy as np import matplotlib.pyplot as plt from math import sin,acos,sqrt # %% def traj_poly(s0,stf,sd0,sdtf,sdd0,sddtf,t): t0=t[0] # NOTe! t0 must always 0 tf=t[-1] if t0 != 0: return 0 #solving for equation coef = np.zeros((6,1)) #we are looking for this param = np.asarray([[s0],[stf],[sd0],[sdtf],[sdd0],[sddtf]]) mat = np.asarray([[0,0,0,0,0,1], [tf**5,tf**4,tf**3,tf**2,tf,1], [0,0,0,0,1,0], [5*tf**4,4*tf**3,3*tf**2,2*tf,1,0], [0,0,0,2,0,0], [20*tf**3,12*tf**2,6*tf,2,0,0]]) mat_i = np.linalg.inv(mat) #inverse coef = np.matmul(mat_i,param) #acquiring A B C D E F #using equation zeros = np.zeros(t.shape) ones = np.ones(t.shape) twos = ones*2 mat = np.asarray([ #the original equation [t**5,t**4,t**3,t**2,t,ones], [5*t**4,4*t**3,3*t**2,2*t,ones,zeros], [20*t**3,12*t**2,6*t,twos,zeros,zeros] ]) coef_tensor=(np.repeat(coef,t.size,axis=1)) coef_tensor=np.reshape(coef_tensor,(coef_tensor.shape[0],1,coef_tensor.shape[1])) # d = np.tensordot(mat,coef_tensor,axes=[1, 0]).diagonal(axis1=1, axis2=3) #alternative way res = np.einsum('mnr,ndr->mdr', mat, coef_tensor) return res ``` ---- ## Example CODE: ```python=0 # %% t = np.arange(1,11) y = traj_poly(0,1,0,0,0,0,t) plt.plot(y[0,0,:],'r',y[1,0,:],'g',y[2,0,:],'b') ``` ![](https://i.imgur.com/2opNhWJ.png) ---- ## Shift the time To shift a signal $f(x)=a+bx$ to the right (delay), we can do $f(x-\tau)=a+b(x-\tau)$ with $\tau$ as the amount of delay. Our matrix became more general like this: $$ \left(\begin{matrix} s_0\\ s_T\\ \dot{s_0}\\ \dot{s_T}\\ \ddot{s_0}\\ \ddot{s_T}\\ \end{matrix}\right)= \left(\begin{matrix} T_0^5 & T_0^4 & T_0^3 & T_0^2 & T_0 & 1\\ T_f^5 & T_f^4 & T_f^3 & T_f^2 & T_f & 1\\ 5T_0^4 & 4T_0^3 & 3T_0^2 & 2T_0 & 1 & 0\\ 5T_f^4 & 4T_f^3 & 3T_f^2 & 2T_f & 1 & 0\\ 20T_0^3 & 12T_0^2 & 6T_0 & 2 & 0 & 0\\ 20T_f^3 & 12T_f^2 & 6T_f & 2 & 0 & 0\\ \end{matrix}\right) \left(\begin{matrix} A\\ B\\ C\\ D\\ E\\ F\\ \end{matrix}\right) $$ ---- Then you can actuate with $T_0<t<T_f$ $$ \left(\begin{matrix} s(t)\\ \dot{s(t)}\\ \ddot{s(t)}\\ \end{matrix}\right)= \left(\begin{matrix} t^5 & t^4 & t^3 & t^2 & t & 1\\ 5t^4 & 4t^3 & 3t^2 & 2t & 1 & 0\\ 20t^3 & 12t^2 & 6t & 2 & 0 & 0\\ \end{matrix}\right) \left(\begin{matrix} A\\ B\\ C\\ D\\ E\\ F\\ \end{matrix}\right) $$ ---- CODE: ```python=0 def traj_poly2(s0,stf,sd0,sdtf,sdd0,sddtf,t0,tf,step=1): #arranging time t = np.arange(t0,tf+step,step) #solving for equation coef = np.zeros((6,1)) #we are looking for this param = np.asarray([[s0],[stf],[sd0],[sdtf],[sdd0],[sddtf]]) mat = np.asarray([ [t0**5,t0**4,t0**3,t0**2,t0,1], [tf**5,tf**4,tf**3,tf**2,tf,1], [5*t0**4,4*t0**3,3*t0**2,2*t0,1,0], [5*tf**4,4*tf**3,3*tf**2,2*tf,1,0], [20*t0**3,12*t0**2,6*t0,2,0,0], [20*tf**3,12*tf**2,6*tf,2,0,0] ]) mat_i = np.linalg.inv(mat) #inverse coef = np.matmul(mat_i,param) #acquiring A B C D E F #using equation zeros = np.zeros(t.shape) ones = np.ones(t.shape) twos = ones*2 mat = np.asarray([ #the original equation [(t)**5,(t)**4,(t)**3,(t)**2,(t),ones], [5*(t)**4,4*(t)**3,3*(t)**2,2*(t),ones,zeros], [20*(t)**3,12*(t)**2,6*(t),twos,zeros,zeros] ]) coef_tensor=(np.repeat(coef,t.size,axis=1)) coef_tensor=np.reshape(coef_tensor,(coef_tensor.shape[0],1,coef_tensor.shape[1])) # d = np.tensordot(mat,coef_tensor,axes=[1, 0]).diagonal(axis1=1, axis2=3) #alternative way res = np.einsum('mnr,ndr->mdr', mat, coef_tensor) time = t possi = res[0,0,:] speed = res[1,0,:] accel = res[2,0,:] return (time,possi,speed,accel) ``` ---- ## Example CODE: ```python=0 #Call function y = traj_poly2(40,0,0,0,0,0,0,10,0.1) plt.subplot(3,2,1) plt.plot(y[0],y[1],'r') plt.title('Position') plt.subplot(3,2,3) plt.plot(y[0],y[2],'g') plt.title('Speed') plt.subplot(3,2,5) plt.plot(y[0],y[3],'b') plt.title('Acceleration') y = traj_poly2(40,0,0,0,0,0,10,20,0.1) plt.subplot(3,2,2) plt.plot(y[0],y[1],'r') plt.title('Position delayed') plt.subplot(3,2,4) plt.plot(y[0],y[2],'g') plt.title('Speed delayed') plt.subplot(3,2,6) plt.plot(y[0],y[3],'b') plt.title('Acceleration delayed') ``` ---- RESULT: ![](https://i.imgur.com/tgQfCLT.png) --- # Linear segment with parabolic blends ![](https://i.imgur.com/Tmrg0No.png) ---- 1. Because the previous movement method actuates our motor to full speed only at 50% of the total time 2. That case, we need more linear area to drive our motor full speed 3. Check Ref [[3]](https://smartech.gatech.edu/bitstream/handle/1853/41948/ParabolicBlends.pdf). This guy has a very intuitive explanation that helps me write the code 4. We use via a transition between linear movement 5. Around this via we introduce blend time to smooth the speed transition 6. On linear area speed is constant 7. On parabolic area acceleration is constant but speed is changing, for example from $v_0$ to $0$ then to $v_f=-v_0$. ---- ## Linear phase At the linear phase we use this equation $$ q(t) = q_i + v_i(t-T_i)\\ \dot{q}(t) = v_i\\ \ddot{q}(t) = 0 $$ Where $T_i$ is the time delay. $v_i$ can be calculated or specified I have 2 different codes for different needs. If you only use linear you might want `line2()`. `line()` will be used in our next function ---- How to use it? 1. Specify a. Initial position b. Speed c. Time initial d. Time final 2. Or specify a. Position initial b. Position final c. Time initial d. Time final ---- CODE: ```python=0 # function for linear interpolation def line(point0, v0, t0, t1, step=1): # Generate a series of timestep t = np.arange(t0, t1+step,step)#makit one column # Calculate velocity v = v0 #time shift Ti = t0 #equation s = point0 + v*(t-Ti) v = np.ones(t.size)*v a = np.zeros(t.size) return (t,s,v,a) # function for linear interpolation def line2(point0, point1, t0, t1, step=1): # Generate a series of timestep t = np.arange(t0, t1+step,step)#makit one column # Calculate velocity v = (point1-point0)/(t1-t0) #time shift Ti = t0 #equation s = point0 + v*(t-Ti) v = np.ones(t.size)*v a = np.zeros(t.size) return (t,s,v,a) ``` ---- ### Example 1 CODE: ```python=0 # %% # Call function y = line(0,1,-10,10,1) plt.title('LINE') plt.plot(y[0],y[1]) ``` ![](https://i.imgur.com/UMryohx.png) ---- ### Example 2 CODE: ```python=0 #%% # Call function y = line2(10,20,-10,10,1) plt.title('LINE2') plt.plot(y[0],y[1]) ``` ![](https://i.imgur.com/cBfzRTX.png) ---- ## Parabolic phase This phase we use equation $$ q(t) = q_i + v_{i-1}(t-T_i) + \frac{1}{2}a(t-T_i+\frac{t_i^b}{2})^2\\ \dot{q}(t) = v_{i-1}(t-T_i) + a(t-T_i+\frac{t_i^b}{2})\\ \ddot{q}(t) = a $$ $q_i$ is the starting value. $i$ is the index of the via. $t$ is between $T_i-\frac{t_i^b}{2}<t<T_i+\frac{t_i^b}{2}$. $T_i$ is the time via happens and $t_i^b$ is the amount of blend *around* that via. So the parabolic phase start half $t_i^b$ before $T_i$ and half after that. Because the signal start at $T_i-\frac{t_i^b}{2}$ so we shift the equation to $T_i-\frac{t_i^b}{2}$. ---- CODE: ```python=0 def parab(p0, v0, v1, t0, t1, step=1): # Generate a series of timestep t = np.arange(t0, t1+step,step) #calculate acceleration a = (v1-v0)/(t1-t0) #time shift Ti=t0 # equation s = p0 +v0*(t-Ti) +0.5*a*(t-Ti)**2 v = v0 + a*(t-Ti) a = np.ones(t.size)*a return (t,s,v,a) ``` How to use it? Specify: 1. Initial pose 2. Speed before via 3. Speed after via 4. Start of period which is $T_i-\frac{t_i^b}{2}$ 5. Final of period which is $T_i+\frac{t_i^b}{2}$ ---- CODE: ```python=0 #%% y = parab(0, 5, 0, 0, 100, step=1) plt.plot(y[0],y[1]) y = parab(y[1][-1], 0, -5, 100, 200, step=1) plt.plot(y[0],y[1]) y = parab(50, 5, -5, 50, 250, step=1) plt.plot(y[0],y[1]) ``` RESULT: ![](https://i.imgur.com/mPVhTIp.png) ---- ## Linear and parabolic phase combined CODE: ```python=0 # %% def lspb(via,dur,tb): #1. It must start and end at the first and last waypoint respectively with zero velocity #2. Note that during the linear phase acceleration is zero, velocity is constant and position is linear in time # if acc.min < 0 : # print('acc must bigger than 0') # return 0 if ((via.size-1) != dur.size): print('duration must equal to number of segment which is via-1') return 0 if (via.size <2): print('minimum of via is 2') return 0 if (via.size != (tb.size)): print('acc must equal to number of via') return 0 #=====CALCULATE-VELOCITY-EACH-SEGMENT===== v_seg=np.zeros(dur.size) for i in range(0,len(via)-1): v_seg[i]=(via[i+1]-via[i])/dur[i] #=====CALCULATE-ACCELERATION-EACH-VIA===== a_via=np.zeros(via.size) a_via[0]=(v_seg[0]-0)/tb[0] for i in range(1,len(via)-1): a_via[i]=(v_seg[i]-v_seg[i-1])/tb[i] a_via[-1]=(0-v_seg[-1])/tb[-1] #=====CALCULATE-TIMING-EACH-VIA===== T_via=np.zeros(via.size) T_via[0]=0.5*tb[0] for i in range(1,len(via)-1): T_via[i]=T_via[i-1]+dur[i-1] T_via[-1]=T_via[-2]+dur[-1] #=====GENERATING-CHART/GRAPH/FIGURE===== # q(t) = q_i + v_{i-1}(t-T_i) + \frac{1}{2}a(t-T_i+\frac{t_i^b}{2})^2 #parabolic phase # q(t) = q_i + v_i*(t-T_i) #linear phase #parabolic t,s,v,a = parab(via[0], 0, v_seg[0], T_via[0]-0.5*tb[0], T_via[0]+0.5*tb[0], step=1) time = t pos = s speed = v accel = a for i in range(1,len(via)-1): # linear t,s,v,a = lerp(pos[-1],v_seg[i-1],T_via[i-1]+0.5*tb[i],T_via[i]-0.5*tb[i+1],0.01) time = np.concatenate((time,t)) pos = np.concatenate((pos,s)) speed = np.concatenate((speed,v)) accel = np.concatenate((accel,a)) #parabolic t,s,v,a = parab(pos[-1], v_seg[i-1], v_seg[i], T_via[i]-0.5*tb[i+1], T_via[i]+0.5*tb[i+1], 0.01) time = np.concatenate((time,t)) pos = np.concatenate((pos,s)) speed = np.concatenate((speed,v)) accel = np.concatenate((accel,a)) # linear t,s,v,a = lerp(pos[-1],v_seg[-1],T_via[-2]+0.5*tb[-2],T_via[-1]-0.5*tb[-1],0.01) time = np.concatenate((time,t)) pos = np.concatenate((pos,s)) speed = np.concatenate((speed,v)) accel = np.concatenate((accel,a)) #parabolic t,s,v,a = parab(pos[-1], v_seg[-1], 0, T_via[-1]-0.5*tb[-1], T_via[-1]+0.5*tb[-1], 0.01) time = np.concatenate((time,t)) pos = np.concatenate((pos,s)) speed = np.concatenate((speed,v)) accel = np.concatenate((accel,a)) print('v seg = ',v_seg, '\na via = ',a_via, '\nT via = ',T_via, '\ntime = ',time, '\npos = ',pos) return(v_seg,a_via,T_via,time,pos,speed,accel) ``` ---- How to use it? Specify: 1. All the via 2. All the duration between via 3. Blend time In Ref [[1]](https://link.springer.com/book/10.1007%2F978-3-642-20144-8) [[2]](http://www.mech.sharif.ir/c/document_library/get_file?uuid=5a4bb247-1430-4e46-942c-d692dead831f&groupId=14040) they prefer acceleration on each via as input. But I follow [[3]](https://smartech.gatech.edu/bitstream/handle/1853/41948/ParabolicBlends.pdf) to use blend time as an input because it is more intuitive, easy to imagine and understand. Instead of duration between via, we could use specific time each via, it will be easier to code. But don't input $t_b=0$ that means infinite acceleration in that blend $a=\frac{(v_1-v_0)}{t_b}$ ---- CODE: ```python=0 # %% via = np.asarray([0,40,0,40,0]) dur = np.asarray([20,20,20,20]) tb = np.asarray([1,1,1,1,1])*5 res=lspb(via,dur,tb) plt.plot(res[2],via,'*',res[3],res[4]) ``` RESULT: ![](https://i.imgur.com/LVZffTD.png) ---- We can plot acceleration, speed, together. As you can see that the speed is zero at the via with constant accelereration ```python=0 # %% plt.plot(res[2],np.zeros(via.size),'*') plt.plot(res[3],res[5]) plt.plot(res[3],res[6]) ``` ![](https://i.imgur.com/GLwGNHx.png) ---- Let's try anoter CODE: ```python=0 # %% via = np.asarray([0,30,40,10,0])-20 dur = np.asarray([20,20,20,20]) tb = np.asarray([1,1,1,1,1])*5 res=lspb(via,dur,tb) plt.plot(res[2],via,'*',res[3],res[4]) ``` ![](https://i.imgur.com/4a59pkN.png) ---- This thime the transition speed is not zero ```python=0 # %% plt.plot(res[2],np.zeros(via.size),'*') plt.plot(res[3],res[5]) plt.plot(res[3],res[6]) ``` ![](https://i.imgur.com/CU2yTOo.png) --- # Interpolation If you have 2 points, and you want to know a point in between. Interpolation works by specifying a scale $s$ between $0$ and $1$. $s=0$ means first point. $s=1$ means last point. $s=0.5$ is middle value between first and last point. ---- ## Linear interpolation (LERP) $$ \textrm{Lerp}(p_0, p_1, s)=(1-s)p_0+(s){p_1} $$ CODE: ```python=0 # linear interopolation def lerp(p0, p1, s): # p0 : point 1 # p1 : point 2 # s : scale, 0 to 1, 0 is p0, 1 is p1, and 0.5 is value between p0 and p1 return (1 - s) * p0 + s * p1 def lerp2(p0, p1, t0, t1, step=1): # map t0 to 0 and t1 to 1 t = np.arange(t0,t1+step,step) s = (t-t0)/(t1-t0) #map function pos = (1 - s) * p0 + s * p1 return (t,pos) ``` ---- Example 1: First point = 10, last point = 50. what is value when $s=\{0,0.5,0.7,1\}$ ```python=0 s = np.asarray([0,0.5,0.7,1]) y = lerp(10, 50, s) plt.plot(s,y,'o') ``` Result: ``` [10. 30. 38. 50.] ``` ![](https://i.imgur.com/gRidxjL.png) ---- By using the same idea we can build a function but not scale based but time based. So we can input first point with its time step, and last point with its time step. Basically we create a time step array and map each value in the time step array to be between $0$ and $1$. Example 2: $p=40$ when $t=10$ and $p=10$ when $t=40$. Plot the graph ```python=0 y = lerp2(40, 10, 10, 40, step=1) plt.plot(y[0],y[1]) ``` Result: ![](https://i.imgur.com/J0H1w35.png) ---- ## Spherical interpolation (SLERP) This kind of interpolation are linear based on the relation between points and scale but the point path it creates is arch like path. The center of the arch is always on the origin of coordinate frame. This function is really useful if we have 2 quaternion vectors. By this function we can interpolate these 2 quaternion vectors to make a direct rotation path. $$ \cos\Omega = q_1q_2\\ q_1q_2 = q_{1x}q_{2x}+q_{1y}q_{2y}+q_{1z}q_{2z}\\ \textrm{Slerp}(q_1,q_2,s) = \frac{\sin((1-s)\Omega)}{\sin\Omega}q_1+\frac{\sin(s\Omega)}{\sin\Omega}q_2 $$ ---- ![](https://upload.wikimedia.org/wikipedia/commons/thumb/b/b3/Slerp_factor_explanation.png/800px-Slerp_factor_explanation.png) ---- CODE: ```python=0 # %% # Spherical linear interopolation def slerp(q1,q2,s): #Normalize q1_norm = q1/np.sum(np.square(q1)) q2_norm = q2/np.sum(np.square(q2)) #dor product q1\{w1,x1,y1,z1} q2\{w2,x2,y2,z2} q1q2=w1w2+x1x2+y1y2+z1z2 q1q2=q1_norm*q2_norm q1q2=np.sum(q1q2) #omega omega = acos(q1q2) #main eq res = np.sin((1-s)*omega)/np.sin(omega)*q1 + np.sin(s*omega)/np.sin(omega)*q2 return res #%% def slerp2(q1,q2,t0,t1,step=0.1): #arangig scale t = np.arange(t0,t1+step,step) s = (t-t0)/(t1-t0) #map t to scale #Normalize q1_norm = q1/np.sum(np.square(q1)) q2_norm = q2/np.sum(np.square(q2)) #dor product q1\{w1,x1,y1,z1} q2\{w2,x2,y2,z2} q1q2=w1w2+x1x2+y1y2+z1z2 q1q2=q1_norm*q2_norm q1q2=np.sum(q1q2) #omega omega = acos(q1q2) #main eq res = np.sin((1-s)*omega)/np.sin(omega)*q1 + np.sin(s*omega)/np.sin(omega)*q2 return (t,res) ``` ---- Example 1 : If we have $v0=(5,0,0)^T$ and $v1=(0,0,5)^T$. What's value in between the scale of $s={0,0.5,0.8,1}$ ```python=0 s = np.arange(0,1,0.01) s = np.asarray([0,0.5,0.8,1]) p0 = np.asarray([ [5], [0], [0] ]) p1 = np.asarray([ [0], [0], [5] ]) pos = slerp(p0, p1, s) print(pos) ``` ---- Result: ``` [[5. 3.53553391 1.54508497 0. ] [0. 0. 0. 0. ] [0. 3.53553391 4.75528258 5. ]] ``` ![](https://i.imgur.com/4mcXCC1.png) ---- Same as LERP, we can construct a version of SLERP that uses time step as trajectory. Example 1 : We have $v0=(0,-5,-5)^T$ at $t=0$ and $v1=(0,-5,5)^T$ at $t=10$. Plot the graph. ```python=0 # %% %matplotlib inline from mpl_toolkits.mplot3d import axes3d p0 = np.asarray([ [0], [-5], [-5] ]) p1 = np.asarray([ [0], [-5], [5] ]) t, pos = slerp2(p0, p1, 0, 10, 0.1) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.scatter(pos[0],pos[1],pos[2]) ``` ---- Result: ![](https://i.imgur.com/AiB24ne.png) ---- If you are curious about time plot of example 2, here I give you an answer. This is the plot of Y-axes across time. ![](https://i.imgur.com/1v6RNwt.png) --- # MATLAB class README **How to use** 1. $s \in [0,1]$ so first parameter always starts with $0$ and ends with $1$. Define in rows vector. For example `p_param = [0; 0.5; 1];` 3. Second parameter is velocity. Define in rows vector. For example `v_param = [0; 0.5; 0];` 4. Thirs parameter is acceleration, It is not mandatory, you can put `[]`. Define in rows vector. For example `a_param = [0; 0.5; 0];` <details> <summary> CODE (not using acceleration) </summary> ```c= %% clc;clear; gen_traj = gen_traj(); T = 60; t_series = 0:0.1:T; p_param = [0; 1]; v_param = [0; 1]; gen_traj.pos_vel_T(p_param, v_param, t_series); gen_traj.plot_s_sdot(); ``` </details> <details> <summary> CODE (using acceleration) </summary> ```c= %% clear; clc; gen_traj = gen_traj(); T = 60; t_series = 0:0.1:T; p_param = [0; 1]; v_param = [0; 0]; a_param = [0; 0]; gen_traj.pos_vel_acc_T(p_param, v_param, a_param, t_series); s_t = gen_traj.s_t; s_t_dot = gen_traj.s_t_dot; s_t_ddot = gen_traj.s_t_ddot; t = t_series; gen_traj.plot_s_sdot_sddot(); ``` </details> <details> <summary> CLASS </summary> ```c= classdef gen_traj < handle %GEN_TRAJ Summary of this class goes here % Detailed explanation goes here properties s_t; s_t_dot; s_t_ddot; coef_ABCD; mat_design; mat_utilize; end properties (Access = private) n1 n2 n3 deg t_series end methods function obj = gen_traj() end function obj = pos_vel_T(obj, p_param, v_param, t_series) obj.t_series = t_series; %design design_pos_vel(obj, p_param, v_param, t_series); % utilizing utilize_pos_vel(obj, t_series); end function obj = pos_vel_acc_T(obj, p_param, v_param, a_param, t_series) obj.t_series = t_series; T = t_series(end); obj.n1 = length(p_param); % obj.n2 = length(v_param); obj.n3 = length(a_param); obj.deg = obj.n1+obj.n2+obj.n3-1; %design mat_p = zeros(obj.n1, obj.deg+1); for i=0:obj.n1-1 for j=0:obj.deg t = (i)/(obj.n1-1)*T; % i is from 1 to n1+1; t is from 0/(n1-1) ~ (n1-1)/(n1-1) pow = j-1; % j is from 1 to deg+1; pow is from 0 to deg mat_p(i+1,j+1)=t^j; end end mat_v = zeros(obj.n2, obj.deg+1); for i=0:obj.n2-1 for j=1:obj.deg t = (i)/(obj.n2-1)*T; % i is from 1 to n2+1; t is from 0/(n2-1) ~ (n2-1)/(n2-1) pow = j-1; % j is from 1 to deg+1; pow is from 0 to deg mul = j; mat_v(i+1,j+1)=mul*t^pow; end end mat_a = zeros(obj.n3, obj.deg+1); for i=0:obj.n3-1 for j=2:obj.deg t = (i)/(obj.n3-1)*T; % i is from 1 to n3+1; t is from 0/(n3-1) ~ (n3-1)/(n3-1) pow = j-2; % j is from 1 to deg+1; pow is from 0 to deg mul1 = j; mul2 = j-1; mat_a(i+1,j+1)=mul1 * mul2 * t^pow; end end obj.mat_design = [mat_p; mat_v; mat_a]; obj.coef_ABCD = obj.mat_design\[p_param;v_param;a_param]; % Utilize t = t_series; mat_p_u = zeros(1, obj.deg+1, length(t)); mat_v_u = zeros(1, obj.deg+1, length(t)); mat_a_u = zeros(1, obj.deg+1, length(t)); for i=1:length(t) for j=0:obj.deg pow = j-1; % j is from 1 to deg+1; pow is from 0 to deg mat_p_u(1,j+1, i)=t(i)^j; end for j=1:obj.deg pow = j-1; % j is from 1 to deg+1; pow is from 0 to deg mul = j; mat_v_u(1,j+1,i)=mul*t(i)^pow; end for j=2:obj.deg pow = j-2; % j is from 1 to deg+1; pow is from 0 to deg mul1 = j; mul2 = j-1; mat_a_u(1,j+1,i)=mul1 * mul2 * t(i)^pow; end end obj.mat_utilize = [mat_p_u;mat_v_u;mat_a_u]; obj.s_t = zeros(length(t), 1); obj.s_t_dot = zeros(length(t), 1); obj.s_t_ddot = zeros(length(t), 1); for i=1:length(t) obj.s_t(i) =obj.mat_utilize(1,:, i)*obj.coef_ABCD; obj.s_t_dot(i) =obj.mat_utilize(2,:, i)*obj.coef_ABCD; obj.s_t_ddot(i) =obj.mat_utilize(3,:, i)*obj.coef_ABCD; end end function obj = design_pos_vel(obj, p_param, v_param, t_series) T = t_series(end); obj.n1 = length(p_param); % obj.n2 = length(v_param); obj.deg = obj.n1+obj.n2-1; mat_p = zeros(obj.n1, obj.n1+obj.n2); for i=0:obj.n1-1 for j=0:obj.deg t = (i)/(obj.n1-1)*T; % i is from 1 to n1+1; t is from 0/(n1-1) ~ (n1-1)/(n1-1) pow = j-1; % j is from 1 to deg+1; pow is from 0 to deg mat_p(i+1,j+1)=t^j; end end mat_v = zeros(obj.n2, obj.n1+obj.n2); for i=0:obj.n2-1 for j=1:obj.deg t = (i)/(obj.n2-1)*T; % i is from 1 to n2+1; t is from 0/(n2-1) ~ (n2-1)/(n2-1) pow = j-1; % j is from 1 to deg+1; pow is from 0 to deg mul = j; mat_v(i+1,j+1)=mul*t^pow; end end obj.mat_design = [mat_p;mat_v]; obj.coef_ABCD = obj.mat_design\[p_param;v_param]; end function obj = utilize_pos_vel(obj, t_series) % Utilize t = t_series; mat_p_u = zeros(1, obj.n1+obj.n2, length(t)); mat_v_u = zeros(1, obj.n1+obj.n2, length(t)); for i=1:length(t) for j=0:obj.deg pow = j-1; % j is from 1 to deg+1; pow is from 0 to deg mat_p_u(1,j+1, i)=t(i)^j; end for j=1:obj.deg pow = j-1; % j is from 1 to deg+1; pow is from 0 to deg mul = j; mat_v_u(1,j+1,i)=mul*t(i)^pow; end end obj.mat_utilize = [mat_p_u;mat_v_u]; obj.s_t = zeros(length(t), 1); obj.s_t_dot = zeros(length(t), 1); for i=1:length(t) obj.s_t(i)=obj.mat_utilize(1,:, i)*obj.coef_ABCD; obj.s_t_dot(i)=obj.mat_utilize(2,:, i)*obj.coef_ABCD; end end function plot_s_sdot_sddot(obj) t = obj.t_series; fig1000=figure(1000); fig1000.Name=('using s, s_dot, s_ddot'); subplot(3,1,1); plot(t,obj.s_t, 'linewidth', 2); ylabel('$s(t)$', 'interpreter', 'latex'); xlabel('$t$', 'interpreter', 'latex'); grid on; set(gca,'FontSize',14); set(gca,'FontName','serif'); set(gca,'FontWeight','bold'); set(gca,'LineWidth',2); subplot(3,1,2); plot(t,obj.s_t_dot, 'linewidth', 2); ylabel('$\dot s(t)$', 'interpreter', 'latex'); xlabel('$t$', 'interpreter', 'latex');grid on; set(gca,'FontSize',14); set(gca,'FontName','serif'); set(gca,'FontWeight','bold'); set(gca,'LineWidth',2); subplot(3,1,3); plot(t,obj.s_t_ddot, 'linewidth', 2); ylabel('$\ddot s(t)$', 'interpreter', 'latex'); xlabel('$t$', 'interpreter', 'latex');grid on; set(gca,'FontSize',14); set(gca,'FontName','serif'); set(gca,'FontWeight','bold'); set(gca,'LineWidth',2); end function plot_s_sdot(obj) t = obj.t_series; fig1001=figure(1001); fig1001.Name=('using s and s_dot only'); subplot(2,1,1); plot(t,obj.s_t, 'linewidth', 2); ylabel('$s(t)$', 'interpreter', 'latex'); xlabel('$t$', 'interpreter', 'latex'); grid on; set(gca,'FontSize',14); set(gca,'FontName','serif'); set(gca,'FontWeight','bold'); set(gca,'LineWidth',2); subplot(2,1,2); plot(t,obj.s_t_dot, 'linewidth', 2); ylabel('$\dot s(t)$', 'interpreter', 'latex'); xlabel('$t$', 'interpreter', 'latex');grid on; set(gca,'FontSize',14); set(gca,'FontName','serif'); set(gca,'FontWeight','bold'); set(gca,'LineWidth',2); end end end ``` </details> --- # References [[1] Robotics vision and control Professor Peter Corke p.44](https://link.springer.com/book/10.1007%2F978-3-642-20144-8) [[2] Introduction to robotics John J. Craig p.207](http://www.mech.sharif.ir/c/document_library/get_file?uuid=5a4bb247-1430-4e46-942c-d692dead831f&groupId=14040) [[3] Turning Paths Into Trajectories Using Parabolic Blends Tobias Kunz and Mike Stilman](https://smartech.gatech.edu/bitstream/handle/1853/41948/ParabolicBlends.pdf) [[4] wikipedia SLERP](https://en.wikipedia.org/wiki/Slerp) ###### tags: `robot`
{"metaMigratedAt":"2023-06-15T05:58:52.453Z","metaMigratedFrom":"YAML","title":"Trajectory planning","breaks":false,"slideOptions":"{\"transition\":\"slide\"}","contributors":"[{\"id\":\"5873a290-d7f7-4feb-90ca-77e42841519a\",\"add\":46674,\"del\":16826}]"}
    1686 views