(* ::Package:: *) (* ::Title::Closed:: *) (*Modern Robotics: Mechanics, Planning, and Control*) (*Code Library*) (* ::Text:: *) (* :Name: ModernRobotics` *) (* :Book: Modern Robotics: Mechanics, Planning, and Control *) (* :Author: Huan Weng, Jarvis Schultz, Mikhail Todes*) (* :Contact: huanweng@u.northwestern.edu*) (* :Summary: This package is the code library accompanying the book. *) (* :Package Version: 1.1.0*) (* :Mathematica Version: 11.3 *) (* Tested in Mathematica 11.3 *) BeginPackage["ModernRobotics`"]; (* ::Section::Closed:: *) (*USAGE*) (* ::Subsection::Closed:: *) (*BASIC HELPER FUNCTIONS*) NearZero::usage= "NearZero[s]: Takes a scalar. Checks if the scalar is small enough to be neglected. Example: Input: judge = NearZero[-10^-7] Output: True" (* ::Subsection::Closed:: *) (*CHAPTER 3: RIGID-BODY MOTION*) RotInv::usage= "RotInv[R]: Takes a 3x3 rotation matrix. Returns the inverse (transpose). Example: Input: invR = RotInv[{{0,0,1},{1,0,0},{0,1,0}}] Output: {{0,1,0},{0,0,1},{1,0,0}}" VecToso3::usage= "VecToso3[omg]: Takes a 3-vector (angular velocity). Returns the skew symmetric matrix in so3. Example: Input: so3mat = VecToso3[{{1},{2},{3}}] Output: {{0,-3,2},{3,0,-1},{-2,1,0}}" so3ToVec::usage= "so3ToVec[so3mat]: Takes a 3x3 skew-symmetric matrix (an element of so(3)). Returns the corresponding vector (angular velocity). Example: Input: omg = so3ToVec[{{0,-3,2},{3,0,-1},{-2,1,0}}] Output: {{1},{2},{3}}" AxisAng3::usage= "AxisAng3[expc3]: Takes A 3-vector of exponential coordinates for rotation. Returns unit rotation axis omghat and the corresponding rotation angle theta. The first element of the output is the unit rotation axis column vector (omghat) and the second element is theta. Example: Input: {omghat,theta} = AxisAng3[{{1,2,3}}\[Transpose]]//N Output: {{{0.2672612419124244},{0.5345224838248488},{0.8017837257372732}}, 3.7416573867739413}" MatrixExp3::usage= "MatrixExp3[so3mat]: Takes a so(3) representation of exponential coordinates. Returns R in SO(3) that is achieved by rotating about omghat by theta from an initial orientation R=I. Example: Input: R = MatrixExp3[{{0,-3,2},{3,0,-1},{-2,1,0}}]//N Output: {{-0.694921,0.713521,0.0892929},{-0.192007,-0.303785,0.933192}, {0.692978,0.63135,0.348107}}" MatrixLog3::usage= "MatrixLog3[R]: Takes R (rotation matrix). Returns the corresponding so(3) representation of exponential coordinates. Example: Input: so3mat = MatrixLog3[{{0,0,1},{1,0,0},{0,1,0}}]//N Output: {{0,-1.2092,1.2092},{1.2092,0,-1.2092},{-1.2092,1.2092,0}}" DistanceToSO3::usage= "DistanceToSO3[mat]: Takes mat as a 3x3 matrix. Returns the Frobenius norm to describe the distance of mat from the SO(3) manifold. Computes the distance from mat to the SO(3) manifold using the following method: If det(mat) <= 0, return a large number. If det(mat) > 0, return norm(mat'.mat - I). Example: Input: d = DistanceToSO3[{{1.0,0.0,0.0},{0.0,0.1,-0.95},{0.0,1.0,0.1}}] Output: 0.088353" TestIfSO3::usage= "TestIfSO3[mat]: Takes mat as a 3x3 matrix. Check if mat is close to or on the manifold SO(3). Example: Input: judge = TestIfSO3[{{1.0,0.0,0.0},{0.0,0.1,-0.95},{0.0,1.0,0.1}}] Output: False" ProjectToSO3::usage= "ProjectToSO3[mat]: Takes a matrix near SO(3) to project to SO(3). Returns the closest rotation matrix that is in SO(3). This function uses singular-value decomposition (see http://hades.mech.northwestern.edu/index.php/Modern_Robotics_Linear_Algebra_Review) and is only appropriate for matrices close to SO(3). Example: Input: R = ProjectToSO3[{{0.675,0.150,0.720},{0.370,0.771,-0.511}, {-0.630,0.619,0.472}}]//N Output: {{0.679011,0.148945,0.718859},{0.373207,0.773196,-0.512723}, {-0.632187,0.616428,0.469421}}" RpToTrans::usage= "RpToTrans[R,p]: Takes rotation matrix R and position p. Returns corresponding homogeneous transformation matrix T in SE(3). Example: Input: T = RpToTrans[{{1,0,0},{0,0,-1},{0,1,0}},{{1,2,5}}\[Transpose]] Output: {{1, 0, 0, 1}, {0, 0, -1, 2}, {0, 1, 0, 5}, {0, 0, 0, 1}}" TransToRp::usage= "TransToRp[T]: Takes transformation matrix T in SE(3). Returns R: the corresponding rotation matrix, p: the corresponding position vector. The first element of the output is the rotation matrix and the second is the position vector. Example: Input: {R,p} = TransToRp[{{1,0,0,0},{0,0,-1,0},{0,1,0,3},{0,0,0,1}}] Output: {{{1,0,0},{0,0,-1},{0,1,0}},{{0},{0},{3}}}" TransInv::usage= "TransInv[T]: Takes T a transformation matrix. Returns its inverse. Used the structure of transformation matrices to avoid taking a matrix inverse, for efficiency. Example: Input: invT = TransInv[{{1,0,0,0},{0,0,-1,0},{0,1,0,3},{0,0,0,1}}] Output: {{1,0,0,0},{0,0,1,-3}, {0,-1,0,0},{0,0,0,1}}" VecTose3::usage= "VecTose3[V]: Takes a 6-vector (representing a spatial velocity). Returns the corresponding 4x4 se(3) matrix Example: Input: se3mat = VecTose3[{{1,2,3,4,5,6}}\[Transpose]] Output: {{0,-3,2,4},{3,0,-1,5},{-2,1,0,6},{0,0,0,0}}" se3ToVec::usage= "se3ToVec[se3mat]: Takes se3mat a 4x4 se(3) matrix. Returns the corresponding 6-vector (representing spatial velocity). Example: Input: V = se3ToVec[{{0,-3,2,4},{3,0,-1,5},{-2,1,0,6},{0,0,0,0}}] Output: {{1},{2},{3},{4},{5},{6}}" Adjoint::usage= "Adjoint[T]: Takes T a transformation matrix SE(3). Returns the corresponding 6x6 adjoint representation [AdT] Example: Input: AdT = Adjoint[{{1,0,0,0},{0,0,-1,0},{0,1,0,3},{0,0,0,1}}] Output: {{1,0,0,0,0,0},{0,0,-1,0,0,0},{0,1,0,0,0,0},{0,0,3,1,0,0}, {3,0,0,0,0,-1},{0,0,0,0,1,0}}" ScrewToAxis::usage= "ScrewToAxis[q,s,h]: Takes q: a point lying on the screw axis, s: a unit vector in the direction of the screw axis, h: the pitch of the screw axis. Returns the corresponding normalized screw axis. Example: Input: S = ScrewToAxis[{{3,0,0}}\[Transpose],{{0,0,1}}\[Transpose],2] Output: {{0},{0},{1},{0},{-3},{2}}" AxisAng6::usage= "AxisAng6[expc6]: Takes a 6-vector of exponential coordinates for rigid-body motion S*theta. Returns S: the corresponding normalized screw axis, theta: the distance traveled along/about S. The first element of the output is the screw axis S and the second is the distance theta. Example: Input: {S, theta} = AxisAng6[{{1},{0},{0},{1},{2},{3}}] Output: {{{1},{0},{0},{1},{2},{3}},1}" MatrixExp6::usage= "MatrixExp6[se3mat]: Takes a se(3) representation of exponential coordinates. Returns a T matrix SE(3) that is achieved by traveling along/about the screw axis S for a distance theta from an initial configuration T=I. Example: Input: T = MatrixExp6[ {{0,0,0,0},{0,0,-1.5707963267948966,2.3561944901923448}, {0,1.5707963267948966,0,2.3561944901923448},{0,0,0,0}} ] Output: {{1,0,0,0},{0,0,-1,0},{0,1,0,3},{0,0,0,1}}" MatrixLog6::usage= "MatrixLog6[T]: Takes a transformation matrix T in SE(3). Returns the corresponding se(3) representation of exponential coordinates. Example: Input: se3mat = MatrixLog6[{{1,0,0,0},{0,0,-1,0},{0,1,0,3},{0,0,0,1}}]//N Output: {{0,0,0,0},{0,0,-1.5708,2.35619},{0,1.5708,0,2.35619},{0,0,0,0}}" DistanceToSE3::usage= "DistanceToSE3[mat]: Takes mat as a 4x4 matrix. Returns the Frobenius norm to describe the distance of mat from the SE(3) manifold. Computes the determinant of matR, the top 3x3 submatrix of mat. If det (matR) <= 0, return a large number. If det (mat) > 0, replace the top 3x3 submatrix of mat with matR'.matR, and set the first three entries of the fourth column of mat to zero. Then return norm (mat - I). Example : Input: d = DistanceToSE3[{{1.0,0.0,0.0,1.2},{0.0,0.1,-0.95,1.5}, {0.0,1.0,0.1,-0.9},{0.0,0.0,0.1,0.98}}] Output: 0.134931" TestIfSE3::usage= "TestIfSE3[mat]: Takes mat as a 4X4 matrix. Check if mat is close to or on the manifold SE(3). Example: Input: judge = TestIfSE3[{{1.0,0.0,0.0,1.2},{0.0,0.1,-0.95,1.5}, {0.0,1.0,0.1,-0.9},{0.0,0.0,0.1,0.98}}] Output: False" ProjectToSE3::usage= "ProjectToSE3[mat]: Takes a matrix near SE(3) to project to SE(3). Returns the closest rotation matrix that is in SE(3). This function uses singular-value decomposition (see http://hades.mech.northwestern.edu/index.php/Modern_Robotics_Linear_Algebra_Review) and is only appropriate for matrices close to SE(3). Example: Input: T = ProjectToSE3[{{0.675,0.150,0.720,1.2}, {0.370,0.771,-0.511,5.4}, {-0.630,0.619,0.472,3.6}, {0.003,0.002,0.010,0.9}}] Output: {{0.679011,0.148945,0.718859,1.2},{0.373207,0.773196,-0.512723,5.4}, {-0.632187,0.616428,0.469421,3.6},{0,0,0,1}}" (* ::Subsection::Closed:: *) (*CHAPTER 4: FORWARD KINEMATICS*) FKinBody::usage= "FKinBody[M,Blist,thetalist]: Takes M: The home configuration (position and orientation) of the end-effector, Blist: The joint screw axes in the end-effector frame when the manipulator is at the home position, in the format of a matrix with axes as the columns, thetalist: A list of joint coordinates. Returns T in SE(3) representing the end-effector frame when the joints are at the specified coordinates (i.t.o Body Frame). Example: Inputs: M = {{-1,0,0,0},{0,1,0,6},{0,0,-1,2},{0,0,0,1}}; Blist = {{0,0,-1,2,0,0},{0,0,0,0,1,0},{0,0,1,0,0,0.1}}\[Transpose]; thetalist = {{(Pi/2.0),3,Pi}}\[Transpose]; T = FKinBody[M,Blist,thetalist] Output: {{0,1,0,-5},{1,0,0,4},{0,0,-1,1.68584},{0,0,0,1}}" FKinSpace::usage= "FKinSpace[M,Slist,thetalist]: Takes M: the home configuration (position and orientation) of the end-effector, Slist: The joint screw axes in the space frame when the manipulator is at the home position, in the format of a matrix with screw axes as the columns, thetalist: A list of joint coordinates. Returns T in SE(3) representing the end-effector frame when the joints are at the specified coordinates (i.t.o Fixed Space Frame). Example: Inputs: M = {{-1,0,0,0},{0,1,0,6},{0,0,-1,2},{0,0,0,1}}; Slist = {{0,0,1,4,0,0},{0,0,0,0,1,0},{0,0,-1,-6,0,-0.1}}\[Transpose]; thetalist = {{Pi/2,3,Pi}}\[Transpose]; T = FKinSpace[M,Slist,thetalist] Output: {{0,1,0,-5},{1,0,0,4},{0,0,-1,1.68584},{0,0,0,1}}" (* ::Subsection::Closed:: *) (*CHAPTER 5: VELOCITY KINEMATICS AND STATICS*) JacobianBody::usage= "JacobianBody[Blist,thetalist]: Takes Blist: The joint screw axes in the end-effector frame when the manipulator is at the home position, in the format of a matrix with axes as the columns, thetalist:A list of joint coordinates. Returns the corresponding body Jacobian (6xn real numbers). Example: Inputs: Blist = {{0,0,1,0,0.2,0.2},{1,0,0,2,0,3},{0,1,0,0,2,1}, {1,0,0,0.2,0.3,0.4}}\[Transpose]; thetalist = {{0.2,1.1,0.1,1.2}}\[Transpose]; Jb = JacobianBody[Blist,thetalist] Output: {{-0.0452841,0.995004,0,1},{0.743593,0.0930486,0.362358,0}, {-0.667097,0.0361754,-0.932039,0},{2.32586,1.66809,0.564108,0.2}, {-1.44321,2.94561,1.43307,0.3},{-2.0664,1.82882,-1.58869,0.4}}" JacobianSpace::usage= "JacobianSpace[Slist,thetalist]: Takes Slist: The joint screw axes in the space frame when the manipulator is at the home position, in the format of a matrix with axes as the columns, thetalist: A list of joint coordinates. Returns the corresponding space Jacobian (6xn real numbers). Example: Inputs: Slist = {{0,0,1,0,0.2,0.2},{1,0,0,2,0,3},{0,1,0,0,2,1}, {1,0,0,0.2,0.3,0.4}}\[Transpose]; thetalist = {{0.2,1.1,0.1,1.2}}\[Transpose]; Js = JacobianSpace[Slist, thetalist] Ouput: {{0,0.980067,-0.0901156,0.957494},{0,0.198669,0.444554,0.284876}, {1,0,0.891207,-0.0452841},{0,1.95219,-2.21635,-0.511615}, {0.2,0.436541,-2.43713,2.77536},{0.2,2.96027,3.23573,2.22512}}" (* ::Subsection::Closed:: *) (*CHAPTER 6: INVERSE KINEMATICS*) IKinBody::usage= "IKinBody[Blist,M,T,thetalist0,eomg,ev]: Takes Blist: The joint screw axes in the end-effector frame when the manipulator is at the home position, in the format of a matrix with axes as the columns, M: The home configuration of the end-effector, T: The desired end-effector configuration T, thetalist0: An initial guess of joint angles that are close to satisfying T, eomg: A small positive tolerance on the end-effector orientation error. The returned joint angles must give an end-effector orientation error less than eomg, ev: A small positive tolerance on the end-effector linear position error. The returned joint angles must give an end-effector position error less than ev. Returns thetalist: Joint angles that achieve T within the specified tolerance, success: A logical value where True means that the function found a solution and False means that it ran through the set number of maximum iterations without finding a solution within the tolerances eomg and ev. Uses an iterative Newton-Raphson root-finding method. The maximum number of iterations before the algorithm is terminated has been hardcoded in as a variable called maxiterations.It is set to 20 at the start of the function,but can be changed if needed. Example: Inputs: Blist = {{0,0,-1,2,0,0},{0,0,0,0,1,0},{0,0,1,0,0,0.1}}\[Transpose]; M = {{-1,0,0,0},{0,1,0,6},{0,0,-1,2},{0,0,0,1}}; T = {{0,1,0,-5},{1,0,0,4},{0,0,-1,1.6858},{0,0,0,1}}; thetalist0 = {{1.5,2.5,3}}\[Transpose]; eomg = 0.01; ev = 0.001; {thetalist,success} = IKinBody[Blist,M,T,thetalist0,eomg,ev] Ouput: {{{1.57074},{2.99967},{3.14154}},True}" IKinSpace::usage= "IKinSpace[Slist,M,T,thetalist0,eomg,ev]: Takes Slist: The joint screw axes in the space frame when the manipulator is at the home position, in the format of a matrix with axes as the columns, M: The home configuration of the end-effector, T: The desired end-effector configuration T, thetalist0: An initial guess of joint angles that are close to satisfying T, eomg: A small positive tolerance on the end-effector orientation error. The returned joint angles must give an end-effector orientation error less than eomg, ev: A small positive tolerance on the end-effector linear position error. The returned joint angles must give an end-effector position error less than ev. Returns thetalist: Joint angles that achieve T within the specified tolerances, success: A logical value where True means that the function found a solution and False means that it ran through the set number of maximum iterations without finding a solution within the tolerances eomg and ev. Uses an iterative Newton-Raphson root-finding method. The maximum number of iterations before the algorithm is terminated has been hardcoded in as a variable called maxiterations.It is set to 20 at the start of the function,but can be changed if needed. Exapmle: Inputs: Slist = {{0,0,1,4,0,0},{0,0,0,0,1,0},{0,0,-1,-6,0,-0.1}}\[Transpose]; M = {{-1,0,0,0},{0,1,0,6},{0,0,-1,2},{0,0,0,1}}; T = {{0,1,0,-5},{1,0,0,4},{0,0,-1,1.6858},{0,0,0,1}}; thetalist0 = {{1.5,2.5,3}}\[Transpose]; eomg = 0.01; ev = 0.001; {thetalist,success} = IKinSpace[Slist,M,T,thetalist0,eomg,ev] Ouput: {{{1.57074},{2.99966},{3.14153}},True}" (* ::Subsection::Closed:: *) (*CHAPTER 8: DYNAMICS OF OPEN CHAINS*) ad::usage= "ad[V]: Takes a 6-vector spatial velocity. Returns the corresponding 6x6 matrix [adV]. Used to calculate the Lie bracket [V1, V2] = [adV1] V2 Example: Input: mat = ad[{{1,2,3,4,5,6}}\[Transpose]] Output: {{0,-3,2,0,0,0},{3,0,-1,0,0,0},{-2,1,0,0,0,0},{0,-6,5,0,-3,2}, {6,0,-4,3,0,-1},{-5,4,0,-2,1,0}}" InverseDynamics::usage= "InverseDynamics[thetalist,dthetalist,ddthetalist,g,Ftip,Mlist,Glist,Slist]: Takes thetalist: n-vector of joint variables, dthetalist: n-vector of joint rates, ddthetalist: n-vector of joint accelerations, g: Gravity vector g, Ftip: Spatial force applied by the end-effector expressed in frame {n+1}, Mlist:L ist of link frames {i} relative to {i-1} at the home position, Glist: Spatial inertia matrices Gi of the links, Slist: Screw axes Si of the joints in a space frame, in the format of a matrix with axes as the columns. Returns taulist:The n-vector of required joint forces/torques. This function uses forward-backward Newton-Euler iterations to solve the equation: taulist=Mlist(thetalist)ddthetalist+c(thetalist,dthetalist)+g(thetalist) +Jtr(thetalist)Ftip Example (3 Link Robot): Inputs: thetalist = {{0.1,0.1,0.1}}\[Transpose]; dthetalist = {{0.1,0.2,0.3}}\[Transpose]; ddthetalist = {{2,1.5,1}}\[Transpose]; g = {{0,0,-9.8}}\[Transpose]; Ftip = {{1,1,1,1,1,1}}\[Transpose]; M01 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.089159},{0,0,0,1}}; M12 = {{0,0,1,0.28},{0,1,0,0.13585},{-1,0,0,0},{0,0,0,1}}; M23 = {{1,0,0,0},{0,1,0,-0.1197},{0,0,1,0.395},{0,0,0,1}}; M34 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.14225},{0,0,0,1}}; G1 = DiagonalMatrix[{0.010267,0.010267,0.00666,3.7,3.7,3.7}]; G2 = DiagonalMatrix[{0.22689,0.22689,0.0151074,8.393,8.393,8.393}]; G3 = DiagonalMatrix[{0.0494433,0.0494433,0.004095, 2.275,2.275,2.275}]; Glist = {G1,G2,G3}; Mlist = {M01,M12,M23,M34}; Slist = {{1,0,1,0,1,0},{0,1,0,-0.089,0,0}, {0,1,0,-0.089,0,0.425}}\[Transpose]; taulist = InverseDynamics[thetalist,dthetalist,ddthetalist,g,Ftip, Mlist,Glist,Slist] Output: {{74.6962},{-33.0677},{-3.23057}}" MassMatrix::usage= "MassMatrix[thetalist,Mlist,Glist,Slist]: Takes thetalist: A list of joint variables,Mlist:List of link frames i relative to i-1 at the home position,Glist:Spatial inertia matrices Gi of the links, Slist: Screw axes Si of the joints in a space frame, in the format of a matrix with axes as the columns. Returns M: The numerical inertia matrix M(thetalist) of an n-joint serial chain at the given configuration thetalist. This function calls InverseDynamics n times, each time passing a ddthetalist vector with a single element equal to one and all other inputs set to zero. Each call of InverseDynamics generates a single column,and these columns are assembled to create the inertia matrix. Example (3 Link Robot): Inputs: thetalist = {{0.1,0.1,0.1}}\[Transpose]; M01 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.089159},{0,0,0,1}}; M12 = {{0,0,1,0.28},{0,1,0,0.13585},{-1,0,0,0},{0,0,0,1}}; M23 = {{1,0,0,0},{0,1,0,-0.1197},{0,0,1,0.395},{0,0,0,1}}; M34 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.14225},{0,0,0,1}}; G1 = DiagonalMatrix[{0.010267,0.010267,0.00666,3.7,3.7,3.7}]; G2 = DiagonalMatrix[{0.22689,0.22689,0.0151074,8.393,8.393,8.393}]; G3 = DiagonalMatrix[{0.0494433,0.0494433,0.004095, 2.275,2.275,2.275}]; Glist = {G1,G2,G3}; Mlist = {M01,M12,M23,M34}; Slist = {{1,0,1,0,1,0},{0,1,0,-0.089,0,0}, {0,1,0,-0.089,0,0.425}}\[Transpose]; M = MassMatrix[thetalist,Mlist,Glist,Slist] Output: {{22.5433,-0.307147,-0.00718426},{-0.307147,1.96851,0.432157}, {-0.00718426,0.432157,0.191631}}" VelQuadraticForces::usage= "VelQuadraticForces[thetalist,dthetalist,Mlist,Glist,Slist]: Takes thetalist: A list of joint variables, dthetalist: A list of joint rates, Mlist: List of link frames i relative to i-1 at the home position, Glist: Spatial inertia matrices Gi of the links, Slist: Screw axes Si of the joints in a space frame, in the format of a matrix with axes as the columns. Returns c: The vector c(thetalist,dthetalist) of Coriolis and centripetal terms for a given thetalist and dthetalist. This function calls InverseDynamics with g=0,Ftip=0,and ddthetalist=0. Example(3 Link Robot): Inputs: thetalist = {{0.1,0.1,0.1}}\[Transpose]; dthetalist = {{0.1,0.2,0.3}}\[Transpose]; M01 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.089159},{0,0,0,1}}; M12 = {{0,0,1,0.28},{0,1,0,0.13585},{-1,0,0,0},{0,0,0,1}}; M23 = {{1,0,0,0},{0,1,0,-0.1197},{0,0,1,0.395},{0,0,0,1}}; M34 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.14225},{0,0,0,1}}; G1 = DiagonalMatrix[{0.010267,0.010267,0.00666,3.7,3.7,3.7}]; G2 = DiagonalMatrix[{0.22689,0.22689,0.0151074,8.393,8.393,8.393}]; G3 = DiagonalMatrix[{0.0494433,0.0494433,0.004095, 2.275,2.275,2.275}]; Glist = {G1,G2,G3}; Mlist = {M01,M12,M23,M34}; Slist = {{1,0,1,0,1,0},{0,1,0,-0.089,0,0}, {0,1,0,-0.089,0,0.425}}\[Transpose]; c = VelQuadraticForces[thetalist,dthetalist,Mlist,Glist,Slist] Output: {{0.264531},{-0.0550516},{-0.00689132}}" GravityForces::usage= "GravityForces[thetalist,g,Mlist,Glist,Slist]: Takes thetalist: A list of joint variables, g: 3-vector for gravitational acceleration, Mlist: List of link frames i relative to i-1 at the home position, Glist: Spatial inertia matrices Gi of the links, Slist: Screw axes Si of the joints in a space frame, in the format of a matrix with axes as the columns. Returns grav: The joint forces/torques required to overcome gravity at thetalist. This function calls InverseDynamics with Ftip=0,dthetalist=0, and ddthetalist=0. Example (3 Link Robot): Inputs: thetalist = {{0.1,0.1,0.1}}\[Transpose]; g = {{0,0,-9.8}}\[Transpose]; M01 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.089159},{0,0,0,1}}; M12 = {{0,0,1,0.28},{0,1,0,0.13585},{-1,0,0,0},{0,0,0,1}}; M23 = {{1,0,0,0},{0,1,0,-0.1197},{0,0,1,0.395},{0,0,0,1}}; M34 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.14225},{0,0,0,1}}; G1 = DiagonalMatrix[{0.010267,0.010267,0.00666,3.7,3.7,3.7}]; G2 = DiagonalMatrix[{0.22689,0.22689,0.0151074,8.393,8.393,8.393}]; G3 = DiagonalMatrix[{0.0494433,0.0494433,0.004095, 2.275,2.275,2.275}]; Glist = {G1,G2,G3}; Mlist = {M01,M12,M23,M34}; Slist = {{1,0,1,0,1,0},{0,1,0,-0.089,0,0}, {0,1,0,-0.089,0,0.425}}\[Transpose]; grav = GravityForces[thetalist,g,Mlist,Glist,Slist] Output: {{28.4033},{-37.6409},{-5.44159}}" EndEffectorForces::usage= "EndEffectorForces[thetalist,Ftip,Mlist,Glist,Slist]: Takes thetalist: A list of joint variables, Ftip: Spatial force applied by the end-effector expressed in frame {n+1}, Mlist: List of link frames i relative to i-1 at the home position, Glist: Spatial inertia matrices Gi of the links, Slist: Screw axes Si of the joints in a space frame, in the format of a matrix with axes as the columns. Returns JTFtip: The joint forces and torques required only to create the end- effector force Ftip. This function calls InverseDynamics with g=0,dthetalist=0,and ddthetalist=0. Example (3 Link Robot): Inputs: thetalist = {{0.1,0.1,0.1}}\[Transpose]; Ftip = {{1,1,1,1,1,1}}\[Transpose]; M01 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.089159},{0,0,0,1}}; M12 = {{0,0,1,0.28},{0,1,0,0.13585},{-1,0,0,0},{0,0,0,1}}; M23 = {{1,0,0,0},{0,1,0,-0.1197},{0,0,1,0.395},{0,0,0,1}}; M34 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.14225},{0,0,0,1}}; G1 = DiagonalMatrix[{0.010267,0.010267,0.00666,3.7,3.7,3.7}]; G2 = DiagonalMatrix[{0.22689,0.22689,0.0151074,8.393,8.393,8.393}]; G3 = DiagonalMatrix[{0.0494433,0.0494433,0.004095, 2.275,2.275,2.275}]; Glist = {G1,G2,G3}; Mlist = {M01,M12,M23,M34}; Slist = {{1,0,1,0,1,0},{0,1,0,-0.089,0,0}, {0,1,0,-0.089,0,0.425}}\[Transpose]; JTFtip = EndEffectorForces[thetalist,Ftip,Mlist,Glist,Slist] Output: {{1.40955}, {1.85771}, {1.39241}}" ForwardDynamics::usage= "ForwardDynamics[thetalist,dthetalist,taulist,g,Ftip,Mlist,Glist,Slist]: Takes thetalist: A list of joint variables, dthetalist: A list of joint rates, taulist: An n-vector of joint forces/torques, g: Gravity vector g, Ftip: Spatial force applied by the end-effector expressed in frame {n+1}, Mlist: List of link frames i relative to i-1 at the home position, Glist: Spatial inertia matrices Gi of the links, Slist: Screw axes Si of the joints in a space frame, in the format of a matrix with axes as the columns. Returns ddthetalist: The resulting joint accelerations. This function computes ddthetalist by solving: Mlist(thetalist).ddthetalist =taulist-c(thetalist,dthetalist)-g(thetalist)-Jtr(thetalist)Ftip Example (3 Link Robot): Inputs: thetalist = {{0.1,0.1,0.1}}\[Transpose]; dthetalist = {{0.1,0.2,0.3}}\[Transpose]; taulist = {{0.5,0.6,0.7}}\[Transpose]; g = {{0,0,-9.8}}\[Transpose]; Ftip = {{1,1,1,1,1,1}}\[Transpose]; M01 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.089159},{0,0,0,1}}; M12 = {{0,0,1,0.28},{0,1,0,0.13585},{-1,0,0,0},{0,0,0,1}}; M23 = {{1,0,0,0},{0,1,0,-0.1197},{0,0,1,0.395},{0,0,0,1}}; M34 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.14225},{0,0,0,1}}; G1 = DiagonalMatrix[{0.010267,0.010267,0.00666,3.7,3.7,3.7}]; G2 = DiagonalMatrix[{0.22689,0.22689,0.0151074,8.393,8.393,8.393}]; G3 = DiagonalMatrix[{0.0494433,0.0494433,0.004095, 2.275,2.275,2.275}]; Glist = {G1,G2,G3}; Mlist = {M01,M12,M23,M34}; Slist = {{1,0,1,0,1,0},{0,1,0,-0.089,0,0}, {0,1,0,-0.089,0,0.425}}\[Transpose]; ddthetalist = ForwardDynamics[thetalist,dthetalist,taulist,g,Ftip, Mlist,Glist,Slist] Output: {{-0.973929}, {25.5847}, {-32.915}}" EulerStep::usage= "EulerStep[thetalist,dthetalist,ddthetalist,dt]: Takes thetalist: n-vector of joint variables, dthetalist: n-vector of joint rates, ddthetalist: n-vector of joint accelerations, dt: The timestep delta t. Returns thetalistNext: Vector of joint variables after dt from first order Euler integration, dthetalistNext: Vector of joint rates after dt from first order Euler integration. Example (3 Link Robot): Inputs\:ff1a thetalist = {{0.1,0.1,0.1}}\[Transpose]; dthetalist = {{0.1,0.2,0.3}}\[Transpose]; ddthetalist = {{2,1.5,1}}\[Transpose]; dt = 0.1; {thetalistNext,dthetalistNext} = EulerStep[thetalist,dthetalist, ddthetalist,dt] Output: {{{0.11},{0.12},{0.13}},{{0.3},{0.35},{0.4}}}" InverseDynamicsTrajectory::usage= "InverseDynamicsTrajectory[thetamat,dthetamat,ddthetamat,g,Ftipmat,Mlist, Glist,Slist]: Takes thetamat: An N x n matrix of robot joint variables, dthetamat: An N x n matrix of robot joint velocities, ddthetamat: An N x n matrix of robot joint accelerations, g: Gravity vector g, Ftipmat: An N x 6 matrix of spatial forces applied by the end-effector (If there are no tip forces the user should input a zero and a zero matrix will be used), Mlist: List of link frames i relative to i-1 at the home position, Glist: Spatial inertia matrices Gi of the links, Slist: Screw axes Si of the joints in a space frame, in the format of a matrix with axes as the columns. Returns taumat: The N x n matrix of joint forces/torques for the specified trajectory,where each of the N rows is the vector of joint forces/torques at each time step. This function uses InverseDynamics to calculate the joint forces/torques required to move the serial chain along the given trajectory. Example (3 Link Robot): Inputs: (*Create a trajectory to follow using functions from Chapter*) thetastart = {{0,0,0}}\[Transpose]; thetaend = {{Pi/2,Pi/2,Pi/2}}\[Transpose]; Tf = 3; No = 1000; method = 5; traj = JointTrajectory[thetastart,thetaend,Tf,No,method]; thetamat = traj; dthetamat = traj; ddthetamat = traj; dt = Tf/(No-1.0); Do[ dthetamat[[i,;;]] = (thetamat[[i,;;]]-thetamat[[i-1,;;]])/dt; ddthetamat[[i,;;]] = (dthetamat[[i,;;]]-dthetamat[[i-1,;;]])/dt, {i,2,Dimensions[traj][[1]]} ]; (*Initialise robot descripstion*) g = {{0,0,-9.8}}\[Transpose]; Ftipmat = ConstantArray[1,{No,6}]; M01 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.089159},{0,0,0,1}}; M12 = {{0,0,1,0.28},{0,1,0,0.13585},{-1,0,0,0},{0,0,0,1}}; M23 = {{1,0,0,0},{0,1,0,-0.1197},{0,0,1,0.395},{0,0,0,1}}; M34 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.14225},{0,0,0,1}}; G1 = DiagonalMatrix[{0.010267,0.010267,0.00666,3.7,3.7,3.7}]; G2 = DiagonalMatrix[{0.22689,0.22689,0.0151074,8.393,8.393,8.393}]; G3 = DiagonalMatrix[{0.0494433,0.0494433,0.004095, 2.275,2.275,2.275}]; Glist = {G1,G2,G3}; Mlist = {M01,M12,M23,M34}; Slist = {{1,0,1,0,1,0},{0,1,0,-0.089,0,0}, {0,1,0,-0.089,0,0.425}}\[Transpose]; taumat = InverseDynamicsTrajectory[thetamat,dthetamat,ddthetamat,g, Ftipmat,Mlist,Glist,Slist]; (*Output to plot the joint forces/torques*) Tau1 = Flatten[taumat[[;;,1]]]; Tau2 = Flatten[taumat[[;;,2]]]; Tau3 =Flatten[ taumat[[;;,3]]]; (*PLEASE REPLACE THE SINGLE QUOTES TO DOUBLE TUOTES BEFORE RUNNING THE COMMAND BELOW*) ListLinePlot[ {Tau1,Tau2,Tau3}, PlotLegends\[Rule]{'Tau1','Tau2','Tau3'}, AxesLabel\[Rule]{'Time','Torque'}, PlotLabel\[Rule]'Plot of Torque Trajectories', PlotRange\[Rule]Full ]" ForwardDynamicsTrajectory::usage= "ForwardDynamicsTrajectory[thetalist,dthetalist,taumat,g,Ftipmat,Mlist,Glist, Slist,dt,intRes]: Takes thetalist: n-vector of initial joint variables, dthetalist: n-vector of initial joint rates, taumat: An N x n matrix of joint forces/torques,where each row is the joint effort at any time step, g:Gravity vector g, Ftipmat: An N x 6 matrix of spatial forces applied by the end-effector (If there are no tip forces the user should input a zero and a zero matrix will be used), Mlist: List of link frames {i} relative to {i-1} at the home position, Glist: Spatial inertia matrices Gi of the links, Slist: Screw axes Si of the joints in a space frame, in the format of a matrix with axes as the columns, dt: The timestep between consecutive joint forces/torques, intRes: Integration resolution is the number of times integration (Euler) takes places between each time step. Must be an integer value greater than or equal to 1. Returns thetamat: The N x n matrix of robot joint angles resulting from the specified joint forces/torques,dthetamat:The N x n matrix of robot joint velocities. This function simulates the motion of a serial chain given an open-loop history of joint forces/torques. It calls a numerical integration procedure that uses ForwardDynamics. Example: Inputs: thetalist = {{0.1,0.1,0.1}}\[Transpose]; dthetalist = {{0.1,0.2,0.3}}\[Transpose]; taumat = {{3.63,-6.58,-5.57},{3.74,-5.55,-5.5},{4.31,-0.68,-5.19}, {5.18,5.63,-4.31},{5.85,8.17,-2.59},{5.78,2.79,-1.7}, {4.99,-5.3,-1.19},{4.08,-9.41,0.07},{3.56,-10.1,0.97}, {3.49,-9.41,1.23}}; (*Initialise robot descripstion (Example with 3 links)*) g = {{0,0,-9.8}}\[Transpose]; Ftipmat = ConstantArray[1,{Dimensions[taumat][[1]],6}]; M01 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.089159},{0,0,0,1}}; M12 = {{0,0,1,0.28},{0,1,0,0.13585},{-1,0,0,0},{0,0,0,1}}; M23 = {{1,0,0,0},{0,1,0,-0.1197},{0,0,1,0.395},{0,0,0,1}}; M34 = {{1,0,0,0},{0,1,0,0},{0,0,1,0.14225},{0,0,0,1}}; G1 = DiagonalMatrix[{0.010267,0.010267,0.00666,3.7,3.7,3.7}]; G2 = DiagonalMatrix[{0.22689,0.22689,0.0151074,8.393,8.393,8.393}]; G3 = DiagonalMatrix[{0.0494433,0.0494433,0.004095, 2.275,2.275,2.275}]; Glist = {G1,G2,G3}; Mlist = {M01,M12,M23,M34}; Slist = {{1,0,1,0,1,0},{0,1,0,-0.089,0,0}, {0,1,0,-0.089,0,0.425}}\[Transpose]; dt = 0.1; intRes = 8; {thetamat,dthetamat} = ForwardDynamicsTrajectory[ thetalist,dthetalist,taumat,g,Ftipmat,Mlist,Glist,Slist,dt,intRes ]; (*Output to plot the joint forces/torques*) theta1 = Flatten[thetamat[[;;,1]]]; theta2 = Flatten[thetamat[[;;,2]]]; theta3 = Flatten[thetamat[[;;,3]]]; dtheta1 = Flatten[ dthetamat[[;;,1]]]; dtheta2 = Flatten[dthetamat[[;;,2]]]; dtheta3 = Flatten[ dthetamat[[;;,3]]]; (*PLEASE REPLACE THE SINGLE QUOTES TO DOUBLE TUOTES BEFORE RUNNING THE COMMAND BELOW*) ListLinePlot[ {theta1,theta2,theta3,dtheta1,dtheta2,dtheta3}, PlotLegends->{'theta1','theta2','theta3', 'dtheta1','dtheta2','dtheta3'}, AxesLabel->{'Time','Joint Angles/Velocities'}, PlotLabel->'Plot of Joint Angles and Joint Velocities', PlotRange->Full ]" (* ::Subsection::Closed:: *) (*CHAPTER 9: TRAJECTORY GENERATION*) CubicTimeScaling::usage= "CubicTimeScaling[Tf,t]: Takes Tf: Total time of the motion in seconds from rest to rest, t: The current time t satisfying 0=1, Return[ConstantArray[0,{3,3}]], acosinput<=-1, Which[ Not[NearZero[R[[3,3]]+1]], Return[VecToso3[ 1/Sqrt[2(1+R[[3,3]])]* {{R[[1,3]],R[[2,3]],1+R[[3,3]]}}\[Transpose]*Pi ]], Not[NearZero[R[[2,2]]+1]], Return[VecToso3[ 1/Sqrt[2(1+R[[2,2]])]* {{R[[1,2]],1+R[[2,2]],R[[3,2]]}}\[Transpose]*Pi ]], True, Return[VecToso3[ 1/Sqrt[2(1+R[[1,1]])]* {{1+R[[1,1]],R[[2,1]],R[[3,1]]}}\[Transpose]*Pi ]] ], True, theta=ArcCos[acosinput]; omgmat=(R-R\[Transpose])/(2*Sin[theta]); Return[theta*omgmat] ] ] DistanceToSO3[mat_]:=If[ Det[mat]>0, Return[Norm[mat\[Transpose].mat-IdentityMatrix[3],"Frobenius"]], Return[10^9] ] TestIfSO3[mat_]:=Abs[DistanceToSO3[mat]]<10^-3 ProjectToSO3[mat_]:=Module[ {R,u,w,v}, {u,w,v}=SingularValueDecomposition[mat]; R=u.v\[Transpose]; (*In the case below, the result may be far from mat.*) If[ Det[R] < 0, Return[ArrayFlatten[{{R[[;;,1;;2]],{-R[[;;,3]]}\[Transpose]}}]], Return[R] ] ] RpToTrans[R_,p_]:=ArrayFlatten[{{R,p},{0,1}}] TransToRp[T_]:={T[[1;;3,1;;3]],{T[[1;;3,4]]}\[Transpose]} TransInv[T_]:=ArrayFlatten[ {{T[[1;;3,1;;3]]\[Transpose], -T[[1;;3,1;;3]]\[Transpose].{T[[1;;3,4]]}\[Transpose]}, {0,1}} ] VecTose3[V_]:=ArrayFlatten[ {{VecToso3[{V[[1;;3,1]]}\[Transpose]],{V[[4;;6,1]]}\[Transpose]},{0,0}} ] se3ToVec[se3mat_]:={{se3mat[[3,2]],se3mat[[1,3]],se3mat[[2,1]], se3mat[[1,4]],se3mat[[2,4]],se3mat[[3,4]]}}\[Transpose] Adjoint[T_]:=Module[ {R,p}, {R,p}=TransToRp[T]; Return[ArrayFlatten[{{R,0},{VecToso3[p].R,R}}]] ] ScrewToAxis[q_,s_,h_]:=ArrayFlatten[{{s},{{Cross[Flatten[q], Flatten[s]]}\[Transpose]+h*s}}] AxisAng6[expc6_]:=Module[ {theta}, theta=Norm[{expc6[[1;;3,1]]}\[Transpose]]; If[NearZero[theta],theta=Norm[{expc6[[4;;6,1]]}\[Transpose]]]; Return[{expc6/theta,theta}] ] MatrixExp6[se3mat_]:=Module[ {omgtheta,omghat,theta,omgmat,G}, omgtheta=so3ToVec[se3mat[[1;;3,1;;3]]]; If[ NearZero[Norm[omgtheta]], Return[ArrayFlatten[ {{IdentityMatrix[3],{se3mat[[1;;3,4]]}\[Transpose]},{0,1}} ]], {omghat,theta} = AxisAng3[omgtheta]; omgmat=se3mat[[1;;3,1;;3]]/theta; Return[ArrayFlatten[ {{MatrixExp3[se3mat[[1;;3,1;;3]]], (IdentityMatrix[3]*theta+(1-Cos[theta])*omgmat+ (theta-Sin[theta])*omgmat.omgmat). ({se3mat[[1;;3,4]]}\[Transpose]/theta)}, {0,1}} ]] ] ] MatrixLog6[T_]:=Module[ {R,p,acosinput,theta,omgmat}, {R,p}=TransToRp[T]; omgmat=MatrixLog3[R]; If[ omgmat==ConstantArray[0,{3,3}], ArrayFlatten[ {{ConstantArray[0,{3,3}],{T[[1;;3,4]]}\[Transpose]},{0,0}} ], theta=ArcCos[(Tr[R]-1)/2]; Return[ArrayFlatten[{ {omgmat,(IdentityMatrix[3]-omgmat/2+ (1/theta-Cot[theta/2]/2)*omgmat.omgmat/theta).p}, {0,0} }]] ] ] DistanceToSE3[mat_]:=Module[ {R,p}, {R,p}=TransToRp[mat]; If[ Det[R] > 0, Return[Norm[ Join[Join[R\[Transpose].R,{{0,0,0}}\[Transpose],2],{mat[[4,;;]]}]- IdentityMatrix[4], "Frobenius" ]], Return[10^9] ] ] TestIfSE3[mat_]:=Abs[DistanceToSE3[mat]]<10^-3 ProjectToSE3[mat_]:=RpToTrans[ProjectToSO3[mat[[1;;3,1;;3]]], {mat[[1;;3,4]]}\[Transpose]] (* ::Subsection::Closed:: *) (*CHAPTER 4: FORWARD KINEMATICS*) FKinBody[M_,Blist_,thetalist_]:=Module[ {n,T,i}, T=M; Do[ T=T.MatrixExp6[ VecTose3[{Blist[[;;,i]]}\[Transpose]*thetalist[[i,1]]] ], {i,Length[thetalist]} ]; Return[T] ] FKinSpace[M_,Slist_,thetalist_]:=Module[ {n,T}, T=M; Do[ T=MatrixExp6[ VecTose3[{Slist[[;;,i]]}\[Transpose]*thetalist[[i,1]]] ].T, {i,Length[thetalist],1,-1} ]; Return[T] ] (* ::Subsection::Closed:: *) (*CHAPTER 5: VELOCITY KINEMATICS AND STATICS*) JacobianBody[Blist_,thetalist_]:=Module[ {Jb=Blist,T=IdentityMatrix[4]}, Do[ T=T.MatrixExp6[VecTose3[{Blist[[;;,i+1]]}\[Transpose]* -thetalist[[i+1,1]]]]; Jb[[;;,i]]=Flatten[Adjoint[T].{Blist[[;;,i]]}\[Transpose]], {i,Length[thetalist]-1,1,-1} ]; Return[Jb] ] JacobianSpace[Slist_,thetalist_]:=Module[ {Js=Slist,T=IdentityMatrix[4]}, Do[ T=T.MatrixExp6[VecTose3[{Slist[[;;,i-1]]}\[Transpose]* thetalist[[i-1,1]]]]; Js[[;;,i]]=Flatten[Adjoint[T].{Slist[[;;,i]]}\[Transpose]], {i,2,Length[thetalist]} ]; Return[Js] ] (* ::Subsection::Closed:: *) (*CHAPTER 6: INVERSE KINEMATICS*) IKinBody[Blist_,M_,T_,thetalist0_,eomg_,ev_]:=Module[ {Vb,thetalist=thetalist0,err,i=0,maxiterations=20}, Vb = se3ToVec[MatrixLog6[TransInv[FKinBody[M,Blist,thetalist]].T]]; err=Norm[Vb[[1;;3,1]]]>eomg || Norm[Vb[[4;;6,1]]]>ev; While[ err&&ieomg||Norm[Vb[[4;;6,1]]]>ev ]; Return[{thetalist, !err}] ] IKinSpace[Slist_,M_,T_,thetalist0_,eomg_,ev_]:=Module[ {Tsb,Vs,thetalist=thetalist0,err,i=0,maxiterations=20}, Tsb=FKinSpace[M,Slist,thetalist]; Vs = Adjoint[Tsb].se3ToVec[MatrixLog6[TransInv[Tsb].T]]; err=Norm[Vs[[1;;3,1]]]>eomg||Norm[Vs[[4;;6,1]]]>ev; While[ err&&ieomg || Norm[Vs[[4;;6,1]]]>ev ]; Return[{thetalist, !err}] ] (* ::Subsection::Closed:: *) (*CHAPTER 8: DYNAMICS OF OPEN CHAINS*) ad[V_]:=Module[ {w}, w=VecToso3[V[[1;;3]]]; Return[ArrayFlatten[{{w,0},{VecToso3[V[[4;;6]]],w}}]] ] InverseDynamics[thetalist_,dthetalist_,ddthetalist_,g_,Ftip_,Mlist_,Glist_,Slist_]:=Module[ {n ,Ai,AdTi,Ti,Vi,Vdi,Fi=Ftip,taulist,Mi=IdentityMatrix[4]}, n=Length[thetalist]; Ai=ConstantArray[0,{6,n}]; AdTi=ConstantArray[Null,{1,n+1}]; Vi=ConstantArray[0,{6,n+1}]; Vdi=ConstantArray[0,{6,n+1}]; Vdi[[4;;6,1]]=Flatten[-g]; AdTi[[1,n+1]]=Adjoint[TransInv[Mlist[[n+1]]]]; taulist=ConstantArray[0,{n,1}]; Do[ Mi=Mi.Mlist[[i]]; Ai[[;;,i]]=Adjoint[TransInv[Mi]].Slist[[;;,i]]; AdTi[[1,i]]=Adjoint[MatrixExp6[ VecTose3[{Ai[[;;,i]]}\[Transpose]*-thetalist[[i,1]]] ].TransInv[Mlist[[i]]]]; Vi[[;;,i+1]]=AdTi[[1,i]].Vi[[;;,i]]+ {Ai[[;;,i]]}\[Transpose]*dthetalist[[i,1]]; Vdi[[;;,i+1]]=AdTi[[1,i]].Vdi[[;;,i]]+ {Ai[[;;,i]]}\[Transpose]*ddthetalist[[i,1]]+ ad[Vi[[;;,i+1]]].Ai[[;;,i]]*dthetalist[[i,1]], {i,1,n} ]; Do[ Fi=AdTi[[1,i+1]]\[Transpose].Fi+Glist[[i]].Vdi[[;;,i+1]]- ad[Vi[[;;,i+1]]]\[Transpose].(Glist[[i]].Vi[[;;,i+1]]); taulist[[i,1]]=(Fi\[Transpose].Ai[[;;,i]])[[1]], {i,n,1,-1} ]; Return[taulist] ] MassMatrix[thetalist_,Mlist_,Glist_,Slist_]:=Module[ {n,dthetalist,ddthetalist,g,Ftip,taulist,M}, n=Length[thetalist]; M=ConstantArray[0,{n,n}]; Do[ ddthetalist=ConstantArray[0,{n,1}]; ddthetalist[[i,1]]=1; M[[;;,i]]=Flatten[InverseDynamics[thetalist,ConstantArray[0,{6,n}], ddthetalist,{{0,0,0}}\[Transpose], {{0,0,0,0,0,0}}\[Transpose],Mlist, Glist,Slist]], {i,n} ]; Return[M] ] VelQuadraticForces[thetalist_,dthetalist_,Mlist_,Glist_,Slist_]:= InverseDynamics[thetalist,dthetalist, ConstantArray[0,{Length[thetalist],1}], {{0,0,0}}\[Transpose],{{0,0,0,0,0,0}}\[Transpose],Mlist, Glist,Slist] GravityForces[thetalist_,g_,Mlist_,Glist_,Slist_]:=Module[ {n=Length[thetalist]}, Return[InverseDynamics[ thetalist,ConstantArray[0,{n,1}],ConstantArray[0,{n,1}],g, {{0,0,0,0,0,0}}\[Transpose],Mlist,Glist,Slist ]] ] EndEffectorForces[thetalist_,Ftip_,Mlist_,Glist_,Slist_]:=Module[ {n=Length[thetalist]}, Return[InverseDynamics[ thetalist,ConstantArray[0,{n,1}],ConstantArray[0,{n,1}], {{0,0,0}}\[Transpose],Ftip,Mlist,Glist,Slist ]] ] ForwardDynamics[thetalist_,dthetalist_,taulist_,g_,Ftip_,Mlist_,Glist_,Slist_]:= Inverse[MassMatrix[thetalist,Mlist,Glist,Slist]]. (taulist-VelQuadraticForces[thetalist,dthetalist,Mlist,Glist,Slist]- GravityForces[thetalist,g,Mlist,Glist,Slist]- EndEffectorForces[thetalist,Ftip,Mlist,Glist,Slist]) EulerStep[thetalist_,dthetalist_,ddthetalist_,dt_]:= {thetalist+dt*dthetalist,dthetalist+dt*ddthetalist} InverseDynamicsTrajectory[thetamat_,dthetamat_,ddthetamat_,g_,Ftipmat_,Mlist_,Glist_,Slist_]:=Module[ {thetamatt=thetamat\[Transpose],dthetamatt=dthetamat\[Transpose], ddthetamatt=ddthetamat\[Transpose],Ftipmatt=Ftipmat\[Transpose],taumat}, taumat=thetamatt; Do[ taumat[[;;,i]]=InverseDynamics[{thetamatt[[;;,i]]}\[Transpose], {dthetamatt[[;;,i]]}\[Transpose], {ddthetamatt[[;;,i]]}\[Transpose],g, Ftipmatt[[;;,i]],Mlist,Glist,Slist], {i,Dimensions[thetamatt][[2]]} ]; taumat=taumat\[Transpose]; Return[taumat] ] ForwardDynamicsTrajectory[thetalist_,dthetalist_,taumat_,g_,Ftipmat_,Mlist_,Glist_,Slist_,dt_,intRes_]:=Module[ {taumatt=taumat\[Transpose],Ftipmatt=Ftipmat\[Transpose],thetamat, dthetamat,ddthetanext,thetanext=thetalist,dthetanext=dthetalist}, thetamat=taumatt; dthetamat=taumatt; thetamat[[;;,1]]=thetanext; dthetamat[[;;,1]]=dthetanext; Do[ Do[ ddthetanext=ForwardDynamics[thetanext,dthetanext,taumatt[[;;,i]], g,Ftipmatt[[;;,i]],Mlist,Glist, Slist]; {thetanext,dthetanext}=EulerStep[thetanext,dthetanext, ddthetanext,dt/intRes], intRes ]; thetamat[[;;,i+1]]=thetanext; dthetamat[[;;,i+1]]=dthetanext, {i,Dimensions[taumatt][[2]]-1} ]; thetamat=thetamat\[Transpose]; dthetamat=dthetamat\[Transpose]; Return[{thetamat,dthetamat}] ] (* ::Subsection::Closed:: *) (*CHAPTER 9: TRAJECTORY GENERATION*) CubicTimeScaling[Tf_,t_]:=3*(t/Tf)^2-2*(t/Tf)^3 QuinticTimeScaling[Tf_,t_]:=10*(t/Tf)^3-15*(t/Tf)^4+6*(t/Tf)^5 JointTrajectory[thetastart_,thetaend_,Tf_,Num_,method_]:=Module[ {Numb=Round[Num],timegap,traj,s}, timegap=Tf/(Numb-1); traj= ConstantArray[0,{Length[thetastart],Numb}]; Do[ If[method == 3,s=CubicTimeScaling[Tf,(i-1)*timegap], s=QuinticTimeScaling[Tf,(i-1)*timegap]]; traj[[;;,i]]=Flatten[thetastart +s*(thetaend-thetastart)], {i,Numb} ]; traj=traj\[Transpose]; Return[traj] ] ScrewTrajectory[Xstart_,Xend_,Tf_,N_,method_]:=Module[ {timegap=Tf/(N-1),traj=ConstantArray[Null,N],s}, Do[ If[method==3,s=CubicTimeScaling[Tf,(i-1)*timegap], s=QuinticTimeScaling[Tf,(i-1)*timegap]]; traj[[i]]=Xstart.MatrixExp6[MatrixLog6[TransInv[Xstart].Xend]*s], {i,N} ]; Return[traj] ] CartesianTrajectory[Xstart_,Xend_,Tf_,N_,method_]:=Module[ {timegap=Tf/(N-1),traj=ConstantArray[Null,N],s,Rstart,pstart,Rend,pend}, {Rstart,pstart}=TransToRp[Xstart];{Rend,pend}=TransToRp[Xend]; Do[ If[ method == 3, s=CubicTimeScaling[Tf,(i-1)*timegap], s=QuinticTimeScaling[Tf,(i-1)*timegap] ]; traj[[i]]=ArrayFlatten[ {{Rstart.MatrixExp3[MatrixLog3[Rstart\[Transpose].Rend]*s], pstart+s*(pend-pstart)}, {0,1}} ], {i,N} ]; Return[traj] ] (* ::Subsection::Closed:: *) (*CHAPTER 11: ROBOT CONTROL*) ComputedTorque[thetalist_,dthetalist_,eint_,g_,Mlist_,Glist_,Slist_,thetalistd_,dthetalistd_,ddthetalistd_,Kp_,Ki_,Kd_]:=Module[ {e=thetalistd-thetalist}, Return[ MassMatrix[thetalist,Mlist,Glist,Slist]. (Kp*e+Ki*(eint+e)+Kd*(dthetalistd-dthetalist))+ InverseDynamics[thetalist,dthetalist,ddthetalistd,g, {{0,0,0,0,0,0}}\[Transpose],Mlist,Glist,Slist] ] ] SimulateControl[thetalist_,dthetalist_,g_,Ftipmat_,Mlist_,Glist_,Slist_,thetamatd_,dthetamatd_,ddthetamatd_,gtilde_,Mtildelist_,Gtildelist_,Kp_,Ki_,Kd_,dt_,intRes_]:=Module[ {Ftipmatt=Ftipmat\[Transpose],thetamatdt=thetamatd\[Transpose], dthetamatdt=dthetamatd\[Transpose],ddthetamatdt=ddthetamatd\[Transpose], taumat,thetamat,n,thetacurrent,dthetacurrent,eint,taulist,ddthetalist, links, plotAngles, labels,style,col}, n=Dimensions[thetamatdt][[2]]; thetamat=thetamatdt; taumat=thetamatdt; thetacurrent=thetalist; dthetacurrent=dthetalist; eint=ConstantArray[0,{Dimensions[thetamatdt][[1]],1}]; Do[ taulist= ComputedTorque[thetacurrent,dthetacurrent,eint,gtilde,Mtildelist, Gtildelist,Slist,{thetamatdt[[;;,i]]}\[Transpose], {dthetamatdt[[;;,i]]}\[Transpose], {ddthetamatdt[[;;,i]]}\[Transpose],Kp,Ki,Kd]; Do[ ddthetalist=ForwardDynamics[thetacurrent,dthetacurrent,taulist,g, {Ftipmatt[[;;,i]]}\[Transpose],Mlist, Glist,Slist]; {thetacurrent,dthetacurrent}= EulerStep[thetacurrent,dthetacurrent,ddthetalist, (dt/intRes)], {j,intRes} ]; taumat[[;;,i]]=taulist; thetamat[[;;,i]]=thetacurrent; eint=eint+(dt*(thetamatdt[[;;,i]]-thetacurrent)), {i,n} ]; (*Output plotting*) links=Dimensions[thetamat][[1]]; plotAngles ={}; labels = {}; style={}; Do[AppendTo[plotAngles, Flatten[thetamat[[i,;;]]]]; AppendTo[plotAngles, Flatten[thetamatdt[[i,;;]]]]; AppendTo[labels, StringJoin["ActualTheta",ToString[i]]]; AppendTo[labels, StringJoin["DesiredTheta",ToString[i]]]; col = RandomColor[]; AppendTo[style, col]; AppendTo[style, {col,Dashed}],{i,1,links}]; Print[ListLinePlot[plotAngles,PlotLegends->labels,PlotStyle->style, AxesLabel->{"Time","Joint Angles"}, PlotLabel->"Plot of Actual and Desired Joint Angles", PlotRange->Full]]; taumat=taumat\[Transpose]; thetamat=thetamat\[Transpose]; Return[{taumat,thetamat}] ] (* ::Section::Closed:: *) (*END*) End[]; Protect["NearZero"]; Protect["RotInv","VecToso3","so3ToVec","AxisAng3","MatrixExp3","MatrixLog3", "DistanceToSO3","TestIfSO3","ProjectToSO3","RpToTrans","TransToRp", "TransInv","VecTose3","se3ToVec","Adjoint","ScrewToAxis","AxisAng6", "MatrixExp6","MatrixLog6","DistanceToSE3","TestIfSE3", "ProjectToSE3"]; Protect["FKinBody","FKinSpace"]; Protect["JacobianBody","JacobianSpace"]; Protect["IKinBody","IKinSpace"]; Protect["ad","InverseDynamics","MassMatrix","VelQuadraticForces", "GravityForces","EndEffectorForces","ForwardDynamics","EulerStep", "InverseDynamicsTrajectory","ForwardDynamicsTrajectory"]; Protect["CubicTimeScaling","QuinticTimeScaling","JointTrajectory", "ScrewTrajectory","CartesianTrajectory"]; Protect["ComputedTorque","SimulateControl"]; EndPackage[];